1use crate::{Action, EditResult, Key, KeyCode, LineEditor, TextEdit};
8use std::ops::Range;
9
10mod command_line;
11mod edits;
12mod motions;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16pub(crate) enum Mode {
17 #[default]
18 Normal,
19 Insert,
20 OperatorPending(Operator),
21 Visual,
22 ReplaceChar,
24 CommandLine,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub(crate) enum Operator {
33 Delete,
34 Change,
35 Yank,
36}
37
38#[derive(Debug, Clone)]
43pub struct VimLineEditor {
44 pub(in crate::vim) cursor: usize,
45 pub(in crate::vim) mode: Mode,
46 pub(in crate::vim) visual_anchor: Option<usize>,
48 pub(in crate::vim) yank_buffer: String,
50 pub(in crate::vim) command_mode_enabled: bool,
54 pub(in crate::vim) command_buf: String,
57 pub(in crate::vim) command_cursor: usize,
59}
60
61impl Default for VimLineEditor {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67impl VimLineEditor {
68 pub fn new() -> Self {
70 Self {
71 cursor: 0,
72 mode: Mode::Normal,
73 visual_anchor: None,
74 yank_buffer: String::new(),
75 command_mode_enabled: false,
76 command_buf: String::new(),
77 command_cursor: 0,
78 }
79 }
80
81 pub fn with_command_mode(mut self, enabled: bool) -> Self {
84 self.command_mode_enabled = enabled;
85 self
86 }
87
88 pub fn command_line_buffer(&self) -> Option<&str> {
92 if self.mode == Mode::CommandLine {
93 Some(&self.command_buf)
94 } else {
95 None
96 }
97 }
98
99 pub fn command_line_cursor(&self) -> usize {
102 self.command_cursor
103 }
104
105 pub fn set_command_line(&mut self, buf: String, cursor: usize) {
109 if self.mode != Mode::CommandLine {
110 return;
111 }
112 let cursor = cursor.min(buf.len());
113 let cursor = if buf.is_char_boundary(cursor) {
116 cursor
117 } else {
118 let mut p = cursor;
119 while p > 0 && !buf.is_char_boundary(p) {
120 p -= 1;
121 }
122 p
123 };
124 self.command_buf = buf;
125 self.command_cursor = cursor;
126 }
127
128 #[cfg(test)]
130 fn mode(&self) -> Mode {
131 self.mode
132 }
133
134 fn clamp_cursor(&mut self, text: &str) {
136 self.cursor = self.cursor.min(text.len());
137 }
138
139 fn clamp_to_last_char(&mut self, text: &str) {
154 if text.is_empty() {
155 self.cursor = 0;
156 } else if self.cursor >= text.len() {
157 let mut p = text.len() - 1;
158 while p > 0 && !text.is_char_boundary(p) {
159 p -= 1;
160 }
161 self.cursor = p;
162 }
163 }
164
165 fn move_left(&mut self, text: &str) {
167 self.cursor = motions::move_left(self.cursor, text);
168 }
169
170 fn move_right(&mut self, text: &str) {
172 self.cursor = motions::move_right(self.cursor, text);
173 }
174
175 fn move_line_start(&mut self, text: &str) {
177 self.cursor = motions::move_line_start(self.cursor, text);
178 }
179
180 fn move_first_non_blank(&mut self, text: &str) {
182 self.cursor = motions::move_first_non_blank(self.cursor, text);
183 }
184
185 fn move_line_end(&mut self, text: &str) {
187 self.cursor = motions::move_line_end(self.cursor, text);
188 }
189
190 fn move_line_end_insert(&mut self, text: &str) {
192 self.cursor = motions::move_line_end_insert(self.cursor, text);
193 }
194
195 fn move_word_forward(&mut self, text: &str) {
197 self.cursor = motions::move_word_forward(self.cursor, text);
198 }
199
200 fn move_word_backward(&mut self, text: &str) {
202 self.cursor = motions::move_word_backward(self.cursor, text);
203 }
204
205 fn move_word_end(&mut self, text: &str) {
207 self.cursor = motions::move_word_end(self.cursor, text);
208 }
209
210 fn move_word_forward_word(&mut self, text: &str) {
212 self.cursor = motions::move_word_forward_word(self.cursor, text);
213 }
214
215 fn move_word_backward_word(&mut self, text: &str) {
217 self.cursor = motions::move_word_backward_word(self.cursor, text);
218 }
219
220 fn move_word_end_word(&mut self, text: &str) {
222 self.cursor = motions::move_word_end_word(self.cursor, text);
223 }
224
225 fn move_up(&mut self, text: &str) {
227 self.cursor = motions::move_up(self.cursor, text);
228 }
229
230 fn move_down(&mut self, text: &str) {
232 self.cursor = motions::move_down(self.cursor, text);
233 }
234
235 fn move_to_matching_bracket(&mut self, text: &str) {
238 self.cursor = motions::move_to_matching_bracket(self.cursor, text);
239 }
240
241 fn dispatch_motion(&mut self, code: KeyCode, text: &str) -> bool {
253 match code {
254 KeyCode::Char('h') | KeyCode::Left => self.move_left(text),
255 KeyCode::Char('l') | KeyCode::Right => self.move_right(text),
256 KeyCode::Char('j') => self.move_down(text),
257 KeyCode::Char('k') => self.move_up(text),
258 KeyCode::Char('0') | KeyCode::Home => self.move_line_start(text),
259 KeyCode::Char('^') => self.move_first_non_blank(text),
260 KeyCode::Char('$') | KeyCode::End => self.move_line_end(text),
261 KeyCode::Char('w') => self.move_word_forward(text),
262 KeyCode::Char('b') => self.move_word_backward(text),
263 KeyCode::Char('e') => self.move_word_end(text),
264 KeyCode::Char('W') => self.move_word_forward_word(text),
265 KeyCode::Char('B') => self.move_word_backward_word(text),
266 KeyCode::Char('E') => self.move_word_end_word(text),
267 KeyCode::Char('%') => self.move_to_matching_bracket(text),
268 _ => return false,
269 }
270 true
271 }
272
273 fn handle_normal(&mut self, key: Key, text: &str) -> EditResult {
275 match key.code {
280 KeyCode::Char('k') if motions::is_on_first_line(self.cursor, text) => {
281 return EditResult::action(Action::HistoryPrev);
282 }
283 KeyCode::Char('j') if motions::is_on_last_line(self.cursor, text) => {
284 return EditResult::action(Action::HistoryNext);
285 }
286 _ => {}
287 }
288
289 if self.dispatch_motion(key.code, text) {
292 self.clamp_to_last_char(text);
297 return EditResult::cursor_only();
298 }
299
300 match key.code {
301 KeyCode::Char('i') => {
303 self.mode = Mode::Insert;
304 EditResult::none()
305 }
306 KeyCode::Char('a') => {
307 self.mode = Mode::Insert;
308 self.move_right(text);
309 EditResult::none()
310 }
311 KeyCode::Char('A') => {
312 self.mode = Mode::Insert;
313 self.move_line_end_insert(text);
314 EditResult::none()
315 }
316 KeyCode::Char('I') => {
317 self.mode = Mode::Insert;
318 self.move_first_non_blank(text);
319 EditResult::none()
320 }
321 KeyCode::Char('o') => {
322 self.mode = Mode::Insert;
323 self.move_line_end(text);
324 let pos = self.cursor;
325 self.cursor = pos + 1;
326 EditResult::edit(TextEdit::Insert {
327 at: pos,
328 text: "\n".to_string(),
329 })
330 }
331 KeyCode::Char('O') => {
332 self.mode = Mode::Insert;
333 self.move_line_start(text);
334 let pos = self.cursor;
335 EditResult::edit(TextEdit::Insert {
336 at: pos,
337 text: "\n".to_string(),
338 })
339 }
340
341 KeyCode::Char('v') => {
343 self.mode = Mode::Visual;
344 self.visual_anchor = Some(self.cursor);
345 EditResult::none()
346 }
347
348 KeyCode::Char('c') if key.ctrl => EditResult::action(Action::Cancel),
350
351 KeyCode::Char('d') => {
353 self.mode = Mode::OperatorPending(Operator::Delete);
354 EditResult::none()
355 }
356 KeyCode::Char('c') => {
357 self.mode = Mode::OperatorPending(Operator::Change);
358 EditResult::none()
359 }
360 KeyCode::Char('y') => {
361 self.mode = Mode::OperatorPending(Operator::Yank);
362 EditResult::none()
363 }
364
365 KeyCode::Char('x') => self.delete_char(text),
367 KeyCode::Char('D') => self.delete_to_end(text),
368 KeyCode::Char('C') => {
369 self.mode = Mode::Insert;
370 self.delete_to_end(text)
371 }
372
373 KeyCode::Char('r') => {
375 self.mode = Mode::ReplaceChar;
376 EditResult::none()
377 }
378
379 KeyCode::Char(':') if self.command_mode_enabled => {
383 self.mode = Mode::CommandLine;
384 self.command_buf.clear();
385 self.command_cursor = 0;
386 EditResult::none()
387 }
388
389 KeyCode::Char('p') => self.paste_after(text),
391 KeyCode::Char('P') => self.paste_before(text),
392
393 KeyCode::Up => EditResult::action(Action::HistoryPrev),
395 KeyCode::Down => EditResult::action(Action::HistoryNext),
396
397 KeyCode::Enter if !key.shift => EditResult::action(Action::Submit),
399
400 KeyCode::Enter if key.shift => {
402 self.mode = Mode::Insert;
403 let pos = self.cursor;
404 self.cursor = pos + 1;
405 EditResult::edit(TextEdit::Insert {
406 at: pos,
407 text: "\n".to_string(),
408 })
409 }
410
411 KeyCode::Escape => EditResult::none(),
414
415 _ => EditResult::none(),
416 }
417 }
418
419 fn handle_insert(&mut self, key: Key, text: &str) -> EditResult {
421 match key.code {
422 KeyCode::Escape => {
423 self.mode = Mode::Normal;
424 if self.cursor > 0 {
426 self.move_left(text);
427 }
428 EditResult::none()
429 }
430
431 KeyCode::Char('c') if key.ctrl => {
433 self.mode = Mode::Normal;
434 EditResult::none()
435 }
436
437 KeyCode::Char(c) if !key.ctrl && !key.alt => {
438 let pos = self.cursor;
439 self.cursor = pos + c.len_utf8();
440 EditResult::edit(TextEdit::Insert {
441 at: pos,
442 text: c.to_string(),
443 })
444 }
445
446 KeyCode::Backspace => {
447 if self.cursor == 0 {
448 return EditResult::none();
449 }
450 let mut start = self.cursor - 1;
451 while start > 0 && !text.is_char_boundary(start) {
452 start -= 1;
453 }
454 let end = self.cursor; self.cursor = start;
456 EditResult::edit(TextEdit::Delete { start, end })
457 }
458
459 KeyCode::Delete => self.delete_char(text),
460
461 KeyCode::Left => {
462 self.move_left(text);
463 EditResult::cursor_only()
464 }
465 KeyCode::Right => {
466 self.move_right(text);
467 EditResult::cursor_only()
468 }
469 KeyCode::Up => {
470 self.move_up(text);
471 EditResult::cursor_only()
472 }
473 KeyCode::Down => {
474 self.move_down(text);
475 EditResult::cursor_only()
476 }
477 KeyCode::Home => {
478 self.move_line_start(text);
479 EditResult::cursor_only()
480 }
481 KeyCode::End => {
482 self.move_line_end_insert(text);
484 EditResult::cursor_only()
485 }
486
487 KeyCode::Enter => {
489 let pos = self.cursor;
490 self.cursor = pos + 1;
491 EditResult::edit(TextEdit::Insert {
492 at: pos,
493 text: "\n".to_string(),
494 })
495 }
496
497 _ => EditResult::none(),
498 }
499 }
500
501 fn handle_operator_pending(&mut self, op: Operator, key: Key, text: &str) -> EditResult {
503 if key.code == KeyCode::Escape {
505 self.mode = Mode::Normal;
506 return EditResult::none();
507 }
508
509 let is_line_op = matches!(
511 (op, key.code),
512 (Operator::Delete, KeyCode::Char('d'))
513 | (Operator::Change, KeyCode::Char('c'))
514 | (Operator::Yank, KeyCode::Char('y'))
515 );
516
517 if is_line_op {
518 self.mode = Mode::Normal;
519 return self.apply_operator_line(op, text);
520 }
521
522 let start = self.cursor;
524 match key.code {
525 KeyCode::Char('w') => {
526 if op == Operator::Change {
529 self.move_word_end(text);
530 if self.cursor < text.len() {
532 self.cursor += 1;
533 }
534 } else {
535 self.move_word_forward(text);
536 }
537 }
538 KeyCode::Char('b') => self.move_word_backward(text),
539 KeyCode::Char('e') => {
540 self.move_word_end(text);
541 if self.cursor < text.len() {
543 self.cursor += 1;
544 }
545 }
546 KeyCode::Char('W') => {
547 if op == Operator::Change {
550 self.move_word_end_word(text);
551 if self.cursor < text.len() {
552 self.cursor += 1;
553 }
554 } else {
555 self.move_word_forward_word(text);
556 }
557 }
558 KeyCode::Char('B') => self.move_word_backward_word(text),
559 KeyCode::Char('E') => {
560 self.move_word_end_word(text);
561 if self.cursor < text.len() {
563 self.cursor += 1;
564 }
565 }
566 KeyCode::Char('0') | KeyCode::Home => self.move_line_start(text),
567 KeyCode::Char('$') | KeyCode::End => self.move_line_end(text),
568 KeyCode::Char('^') => self.move_first_non_blank(text),
569 KeyCode::Char('h') | KeyCode::Left => self.move_left(text),
570 KeyCode::Char('l') | KeyCode::Right => self.move_right(text),
571 KeyCode::Char('j') => self.move_down(text),
572 KeyCode::Char('k') => self.move_up(text),
573 _ => {
574 self.mode = Mode::Normal;
576 return EditResult::none();
577 }
578 }
579
580 let end = self.cursor;
581 self.mode = Mode::Normal;
582
583 if start == end {
584 return EditResult::none();
585 }
586
587 let (range_start, range_end) = if start < end {
588 (start, end)
589 } else {
590 (end, start)
591 };
592
593 self.apply_operator(op, range_start, range_end, text)
594 }
595
596 fn handle_visual(&mut self, key: Key, text: &str) -> EditResult {
598 match key.code {
599 KeyCode::Escape => {
600 self.mode = Mode::Normal;
601 self.visual_anchor = None;
602 EditResult::none()
603 }
604
605 KeyCode::Char('h') | KeyCode::Left => {
609 self.move_left(text);
610 EditResult::cursor_only()
611 }
612 KeyCode::Char('l') | KeyCode::Right => {
613 self.move_right(text);
614 EditResult::cursor_only()
615 }
616 KeyCode::Char('j') => {
617 self.move_down(text);
618 EditResult::cursor_only()
619 }
620 KeyCode::Char('k') => {
621 self.move_up(text);
622 EditResult::cursor_only()
623 }
624 KeyCode::Char('w') => {
625 self.move_word_forward(text);
626 EditResult::cursor_only()
627 }
628 KeyCode::Char('b') => {
629 self.move_word_backward(text);
630 EditResult::cursor_only()
631 }
632 KeyCode::Char('e') => {
633 self.move_word_end(text);
634 EditResult::cursor_only()
635 }
636 KeyCode::Char('W') => {
637 self.move_word_forward_word(text);
638 EditResult::cursor_only()
639 }
640 KeyCode::Char('B') => {
641 self.move_word_backward_word(text);
642 EditResult::cursor_only()
643 }
644 KeyCode::Char('E') => {
645 self.move_word_end_word(text);
646 EditResult::cursor_only()
647 }
648 KeyCode::Char('0') | KeyCode::Home => {
649 self.move_line_start(text);
650 EditResult::cursor_only()
651 }
652 KeyCode::Char('$') | KeyCode::End => {
653 self.move_line_end(text);
654 EditResult::cursor_only()
655 }
656
657 KeyCode::Char('d') | KeyCode::Char('x') => {
659 let (start, end) = self.selection_range();
660 self.mode = Mode::Normal;
661 self.visual_anchor = None;
662 self.apply_operator(Operator::Delete, start, end, text)
663 }
664 KeyCode::Char('c') => {
665 let (start, end) = self.selection_range();
666 self.mode = Mode::Normal;
667 self.visual_anchor = None;
668 self.apply_operator(Operator::Change, start, end, text)
669 }
670 KeyCode::Char('y') => {
671 let (start, end) = self.selection_range();
672 self.mode = Mode::Normal;
673 self.visual_anchor = None;
674 self.apply_operator(Operator::Yank, start, end, text)
675 }
676
677 _ => EditResult::none(),
678 }
679 }
680
681 fn handle_replace_char(&mut self, key: Key, text: &str) -> EditResult {
683 self.mode = Mode::Normal;
684
685 match key.code {
686 KeyCode::Escape => EditResult::none(),
687 KeyCode::Char(c) if !key.ctrl && !key.alt => {
688 if self.cursor >= text.len() {
690 return EditResult::none();
691 }
692
693 let mut end = self.cursor + 1;
695 while end < text.len() && !text.is_char_boundary(end) {
696 end += 1;
697 }
698
699 EditResult {
702 edits: vec![
703 TextEdit::Insert {
704 at: self.cursor,
705 text: c.to_string(),
706 },
707 TextEdit::Delete {
708 start: self.cursor,
709 end,
710 },
711 ],
712 ..Default::default()
713 }
714 }
715 _ => EditResult::none(),
716 }
717 }
718
719 fn selection_range(&self) -> (usize, usize) {
721 let anchor = self.visual_anchor.unwrap_or(self.cursor);
722 if self.cursor < anchor {
723 (self.cursor, anchor)
724 } else {
725 (anchor, self.cursor + 1) }
727 }
728}
729
730impl LineEditor for VimLineEditor {
731 fn handle_key(&mut self, key: Key, text: &str) -> EditResult {
732 self.clamp_cursor(text);
733
734 let result = match self.mode {
735 Mode::Normal => self.handle_normal(key, text),
736 Mode::Insert => self.handle_insert(key, text),
737 Mode::OperatorPending(op) => self.handle_operator_pending(op, key, text),
738 Mode::Visual => self.handle_visual(key, text),
739 Mode::ReplaceChar => self.handle_replace_char(key, text),
740 Mode::CommandLine => self.handle_command_line(key),
741 };
742
743 if let Some(ref yanked) = result.yanked {
745 self.yank_buffer = yanked.clone();
746 }
747
748 result
749 }
750
751 fn cursor(&self) -> usize {
752 self.cursor
753 }
754
755 fn status(&self) -> &str {
756 match self.mode {
757 Mode::Normal => "NORMAL",
758 Mode::Insert => "INSERT",
759 Mode::OperatorPending(Operator::Delete) => "d...",
760 Mode::OperatorPending(Operator::Change) => "c...",
761 Mode::OperatorPending(Operator::Yank) => "y...",
762 Mode::Visual => "VISUAL",
763 Mode::ReplaceChar => "r...",
764 Mode::CommandLine => "COMMAND",
765 }
766 }
767
768 fn selection(&self) -> Option<Range<usize>> {
769 if self.mode == Mode::Visual {
770 let (start, end) = self.selection_range();
771 Some(start..end)
772 } else {
773 None
774 }
775 }
776
777 fn reset(&mut self) {
778 self.cursor = 0;
779 self.mode = Mode::Normal;
780 self.visual_anchor = None;
781 self.command_buf.clear();
782 self.command_cursor = 0;
783 }
785
786 fn set_cursor(&mut self, pos: usize, text: &str) {
787 let pos = pos.min(text.len());
789 self.cursor = if text.is_char_boundary(pos) {
790 pos
791 } else {
792 let mut p = pos;
794 while p > 0 && !text.is_char_boundary(p) {
795 p -= 1;
796 }
797 p
798 };
799 }
800}
801
802#[cfg(test)]
803mod tests;