1 // 为了不影响 Command 模式的功能,参考Vim源码单独实现临时的 Normal 模式用于演示 2 3 // Normal模式下的状态机 4 5 // 在 input_data() 中处理输入的数据,根据输入的数据进行状态转移, 6 // 具体而言,根据输入数据及当前状态来更新状态机的参数,如命令字符,重复次数等 7 8 // 在 handle() 中处理状态的转移,根据状态的变化执行相应的操作 9 // handle() 是真正作用于 ui 和 buffer 的地方,可以在这里调用 buffer 的方法,更新 ui 的显示 10 // 因此,NormalState 提供给 handle() 的参数应该具有足够的一般性,以适应不同的需求 11 12 // 在 exit() 中处理状态的退出,清空状态 13 14 // 由此为 ndw,ndd 等命令的实现提供了多重字符匹配之外的方案: 15 // 状态机的下一个状态仅由输入 + 当前状态决定,避免了对输入的全局匹配 16 17 // 另:静态 HashMap 比起 match 性能更好是真的吗?后续是否考虑更换为 HashMap? 18 // 另:在现有框架下,增加一个新功能,需要在 input_data() 中增加一个分支,handle() 中增加一个分支 19 // 是否有更简便的代码结构? 20 21 // 参考:https://github.com/neovim/neovim/blob/master/src/nvim/normal.c#L89 22 23 use lazy_static::lazy_static; 24 25 use crate::utils::ui::event::KeyEventCallback; 26 use crate::utils::ui::event::WarpUiCallBackType; 27 use crate::utils::ui::uicore::UiCore; 28 use crate::utils::ui::uicore::CONTENT_WINSIZE; 29 use std::io; 30 use std::sync::{Mutex, MutexGuard}; 31 32 use super::common::CommonOp; 33 use super::mode::ModeType; 34 35 #[derive(Debug)] 36 #[allow(dead_code)] 37 pub enum BufOpArg { 38 Around, // 操作引号内乃至引号的内容 39 Inside, // 操作引号内的内容 40 Line, // 操作整行 41 Word, // 操作单词 42 WordEnd, // 操作单词的末尾 43 WordBegin, // 操作单词的开头 44 Block, // 操作块 45 } 46 47 #[derive(Debug)] 48 pub struct NormalState { 49 pub cmdchar: Option<char>, 50 pub count: Option<usize>, 51 pub count0: bool, 52 pub start_pos: Option<(u16, u16)>, 53 pub end_pos: Option<(u16, u16)>, 54 pub cmdbuf: Vec<u8>, 55 pub buf_op_arg: Option<BufOpArg>, 56 } 57 58 impl CommonOp for NormalState {} 59 60 lazy_static! { 61 static ref NORMALSTATE: Mutex<NormalState> = Mutex::new(NormalState { 62 cmdchar: None, // 命令开头的字符,通常决定了一类功能,如dw,dd系列命令 63 count: None, // 命令的重复次数,如3j,4k 64 count0: false, // 是否将0作为命令的一部分,在normal模式下,0是一个独立的命令,也可能是一个数字的一部分 65 start_pos: None, // 作用区域的起始位置 66 end_pos: None, // 作用区域的结束位置 67 cmdbuf: Vec::new(), // 用于存储输入的命令,可以与状态的显示通用? 68 buf_op_arg: None // 用于指定操作的区域,如daw,diw 69 }); 70 } 71 72 #[derive(Debug)] 73 pub(crate) struct Normal; 74 impl Normal { 75 pub fn new() -> Self { 76 Self {} 77 } 78 } 79 80 impl KeyEventCallback for Normal { 81 fn backspace(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 82 return Ok(WarpUiCallBackType::None); 83 } 84 fn esc(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 85 return Ok(WarpUiCallBackType::ChangMode(ModeType::Command)); 86 } 87 88 fn enter(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 89 return Ok(WarpUiCallBackType::None); 90 } 91 fn tab(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 92 return Ok(WarpUiCallBackType::None); 93 } 94 fn input_data( 95 &self, 96 ui: &mut MutexGuard<UiCore>, 97 data: &[u8], 98 ) -> io::Result<WarpUiCallBackType> { 99 let mut normal_state = NORMALSTATE.lock().unwrap(); 100 normal_state.cmdbuf.extend_from_slice(data); 101 match data { 102 b"h" => { 103 normal_state.on_h_clicked(); 104 } 105 b"j" => { 106 normal_state.on_j_clicked(); 107 } 108 b"k" => { 109 normal_state.on_k_clicked(); 110 } 111 b"l" => { 112 normal_state.on_l_clicked(); 113 } 114 b"i" => { 115 normal_state.on_i_clicked(); 116 } 117 b"d" => { 118 normal_state.on_d_clicked(); 119 } 120 [b'1'..=b'9'] => { 121 normal_state.on_nonzero_clicked(data); 122 } 123 b"0" => { 124 normal_state.on_zero_clicked(); 125 } 126 b"w" => { 127 normal_state.on_w_clicked(); 128 } 129 b"g" => { 130 normal_state.on_g_clicked(ui); 131 } 132 b"G" => { 133 normal_state.on_G_clicked(ui); 134 } 135 b"b" => { 136 normal_state.on_b_clicked(ui); 137 } 138 b":" => { 139 if normal_state.cmdchar.is_none() { 140 ui.cursor.store_pos(); 141 return Ok(WarpUiCallBackType::ChangMode(ModeType::LastLine)); 142 } 143 } 144 b"$" => { 145 normal_state.on_dollar_clicked(); 146 } 147 b"e" => { 148 normal_state.on_e_clicked(ui); 149 } 150 b"f" => { 151 normal_state.on_f_clicked(); 152 } 153 b"F" => { 154 normal_state.on_F_clicked(); 155 } 156 b"x" => { 157 normal_state.on_x_clicked(); 158 } 159 _ => {} 160 } 161 return normal_state.handle(ui); 162 } 163 } 164 165 impl KeyEventCallback for NormalState { 166 fn backspace(&self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 167 ui.cursor.move_left(1)?; 168 return Ok(WarpUiCallBackType::None); 169 } 170 171 fn esc(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 172 return Ok(WarpUiCallBackType::ChangMode(ModeType::Command)); 173 } 174 175 fn enter(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 176 return Ok(WarpUiCallBackType::None); 177 } 178 179 fn tab(&self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 180 return Ok(WarpUiCallBackType::None); 181 } 182 183 fn input_data( 184 &self, 185 _ui: &mut MutexGuard<UiCore>, 186 _data: &[u8], 187 ) -> io::Result<WarpUiCallBackType> { 188 return Ok(WarpUiCallBackType::None); 189 } 190 } 191 impl NormalState { 192 pub fn reset(&mut self) { 193 self.cmdchar = None; 194 self.count = None; 195 self.count0 = false; 196 self.start_pos = None; 197 self.end_pos = None; 198 self.cmdbuf.clear(); 199 self.buf_op_arg = None; 200 } 201 202 pub fn exec_0_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 203 ui.cursor.move_to_columu(0)?; 204 self.reset(); 205 return Ok(WarpUiCallBackType::None); 206 } 207 208 pub fn on_h_clicked(&mut self) { 209 if self.cmdchar.is_none() { 210 self.cmdchar = Some('h'); 211 } 212 } 213 /// 向左移动数列 214 pub fn exec_h_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 215 let old_x = ui.cursor.x(); 216 let exec_count = match self.count { 217 Some(count) => count.min(old_x as usize), 218 None => { 219 if old_x == 0 { 220 0 221 } else { 222 1 223 } 224 } // 如果在第一列,不再向左移动,防止溢出 225 }; 226 let new_x = old_x - exec_count as u16; 227 ui.cursor.move_to_columu(new_x)?; 228 self.reset(); 229 return Ok(WarpUiCallBackType::None); 230 } 231 232 pub fn on_j_clicked(&mut self) { 233 self.cmdchar = Some('j'); 234 } 235 /// 向下移动数行 236 pub fn exec_j_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 237 let old_y = ui.cursor.y(); 238 let old_abs_y = old_y + ui.buffer.offset() as u16; 239 // 限制最大移动行数 240 let exec_count = match self.count { 241 Some(count) => count.min(ui.buffer.line_count() - old_abs_y as usize - 1), 242 None => 1, // goto_line 会自动处理最大值 243 }; 244 let old_offset = ui.buffer.offset(); 245 let new_y = ui.buffer.goto_line(old_abs_y as usize + exec_count); 246 let new_linesize = ui.buffer.get_linesize(new_y); 247 let new_x = if new_linesize < ui.cursor.x() { 248 // 如果新行的长度小于原来的x坐标,将光标移动到新行的最后一个字符 249 new_linesize - 1 250 } else { 251 ui.cursor.x() 252 }; 253 ui.cursor.move_to(new_x, new_y)?; 254 ui.cursor.highlight(Some(old_y))?; 255 // 如果移动后,buffer的offset发生了变化,需要重新渲染 256 if ui.buffer.offset() != old_offset { 257 ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?; 258 } 259 self.reset(); 260 return Ok(WarpUiCallBackType::None); 261 } 262 pub fn on_k_clicked(&mut self) { 263 if self.cmdchar.is_none() { 264 self.cmdchar = Some('k'); 265 } 266 } 267 268 /// 向上移动数行 269 pub fn exec_k_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 270 let old_y = ui.cursor.y(); 271 let old_abs_y = old_y + ui.buffer.offset() as u16; 272 // 限制最大移动行数 273 let exec_count = match self.count { 274 Some(count) => count.min(old_y as usize + ui.buffer.offset()), 275 None => { 276 if old_abs_y == 0 { 277 0 278 } else { 279 1 280 } 281 } // 如果在第一行,不再向上移动,防止溢出 282 }; 283 let to_line = old_abs_y as usize - exec_count; 284 let old_offset = ui.buffer.offset(); 285 let new_y = ui.buffer.goto_line(to_line); 286 let new_linesize = ui.buffer.get_linesize(new_y); 287 let new_x = if new_linesize < ui.cursor.x() { 288 // 如果新行的长度小于原来的x坐标,将光标移动到新行的最后一个字符 289 new_linesize - 1 290 } else { 291 ui.cursor.x() 292 }; 293 ui.cursor.move_to(new_x, new_y)?; 294 ui.cursor.highlight(Some(old_y))?; 295 // 如果移动后,buffer的offset发生了变化,需要重新渲染 296 if old_offset != ui.buffer.offset() { 297 ui.render_content(0, CONTENT_WINSIZE.read().unwrap().rows as usize)?; 298 } 299 self.reset(); 300 return Ok(WarpUiCallBackType::None); 301 } 302 303 pub fn on_l_clicked(&mut self) { 304 if self.cmdchar.is_none() { 305 self.cmdchar = Some('l'); 306 } 307 } 308 309 /// 向右移动数列 310 pub fn exec_l_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 311 let old_x = ui.cursor.x(); 312 let linesize = ui.buffer.get_linesize(ui.cursor.y()) as usize; 313 let max_count = linesize - old_x as usize - 1; 314 let exec_count = match self.count { 315 Some(count) => count.min(max_count), 316 None => { 317 if old_x == linesize as u16 - 1 { 318 0 319 } else { 320 1 321 } 322 } 323 }; 324 let new_x = old_x + exec_count as u16; 325 ui.cursor.move_to_columu(new_x)?; 326 self.reset(); 327 return Ok(WarpUiCallBackType::None); 328 } 329 330 pub fn on_i_clicked(&mut self) { 331 if self.cmdchar.is_none() { 332 self.cmdchar = Some('i'); 333 } 334 } 335 pub fn exec_i_cmd(&mut self, _ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 336 self.exit()?; 337 return Ok(WarpUiCallBackType::ChangMode(ModeType::Insert)); 338 } 339 340 /// 处理输入的非零数字 341 pub fn on_nonzero_clicked(&mut self, data: &[u8]) { 342 let count = self.count; 343 if count.is_none() { 344 // 如果count为空,将第一个输入的数字作为count 345 let count = data[0] - b'0'; 346 self.count = Some(count as usize); 347 } else { 348 // 如果count不为空,将输入的数字添加到count的末尾 349 let mut count = count.unwrap(); 350 count = count * 10 + (data[0] - b'0') as usize; 351 self.count = Some(count); 352 } 353 self.count0 = true; // 将后续输入的0作为执行次数的一部分 354 } 355 356 /// 处理输入的0 357 pub fn on_zero_clicked(&mut self) { 358 // 如果0是命令的一部分,不再处理 359 if !self.count0 && self.cmdchar.is_none() { 360 self.cmdchar = Some('0'); 361 self.count0 = true; 362 } 363 let count = self.count; 364 // 如果输入的是0,且count不为空,将count扩大10倍 365 if count.is_some() { 366 let mut count = count.unwrap(); 367 count = count * 10; 368 self.count = Some(count); 369 } 370 } 371 372 /// 处理输入的d 373 pub fn on_d_clicked(&mut self) { 374 match self.cmdchar { 375 None => { 376 // 处理d 377 self.cmdchar = Some('d'); 378 } 379 Some('d') => { 380 // 处理dd 381 self.buf_op_arg = Some(BufOpArg::Line); 382 } 383 _ => { 384 self.reset(); 385 } 386 } 387 } 388 389 pub fn exec_d_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 390 let count = match self.count { 391 Some(count) => count as u16, 392 None => 1, 393 }; 394 match self.buf_op_arg { 395 Some(BufOpArg::Line) => { 396 // 删除行 397 let result = self 398 .remove_n_line(ui, count) 399 .map(|_| WarpUiCallBackType::None); 400 self.reset(); 401 return result; 402 } 403 Some(BufOpArg::Word) => { 404 // 删除单词 405 for _ in 0..count { 406 self.remove_word(ui)?; 407 } 408 self.reset(); 409 return Ok(WarpUiCallBackType::None); 410 } 411 _ => { 412 return Ok(WarpUiCallBackType::None); 413 } 414 } 415 } 416 417 pub fn on_w_clicked(&mut self) { 418 if self.cmdchar.is_none() { 419 // 按单词移动 420 self.cmdchar = Some('w'); 421 } else { 422 // 按单词操作,具体由self.cmdchar决定 423 self.buf_op_arg = Some(BufOpArg::Word); 424 } 425 } 426 427 pub fn exec_w_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 428 let count = match self.count { 429 Some(count) => count, 430 None => 1, 431 }; 432 for _ in 0..count { 433 self.jump_to_next_word(ui)?; 434 } 435 self.reset(); 436 return Ok(WarpUiCallBackType::None); 437 } 438 439 fn on_g_clicked(&mut self, ui: &mut MutexGuard<UiCore>) { 440 if self.cmdchar.is_none() { 441 self.cmdchar = Some('g'); 442 } else { 443 let first_line_size = ui.buffer.get_linesize(0); 444 self.end_pos = Some((ui.cursor.x().min(first_line_size - 1), 0)); 445 } 446 } 447 448 fn exec_g_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 449 let end_pos = self.end_pos; 450 if end_pos.is_none() { 451 return Ok(WarpUiCallBackType::None); 452 } 453 let old_y = ui.cursor.y(); 454 let (x, y) = end_pos.unwrap(); 455 let y = ui.buffer.goto_line(y.into()); 456 ui.cursor.move_to(x as u16, y as u16)?; 457 ui.render_content(y, CONTENT_WINSIZE.read().unwrap().rows as usize)?; 458 ui.cursor.highlight(Some(old_y))?; 459 self.reset(); 460 return Ok(WarpUiCallBackType::None); 461 } 462 463 #[allow(non_snake_case)] 464 fn on_G_clicked(&mut self, _ui: &mut MutexGuard<UiCore>) { 465 if self.cmdchar.is_none() { 466 self.cmdchar = Some('G'); 467 } 468 } 469 470 #[allow(non_snake_case)] 471 fn exec_G_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 472 let lineidx = match self.count { 473 Some(count) => count - 1, 474 None => ui.buffer.line_count() - 1, 475 }; 476 self.move_to_line(ui, lineidx as u16)?; 477 self.reset(); 478 return Ok(WarpUiCallBackType::None); 479 } 480 481 fn on_b_clicked(&mut self, ui: &mut MutexGuard<UiCore>) { 482 if self.cmdchar.is_none() { 483 self.cmdchar = Some('b'); 484 } else { 485 self.buf_op_arg = Some(BufOpArg::WordBegin); 486 } 487 let count = match self.count { 488 Some(count) => count, 489 None => 1, 490 }; 491 let mut pos = (ui.cursor.x(), ui.cursor.y() + ui.buffer.offset() as u16); 492 for _ in 0..count { 493 pos = self.locate_prevw_begin(ui, pos.0, pos.1); 494 } 495 self.end_pos = Some(pos); 496 } 497 498 fn exec_b_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 499 let end_pos = self.end_pos.unwrap(); 500 self.move_to_line(ui, end_pos.1)?; 501 ui.cursor.move_to_columu(end_pos.0)?; 502 self.reset(); 503 return Ok(WarpUiCallBackType::None); 504 } 505 506 fn on_dollar_clicked(&mut self) { 507 if self.cmdchar.is_none() { 508 self.cmdchar = Some('$'); 509 } 510 } 511 512 fn exec_dollar_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 513 let line_end = ui.buffer.get_linesize(ui.cursor.y()) as u16 - 1; 514 ui.cursor.move_to_columu(line_end)?; 515 self.reset(); 516 return Ok(WarpUiCallBackType::None); 517 } 518 519 fn on_e_clicked(&mut self, ui: &mut MutexGuard<UiCore>) { 520 if self.cmdchar.is_none() { 521 self.cmdchar = Some('e'); 522 } else { 523 self.buf_op_arg = Some(BufOpArg::WordEnd); 524 } 525 let count = match self.count { 526 Some(count) => count, 527 None => 1, 528 }; 529 let mut pos = (ui.cursor.x(), ui.cursor.y()); 530 for _ in 0..count { 531 pos = self.locate_nextw_ending(ui, pos.0, pos.1); 532 } 533 self.end_pos = Some(pos); 534 } 535 536 fn exec_e_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 537 let end_pos = self.end_pos; 538 if end_pos.is_none() { 539 return Ok(WarpUiCallBackType::None); 540 } 541 let end_pos = end_pos.unwrap(); 542 self.move_to_line(ui, end_pos.1)?; 543 ui.cursor.move_to_columu(end_pos.0)?; 544 self.reset(); 545 return Ok(WarpUiCallBackType::None); 546 } 547 548 fn on_f_clicked(&mut self) { 549 if self.cmdchar.is_none() { 550 self.cmdchar = Some('f'); 551 } 552 } 553 554 fn exec_f_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 555 if self.cmdbuf.len() < 2 { 556 return Ok(WarpUiCallBackType::None); 557 } 558 let to_find = self.cmdbuf.last().unwrap().clone() as char; 559 let old_x = ui.cursor.x(); 560 let old_y = ui.cursor.y(); 561 let line = 562 String::from_utf8_lossy(&ui.buffer.get_line(old_y)[old_x as usize..]).to_string(); 563 let pos = line.find(to_find); 564 if pos.is_none() { 565 return Ok(WarpUiCallBackType::None); 566 } 567 ui.cursor 568 .move_to_columu((old_x + pos.unwrap() as u16) as u16)?; 569 self.reset(); 570 return Ok(WarpUiCallBackType::None); 571 } 572 573 #[allow(non_snake_case)] 574 fn on_F_clicked(&mut self) { 575 if self.cmdchar.is_none() { 576 self.cmdchar = Some('F'); 577 } 578 } 579 580 #[allow(non_snake_case)] 581 fn exec_F_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 582 if self.cmdbuf.len() < 2 { 583 return Ok(WarpUiCallBackType::None); 584 } 585 let to_find = self.cmdbuf.last().unwrap().clone() as char; 586 let old_x = ui.cursor.x(); 587 let old_y = ui.cursor.y(); 588 let line = 589 String::from_utf8_lossy(&ui.buffer.get_line(old_y)[..old_x as usize]).to_string(); 590 let pos = line.rfind(to_find); 591 if pos.is_none() { 592 return Ok(WarpUiCallBackType::None); 593 } 594 ui.cursor.move_to_columu(pos.unwrap() as u16)?; 595 self.reset(); 596 return Ok(WarpUiCallBackType::None); 597 } 598 599 fn on_x_clicked(&mut self) { 600 if self.cmdchar.is_none() { 601 self.cmdchar = Some('x'); 602 } 603 } 604 605 fn exec_x_cmd(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 606 let y = ui.cursor.y(); 607 let x = ui.cursor.x(); 608 if x < ui.buffer.get_linesize(y) - 1 { 609 ui.buffer.remove_char(x, y); 610 ui.render_content(y, 1)?; 611 } 612 return Ok(WarpUiCallBackType::None); 613 } 614 } 615 616 pub trait StateMachine { 617 fn handle(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType>; 618 fn exit(&mut self) -> io::Result<()>; 619 } 620 621 impl StateMachine for NormalState { 622 fn handle(&mut self, ui: &mut MutexGuard<UiCore>) -> io::Result<WarpUiCallBackType> { 623 if self.cmdchar.is_none() { 624 return Ok(WarpUiCallBackType::None); 625 } 626 match self.cmdchar.unwrap() { 627 'h' => self.exec_h_cmd(ui), 628 'j' => self.exec_j_cmd(ui), 629 'k' => self.exec_k_cmd(ui), 630 'l' => self.exec_l_cmd(ui), 631 'i' => self.exec_i_cmd(ui), 632 '0' => self.exec_0_cmd(ui), 633 'd' => self.exec_d_cmd(ui), 634 'w' => self.exec_w_cmd(ui), 635 'g' => self.exec_g_cmd(ui), 636 'G' => self.exec_G_cmd(ui), 637 'b' => self.exec_b_cmd(ui), 638 '$' => self.exec_dollar_cmd(ui), 639 'e' => self.exec_e_cmd(ui), 640 'f' => self.exec_f_cmd(ui), 641 'F' => self.exec_F_cmd(ui), 642 'x' => self.exec_x_cmd(ui), 643 _ => return Ok(WarpUiCallBackType::None), 644 } 645 } 646 647 fn exit(&mut self) -> io::Result<()> { 648 self.reset(); 649 Ok(()) 650 } 651 } 652