Selaa lähdekoodia

重构normal.rs部分代码 (#17)

* 优化跳转逻辑

* 增加'O', 'a', 'A'等命令

* 增加'H', 'M'命令
Z YS 6 kuukautta sitten
vanhempi
commit
c2100e27aa
4 muutettua tiedostoa jossa 305 lisäystä ja 203 poistoa
  1. 44 31
      src/utils/ui/mode/common.rs
  2. 1 0
      src/utils/ui/mode/mod.rs
  3. 243 172
      src/utils/ui/mode/normal.rs
  4. 17 0
      src/utils/ui/mode/state.rs

+ 44 - 31
src/utils/ui/mode/common.rs

@@ -3,12 +3,13 @@ use std::{io, sync::MutexGuard};
 use crate::utils::{
     terminal::TermManager,
     ui::{
-        event::{KeyEventCallback, WarpUiCallBackType},
+        event::KeyEventCallback,
         uicore::{UiCore, CONTENT_WINSIZE},
     },
 };
 
 pub trait CommonOp: KeyEventCallback {
+    /// 删除一行
     fn remove_line(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<()> {
         TermManager::clear_current_line()?;
         TermManager::clear_under_cursor()?;
@@ -33,6 +34,7 @@ pub trait CommonOp: KeyEventCallback {
         Ok(())
     }
 
+    /// 删除数行
     fn remove_n_line(&self, ui: &mut MutexGuard<UiCore>, n: u16) -> io::Result<()> {
         let linecount = ui.buffer.line_count() as u16;
         let y = ui.cursor.y();
@@ -44,6 +46,7 @@ pub trait CommonOp: KeyEventCallback {
         }
         Ok(())
     }
+    /// 删除一个单词
     fn remove_word(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<()> {
         let x = ui.cursor.x();
         let y = ui.cursor.y();
@@ -62,30 +65,7 @@ pub trait CommonOp: KeyEventCallback {
         ui.render_content(y, 1)?;
         return Ok(());
     }
-    fn jump_to_next_word(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
-        let x = ui.cursor.x();
-        let y = ui.cursor.y();
-        let pos = ui.buffer.search_nextw_begin(x, y);
-        let linesize = ui.buffer.get_linesize(y);
-        let abs_y = y + ui.buffer.offset() as u16;
-
-        if pos < linesize as usize {
-            // 如果下一个单词在当前行,则移动光标到该单词的起始位置
-            ui.cursor.move_to_columu(pos as u16)?;
-        } else if y as usize + ui.buffer.offset() < ui.buffer.line_count() - 1 {
-            // 如果当前行不是最后一行,则移动到下一行的单词起始位置
-            let next_word_pos = ui.buffer.search_nextw_begin(0, y + 1) as u16;
-            let next_linesize = ui.buffer.get_linesize_abs(abs_y + 1);
-            self.down(ui)?;
-            ui.cursor
-                .move_to_columu(next_word_pos.min(next_linesize - 1))?;
-            ui.cursor.highlight(Some(y))?;
-        } else {
-            // 如果当前行是最后一行,则移动到当前行的末尾
-            ui.cursor.move_to_columu(linesize as u16 - 1)?;
-        }
-        return Ok(WarpUiCallBackType::None);
-    }
+    /// 移动到指定行
     fn move_to_line(&self, ui: &mut MutexGuard<UiCore>, line: u16) -> io::Result<()> {
         let x = ui.cursor.x();
         let y = ui.cursor.y();
@@ -97,6 +77,7 @@ pub trait CommonOp: KeyEventCallback {
         return Ok(());
     }
 
+    /// 定位到上一个单词的首字母,返回绝对坐标
     fn locate_prevw_begin(&self, ui: &mut MutexGuard<UiCore>, x: u16, abs_y: u16) -> (u16, u16) {
         // 如果光标已在行首,则尝试移动到上一行的单词首字母
         if x == 0 {
@@ -113,15 +94,16 @@ pub trait CommonOp: KeyEventCallback {
 
         return (prev_word_pos as u16, abs_y);
     }
-    fn locate_nextw_ending(&self, ui: &mut MutexGuard<UiCore>, x: u16, y: u16) -> (u16, u16) {
-        let linesize = ui.buffer.get_linesize(y) as usize;
 
-        // y的绝对位置
-        let abs_y = ui.buffer.offset() as u16 + y;
+    /// 定位到下一个单词的末尾,返回绝对坐标
+    fn locate_nextw_ending(&self, ui: &mut MutexGuard<UiCore>, x: u16, abs_y: u16) -> (u16, u16) {
+        let linesize = ui.buffer.get_linesize_abs(abs_y) as usize;
+
         // 如果光标已经在当前行的末尾或最后一个字符(x + 2),则尝试移动到下一行的末尾或单词末尾
         if x as usize + 2 >= linesize {
             if abs_y < ui.buffer.line_count() as u16 - 1 {
-                let next_end_pos = ui.buffer.search_nextw_end(0, y + 1) as u16;
+                let offset = ui.buffer.offset() as u16;
+                let next_end_pos = ui.buffer.search_nextw_end(0, abs_y + 1 - offset) as u16;
                 return (next_end_pos, abs_y + 1);
             } else {
                 // 如果已经是最后一行,则保持光标在当前行的末尾
@@ -130,8 +112,39 @@ pub trait CommonOp: KeyEventCallback {
             }
         }
 
-        let next_end_pos = ui.buffer.search_nextw_end(x, y) as u16;
+        let offset = ui.buffer.offset() as u16;
+        let next_end_pos = ui.buffer.search_nextw_end(x, abs_y - offset) as u16;
         // 如果下一个单词的末尾在当前行,则移动光标到该单词的末尾
         return (next_end_pos.min(linesize as u16 - 1), abs_y);
     }
+
+    /// 定位到下一个单词的首字母,返回绝对坐标
+    fn locate_next_word(&self, ui: &mut MutexGuard<UiCore>, abs_pos: (u16, u16)) -> (u16, u16) {
+        let linesize = ui.buffer.get_linesize_abs(abs_pos.1) as usize;
+        if abs_pos.0 as usize + 2 >= linesize {
+            if abs_pos.1 < ui.buffer.line_count() as u16 - 1 {
+                let offset = ui.buffer.offset() as u16;
+                let next_end_pos = ui.buffer.search_nextw_begin(0, abs_pos.1 + 1 - offset) as u16;
+                return (next_end_pos, abs_pos.1 + 1);
+            } else {
+                let x = if linesize > 0 { linesize - 1 } else { 0 };
+                return (x as u16, abs_pos.1);
+            }
+        }
+        let offset = ui.buffer.offset() as u16;
+        let next_end_pos = ui.buffer.search_nextw_begin(abs_pos.0, abs_pos.1 - offset) as u16;
+        return (next_end_pos.min(linesize as u16 - 1), abs_pos.1);
+    }
+    fn move_to_nlines_of_screen(&self, ui: &mut MutexGuard<UiCore>, n: usize) -> io::Result<()> {
+        let y = ui.cursor.y() as usize;
+
+        let offset = ui.buffer.offset();
+
+        let new_y = ui.buffer.goto_line(offset + n);
+        ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
+        ui.cursor.move_to_row(new_y)?;
+        ui.cursor.highlight(Some(y as u16))?;
+
+        Ok(())
+    }
 }

+ 1 - 0
src/utils/ui/mode/mod.rs

@@ -1,3 +1,4 @@
 pub mod common;
 pub mod mode;
 pub mod normal;
+pub mod state;

+ 243 - 172
src/utils/ui/mode/normal.rs

@@ -1,25 +1,3 @@
-// 为了不影响 Command 模式的功能,参考Vim源码单独实现临时的 Normal 模式用于演示
-
-// Normal模式下的状态机
-
-// 在 input_data() 中处理输入的数据,根据输入的数据进行状态转移,
-// 具体而言,根据输入数据及当前状态来更新状态机的参数,如命令字符,重复次数等
-
-// 在 handle() 中处理状态的转移,根据状态的变化执行相应的操作
-// handle() 是真正作用于 ui 和 buffer 的地方,可以在这里调用 buffer 的方法,更新 ui 的显示
-// 因此,NormalState 提供给 handle() 的参数应该具有足够的一般性,以适应不同的需求
-
-// 在 exit() 中处理状态的退出,清空状态
-
-// 由此为 ndw,ndd 等命令的实现提供了多重字符匹配之外的方案:
-// 状态机的下一个状态仅由输入 + 当前状态决定,避免了对输入的全局匹配
-
-// 另:静态 HashMap 比起 match 性能更好是真的吗?后续是否考虑更换为 HashMap?
-// 另:在现有框架下,增加一个新功能,需要在 input_data() 中增加一个分支,handle() 中增加一个分支
-// 是否有更简便的代码结构?
-
-// 参考:https://github.com/neovim/neovim/blob/master/src/nvim/normal.c#L89
-
 use lazy_static::lazy_static;
 
 use crate::utils::ui::event::KeyEventCallback;
@@ -31,6 +9,8 @@ use std::sync::{Mutex, MutexGuard};
 
 use super::common::CommonOp;
 use super::mode::ModeType;
+use super::state::StateMachine;
+use crate::utils::ui::mode::state::StateCallback;
 
 #[derive(Debug)]
 #[allow(dead_code)]
@@ -99,63 +79,61 @@ impl KeyEventCallback for Normal {
         let mut normal_state = NORMALSTATE.lock().unwrap();
         normal_state.cmdbuf.extend_from_slice(data);
         match data {
-            b"h" => {
-                normal_state.on_h_clicked();
-            }
-            b"j" => {
-                normal_state.on_j_clicked();
-            }
-            b"k" => {
-                normal_state.on_k_clicked();
-            }
-            b"l" => {
-                normal_state.on_l_clicked();
-            }
-            b"i" => {
-                normal_state.on_i_clicked();
-            }
-            b"d" => {
-                normal_state.on_d_clicked();
-            }
-            [b'1'..=b'9'] => {
-                normal_state.on_nonzero_clicked(data);
-            }
-            b"0" => {
-                normal_state.on_zero_clicked();
-            }
-            b"w" => {
-                normal_state.on_w_clicked();
-            }
-            b"g" => {
-                normal_state.on_g_clicked(ui);
-            }
-            b"G" => {
-                normal_state.on_G_clicked(ui);
-            }
-            b"b" => {
-                normal_state.on_b_clicked(ui);
-            }
+            b"h" => normal_state.on_h_clicked(),
+
+            b"j" => normal_state.on_j_clicked(),
+
+            b"k" => normal_state.on_k_clicked(),
+
+            b"l" => normal_state.on_l_clicked(),
+
+            b"i" => normal_state.on_i_clicked(),
+
+            b"d" => normal_state.on_d_clicked(),
+
+            [b'1'..=b'9'] => normal_state.on_nonzero_clicked(data),
+
+            b"0" => normal_state.on_zero_clicked(),
+
+            b"w" => normal_state.on_w_clicked(ui),
+
+            b"g" => normal_state.on_g_clicked(),
+
+            b"G" => normal_state.on_G_clicked(ui),
+
+            b"b" => normal_state.on_b_clicked(ui),
+
             b":" => {
                 if normal_state.cmdchar.is_none() {
                     ui.cursor.store_pos();
                     return Ok(WarpUiCallBackType::ChangMode(ModeType::LastLine));
                 }
             }
-            b"$" => {
-                normal_state.on_dollar_clicked();
-            }
-            b"e" => {
-                normal_state.on_e_clicked(ui);
-            }
-            b"f" => {
-                normal_state.on_f_clicked();
-            }
-            b"F" => {
-                normal_state.on_F_clicked();
-            }
-            b"x" => {
-                normal_state.on_x_clicked();
-            }
+
+            b"$" => normal_state.on_dollar_clicked(),
+
+            b"e" => normal_state.on_e_clicked(ui),
+
+            b"f" => normal_state.on_f_clicked(),
+
+            b"F" => normal_state.on_F_clicked(),
+
+            b"x" => normal_state.on_x_clicked(),
+
+            b"o" => normal_state.on_o_clicked(),
+
+            b"O" => normal_state.on_O_clicked(),
+
+            b"a" => normal_state.on_a_clicked(),
+
+            b"A" => normal_state.on_A_clicked(),
+
+            b"I" => normal_state.on_I_clicked(),
+
+            b"H" => normal_state.on_H_clicked(),
+
+            b"M" => normal_state.on_M_clicked(),
+
             _ => {}
         }
         return normal_state.handle(ui);
@@ -189,20 +167,9 @@ impl KeyEventCallback for NormalState {
     }
 }
 impl NormalState {
-    pub fn reset(&mut self) {
-        self.cmdchar = None;
-        self.count = None;
-        self.count0 = false;
-        self.start_pos = None;
-        self.end_pos = None;
-        self.cmdbuf.clear();
-        self.buf_op_arg = None;
-    }
-
-    pub fn exec_0_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    pub fn exec_0_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         ui.cursor.move_to_columu(0)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     pub fn on_h_clicked(&mut self) {
@@ -211,7 +178,7 @@ impl NormalState {
         }
     }
     /// 向左移动数列
-    pub fn exec_h_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    pub fn exec_h_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let old_x = ui.cursor.x();
         let exec_count = match self.count {
             Some(count) => count.min(old_x as usize),
@@ -225,15 +192,16 @@ impl NormalState {
         };
         let new_x = old_x - exec_count as u16;
         ui.cursor.move_to_columu(new_x)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     pub fn on_j_clicked(&mut self) {
-        self.cmdchar = Some('j');
+        if self.cmdchar.is_none() {
+            self.cmdchar = Some('j');
+        }
     }
     /// 向下移动数行
-    pub fn exec_j_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    pub fn exec_j_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let old_y = ui.cursor.y();
         let old_abs_y = old_y + ui.buffer.offset() as u16;
         // 限制最大移动行数
@@ -256,8 +224,7 @@ impl NormalState {
         if ui.buffer.offset() != old_offset {
             ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
         }
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
     pub fn on_k_clicked(&mut self) {
         if self.cmdchar.is_none() {
@@ -266,7 +233,7 @@ impl NormalState {
     }
 
     /// 向上移动数行
-    pub fn exec_k_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    pub fn exec_k_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let old_y = ui.cursor.y();
         let old_abs_y = old_y + ui.buffer.offset() as u16;
         // 限制最大移动行数
@@ -296,8 +263,7 @@ impl NormalState {
         if old_offset != ui.buffer.offset() {
             ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
         }
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     pub fn on_l_clicked(&mut self) {
@@ -307,7 +273,7 @@ impl NormalState {
     }
 
     /// 向右移动数列
-    pub fn exec_l_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    pub fn exec_l_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let old_x = ui.cursor.x();
         let linesize = ui.buffer.get_linesize(ui.cursor.y()) as usize;
         let max_count = linesize - old_x as usize - 1;
@@ -323,8 +289,7 @@ impl NormalState {
         };
         let new_x = old_x + exec_count as u16;
         ui.cursor.move_to_columu(new_x)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     pub fn on_i_clicked(&mut self) {
@@ -332,9 +297,79 @@ impl NormalState {
             self.cmdchar = Some('i');
         }
     }
-    pub fn exec_i_cmd(&mut self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
-        self.exit()?;
-        return Ok(WarpUiCallBackType::ChangMode(ModeType::Insert));
+    pub fn exec_i_cmd(&mut self, _ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        return Ok(StateCallback::Exit(ModeType::Insert));
+    }
+
+    #[allow(non_snake_case)]
+    pub fn on_I_clicked(&mut self) {
+        if self.cmdchar.is_none() {
+            self.cmdchar = Some('I');
+        }
+    }
+
+    // 切换Insert模式,从行首开始插入字符
+    #[allow(non_snake_case)]
+    pub fn exec_I_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        ui.cursor.move_to_columu(0)?;
+        return Ok(StateCallback::Exit(ModeType::Insert));
+    }
+
+    pub fn on_a_clicked(&mut self) {
+        if self.cmdchar.is_none() {
+            self.cmdchar = Some('a');
+        }
+    }
+
+    // 切换Insert模式,从当前位置的下一个字符开始插入
+    pub fn exec_a_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        self.right(ui)?;
+        return Ok(StateCallback::Exit(ModeType::Insert));
+    }
+
+    #[allow(non_snake_case)]
+    pub fn on_A_clicked(&mut self) {
+        if self.cmdchar.is_none() {
+            self.cmdchar = Some('A');
+        }
+    }
+
+    // 切换Insert模式,从行尾开始插入字符
+    #[allow(non_snake_case)]
+    pub fn exec_A_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        let line_end = ui.buffer.get_linesize(ui.cursor.y()) - 1;
+        ui.cursor.move_to_columu(line_end)?;
+        return Ok(StateCallback::Exit(ModeType::Insert));
+    }
+
+    pub fn on_o_clicked(&mut self) {
+        if self.cmdchar.is_none() {
+            self.cmdchar = Some('o');
+        }
+    }
+
+    // 切换Insert模式,在当前行的下方插入一个新行开始输入文本
+    pub fn exec_o_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        let linesize = ui.buffer.get_linesize(ui.cursor.y());
+        ui.cursor.move_to_columu(linesize - 1)?;
+        ui.buffer.input_enter(ui.cursor.x(), ui.cursor.y());
+        ui.cursor.move_to_nextline(1)?;
+        return Ok(StateCallback::Exit(ModeType::Insert));
+    }
+
+    #[allow(non_snake_case)]
+    pub fn on_O_clicked(&mut self) {
+        if self.cmdchar.is_none() {
+            self.cmdchar = Some('O');
+        }
+    }
+
+    // 切换Insert模式,在当前行的上方插入一个新行开始输入文本
+    #[allow(non_snake_case)]
+    pub fn exec_O_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        ui.cursor.move_to_columu(0)?;
+        ui.buffer.input_enter(ui.cursor.x(), ui.cursor.y());
+        return Ok(StateCallback::Exit(ModeType::Insert));
     }
 
     /// 处理输入的非零数字
@@ -386,7 +421,7 @@ impl NormalState {
         }
     }
 
-    pub fn exec_d_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    pub fn exec_d_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let count = match self.count {
             Some(count) => count as u16,
             None => 1,
@@ -394,70 +429,65 @@ impl NormalState {
         match self.buf_op_arg {
             Some(BufOpArg::Line) => {
                 // 删除行
-                let result = self
-                    .remove_n_line(ui, count)
-                    .map(|_| WarpUiCallBackType::None);
-                self.reset();
-                return result;
+                self.remove_n_line(ui, count)?;
+                return Ok(StateCallback::Reset);
             }
             Some(BufOpArg::Word) => {
                 // 删除单词
                 for _ in 0..count {
                     self.remove_word(ui)?;
                 }
-                self.reset();
-                return Ok(WarpUiCallBackType::None);
+                return Ok(StateCallback::Reset);
             }
             _ => {
-                return Ok(WarpUiCallBackType::None);
+                return Ok(StateCallback::None);
             }
         }
     }
 
-    pub fn on_w_clicked(&mut self) {
+    pub fn on_w_clicked(&mut self, ui: &mut MutexGuard<UiCore>) {
         if self.cmdchar.is_none() {
             // 按单词移动
             self.cmdchar = Some('w');
+            let count = match self.count {
+                Some(count) => count,
+                None => 1,
+            };
+            let mut pos = (ui.cursor.x(), ui.cursor.y() + ui.buffer.offset() as u16);
+            for _ in 0..count {
+                pos = self.locate_next_word(ui, pos);
+            }
+            self.end_pos = Some(pos);
         } else {
             // 按单词操作,具体由self.cmdchar决定
             self.buf_op_arg = Some(BufOpArg::Word);
         }
     }
 
-    pub fn exec_w_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
-        let count = match self.count {
-            Some(count) => count,
-            None => 1,
-        };
-        for _ in 0..count {
-            self.jump_to_next_word(ui)?;
-        }
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+    pub fn exec_w_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        self.end_pos.map(|pos| {
+            self.move_to_line(ui, pos.1).unwrap();
+            ui.cursor.move_to_columu(pos.0).unwrap();
+        });
+        return Ok(StateCallback::Reset);
     }
 
-    fn on_g_clicked(&mut self, ui: &mut MutexGuard<UiCore>) {
+    fn on_g_clicked(&mut self) {
         if self.cmdchar.is_none() {
             self.cmdchar = Some('g');
         } else {
-            let first_line_size = ui.buffer.get_linesize(0);
-            self.end_pos = Some((ui.cursor.x().min(first_line_size - 1), 0));
+            self.end_pos = Some((0, 0));
         }
     }
 
-    fn exec_g_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
-        let end_pos = self.end_pos;
-        if end_pos.is_none() {
-            return Ok(WarpUiCallBackType::None);
+    fn exec_g_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        let rs = self
+            .end_pos
+            .map(|pos| self.move_to_line(ui, pos.1).unwrap());
+        if let None = rs {
+            return Ok(StateCallback::None);
         }
-        let old_y = ui.cursor.y();
-        let (x, y) = end_pos.unwrap();
-        let y = ui.buffer.goto_line(y.into());
-        ui.cursor.move_to(x as u16, y as u16)?;
-        ui.render_content(y, CONTENT_WINSIZE.read().unwrap().rows as usize)?;
-        ui.cursor.highlight(Some(old_y))?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     #[allow(non_snake_case)]
@@ -468,14 +498,13 @@ impl NormalState {
     }
 
     #[allow(non_snake_case)]
-    fn exec_G_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    fn exec_G_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let lineidx = match self.count {
             Some(count) => count - 1,
             None => ui.buffer.line_count() - 1,
         };
         self.move_to_line(ui, lineidx as u16)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     fn on_b_clicked(&mut self, ui: &mut MutexGuard<UiCore>) {
@@ -489,18 +518,18 @@ impl NormalState {
             None => 1,
         };
         let mut pos = (ui.cursor.x(), ui.cursor.y() + ui.buffer.offset() as u16);
+        self.start_pos = Some(pos);
         for _ in 0..count {
             pos = self.locate_prevw_begin(ui, pos.0, pos.1);
         }
         self.end_pos = Some(pos);
     }
 
-    fn exec_b_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    fn exec_b_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let end_pos = self.end_pos.unwrap();
         self.move_to_line(ui, end_pos.1)?;
         ui.cursor.move_to_columu(end_pos.0)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     fn on_dollar_clicked(&mut self) {
@@ -509,11 +538,10 @@ impl NormalState {
         }
     }
 
-    fn exec_dollar_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    fn exec_dollar_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let line_end = ui.buffer.get_linesize(ui.cursor.y()) as u16 - 1;
         ui.cursor.move_to_columu(line_end)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     fn on_e_clicked(&mut self, ui: &mut MutexGuard<UiCore>) {
@@ -526,23 +554,22 @@ impl NormalState {
             Some(count) => count,
             None => 1,
         };
-        let mut pos = (ui.cursor.x(), ui.cursor.y());
+        let mut pos = (ui.cursor.x(), ui.cursor.y() + ui.buffer.offset() as u16);
         for _ in 0..count {
             pos = self.locate_nextw_ending(ui, pos.0, pos.1);
         }
         self.end_pos = Some(pos);
     }
 
-    fn exec_e_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    fn exec_e_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let end_pos = self.end_pos;
         if end_pos.is_none() {
-            return Ok(WarpUiCallBackType::None);
+            return Ok(StateCallback::None);
         }
         let end_pos = end_pos.unwrap();
         self.move_to_line(ui, end_pos.1)?;
         ui.cursor.move_to_columu(end_pos.0)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     fn on_f_clicked(&mut self) {
@@ -551,9 +578,9 @@ impl NormalState {
         }
     }
 
-    fn exec_f_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    fn exec_f_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         if self.cmdbuf.len() < 2 {
-            return Ok(WarpUiCallBackType::None);
+            return Ok(StateCallback::None);
         }
         let to_find = self.cmdbuf.last().unwrap().clone() as char;
         let old_x = ui.cursor.x();
@@ -562,12 +589,11 @@ impl NormalState {
             String::from_utf8_lossy(&ui.buffer.get_line(old_y)[old_x as usize..]).to_string();
         let pos = line.find(to_find);
         if pos.is_none() {
-            return Ok(WarpUiCallBackType::None);
+            return Ok(StateCallback::None);
         }
         ui.cursor
             .move_to_columu((old_x + pos.unwrap() as u16) as u16)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     #[allow(non_snake_case)]
@@ -578,9 +604,9 @@ impl NormalState {
     }
 
     #[allow(non_snake_case)]
-    fn exec_F_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    fn exec_F_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         if self.cmdbuf.len() < 2 {
-            return Ok(WarpUiCallBackType::None);
+            return Ok(StateCallback::None);
         }
         let to_find = self.cmdbuf.last().unwrap().clone() as char;
         let old_x = ui.cursor.x();
@@ -589,11 +615,10 @@ impl NormalState {
             String::from_utf8_lossy(&ui.buffer.get_line(old_y)[..old_x as usize]).to_string();
         let pos = line.rfind(to_find);
         if pos.is_none() {
-            return Ok(WarpUiCallBackType::None);
+            return Ok(StateCallback::None);
         }
         ui.cursor.move_to_columu(pos.unwrap() as u16)?;
-        self.reset();
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
 
     fn on_x_clicked(&mut self) {
@@ -602,28 +627,58 @@ impl NormalState {
         }
     }
 
-    fn exec_x_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
+    fn exec_x_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
         let y = ui.cursor.y();
         let x = ui.cursor.x();
         if x < ui.buffer.get_linesize(y) - 1 {
             ui.buffer.remove_char(x, y);
             ui.render_content(y, 1)?;
         }
-        return Ok(WarpUiCallBackType::None);
+        return Ok(StateCallback::Reset);
     }
-}
 
-pub trait StateMachine {
-    fn handle(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType>;
-    fn exit(&mut self) -> io::Result<()>;
+    #[allow(non_snake_case)]
+    fn on_H_clicked(&mut self) {
+        if self.cmdchar.is_none() {
+            self.cmdchar = Some('H');
+        }
+    }
+
+    #[allow(non_snake_case)]
+    fn exec_H_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        self.move_to_nlines_of_screen(ui, 0)?;
+        return Ok(StateCallback::Reset);
+    }
+    #[allow(non_snake_case)]
+    fn on_M_clicked(&mut self) {
+        if self.cmdchar.is_none() {
+            self.cmdchar = Some('M');
+        }
+    }
+
+    #[allow(non_snake_case)]
+    fn exec_M_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<StateCallback> {
+        let win_size = CONTENT_WINSIZE.read().unwrap().rows as usize;
+        self.move_to_nlines_of_screen(ui, win_size / 2)?;
+        return Ok(StateCallback::Reset);
+    }
 }
 
 impl StateMachine for NormalState {
+    fn reset(&mut self) {
+        self.cmdchar = None;
+        self.count = None;
+        self.count0 = false;
+        self.start_pos = None;
+        self.end_pos = None;
+        self.cmdbuf.clear();
+        self.buf_op_arg = None;
+    }
     fn handle(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> {
         if self.cmdchar.is_none() {
             return Ok(WarpUiCallBackType::None);
         }
-        match self.cmdchar.unwrap() {
+        let state_callback = match self.cmdchar.unwrap() {
             'h' => self.exec_h_cmd(ui),
             'j' => self.exec_j_cmd(ui),
             'k' => self.exec_k_cmd(ui),
@@ -640,12 +695,28 @@ impl StateMachine for NormalState {
             'f' => self.exec_f_cmd(ui),
             'F' => self.exec_F_cmd(ui),
             'x' => self.exec_x_cmd(ui),
-            _ => return Ok(WarpUiCallBackType::None),
-        }
+            'o' => self.exec_o_cmd(ui),
+            'O' => self.exec_O_cmd(ui),
+            'a' => self.exec_a_cmd(ui),
+            'A' => self.exec_A_cmd(ui),
+            'I' => self.exec_I_cmd(ui),
+            'H' => self.exec_H_cmd(ui),
+            'M' => self.exec_M_cmd(ui),
+            _ => Ok(StateCallback::None),
+        };
+        return match state_callback {
+            Ok(StateCallback::None) => Ok(WarpUiCallBackType::None),
+            Ok(StateCallback::Reset) => {
+                self.reset();
+                Ok(WarpUiCallBackType::None)
+            }
+            Ok(StateCallback::Exit(mode)) => self.exit(WarpUiCallBackType::ChangMode(mode)),
+            Err(e) => Err(e),
+        };
     }
 
-    fn exit(&mut self) -> io::Result<()> {
+    fn exit(&mut self, callback: WarpUiCallBackType) -> io::Result<WarpUiCallBackType> {
         self.reset();
-        Ok(())
+        Ok(callback)
     }
 }

+ 17 - 0
src/utils/ui/mode/state.rs

@@ -0,0 +1,17 @@
+use std::{io, sync::MutexGuard};
+
+use crate::utils::ui::{event::WarpUiCallBackType, uicore::UiCore};
+
+use super::mode::ModeType;
+
+pub enum StateCallback {
+    None,
+    Reset,
+    Exit(ModeType),
+}
+
+pub trait StateMachine {
+    fn handle(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType>;
+    fn exit(&mut self, callback: WarpUiCallBackType) -> io::Result<WarpUiCallBackType>;
+    fn reset(&mut self);
+}