1use alloc::{string::String, vec::Vec};
2use core::{fmt, mem};
3
4use crate::{Event, Key, Motion, Operator, Parser, TextObject, Word};
5
6pub const VI_DEFAULT_REGISTER: char = '"';
7
8#[derive(Debug)]
9pub struct ViContext<F: FnMut(Event)> {
10 callback: F,
11 selection: bool,
12 pending_change: Option<Vec<Event>>,
13 change: Option<Vec<Event>>,
14 set_mode: Option<ViMode>,
15}
16
17impl<F: FnMut(Event)> ViContext<F> {
18 fn start_change(&mut self) {
19 if self.pending_change.is_none() {
20 self.pending_change = Some(Vec::new());
21 }
22 (self.callback)(Event::ChangeStart);
23 }
24
25 fn finish_change(&mut self) {
26 self.change = self.pending_change.take();
27 (self.callback)(Event::ChangeFinish);
28 }
29
30 fn e(&mut self, event: Event) {
31 match &mut self.pending_change {
32 Some(change) => change.push(event.clone()),
33 None => {}
34 }
35 (self.callback)(event);
36 }
37}
38
39#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
40pub struct ViCmd {
41 register: Option<char>,
42 count: Option<usize>,
43 operator: Option<Operator>,
44 motion: Option<Motion>,
45 text_object: Option<TextObject>,
46}
47
48impl fmt::Display for ViCmd {
49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 if let Some(register) = self.register {
51 write!(f, "\"{register}")?;
52 }
53 if let Some(count) = self.count {
54 write!(f, "{count}")?;
55 }
56 if let Some(operator) = self.operator {
57 write!(f, "{operator:?}")?;
58 }
59 if let Some(motion) = self.motion {
60 write!(f, "{motion:?}")?;
61 }
62 if let Some(text_object) = self.text_object {
63 write!(f, "{text_object:?}")?;
64 }
65 Ok(())
66 }
67}
68
69impl ViCmd {
70 pub fn repeat<F: FnMut(usize)>(&mut self, mut f: F) {
72 for i in 0..self.count.take().unwrap_or(1) {
73 f(i);
74 }
75 }
76
77 pub fn motion<F: FnMut(Event)>(&mut self, motion: Motion, ctx: &mut ViContext<F>) {
79 self.motion = Some(motion);
80 self.run(ctx);
81 }
82
83 pub fn operator<F: FnMut(Event)>(&mut self, operator: Operator, ctx: &mut ViContext<F>) {
85 if self.operator == Some(operator) {
86 self.motion = Some(Motion::Line);
87 } else {
88 self.operator = Some(operator);
89 }
90 self.run(ctx);
91 }
92
93 pub fn text_object<F: FnMut(Event)>(
95 &mut self,
96 text_object: TextObject,
97 ctx: &mut ViContext<F>,
98 ) -> bool {
99 if !self.motion.map_or(false, |motion| motion.text_object()) {
100 return false;
102 }
103
104 self.text_object = Some(text_object);
106 self.run(ctx);
107 true
108 }
109
110 pub fn run<F: FnMut(Event)>(&mut self, ctx: &mut ViContext<F>) -> bool {
112 match self.motion {
113 Some(motion) => {
114 if motion.text_object() && self.text_object.is_none() {
115 return false;
117 }
118 }
119 None => {
120 if !ctx.selection {
121 return false;
123 }
124 }
125 }
126
127 let register = self.register.take().unwrap_or(VI_DEFAULT_REGISTER);
128 let count = self.count.take().unwrap_or(1);
129 let motion = self.motion.take().unwrap_or(Motion::Selection);
130 let text_object = self.text_object.take();
131
132 match self.operator.take() {
135 Some(operator) => {
136 ctx.start_change();
137
138 match motion {
139 Motion::Around => ctx.e(Event::SelectTextObject(
140 text_object.expect("no text object"),
141 true,
142 )),
143 Motion::Inside => ctx.e(Event::SelectTextObject(
144 text_object.expect("no text object"),
145 false,
146 )),
147 Motion::Line => {
148 ctx.e(Event::SelectLineStart);
149 }
150 Motion::Selection => {}
151 _ => {
152 ctx.e(Event::SelectStart);
153 for _ in 0..count {
154 ctx.e(Event::Motion(motion));
155 }
156 }
157 }
158
159 let mut enter_insert_mode = false;
160 match operator {
161 Operator::AutoIndent => {
162 ctx.e(Event::AutoIndent);
163 }
164 Operator::Change => {
165 ctx.e(Event::Yank { register });
166 ctx.e(Event::Delete);
167 enter_insert_mode = true;
168 }
169 Operator::Delete => {
170 ctx.e(Event::Yank { register });
171 ctx.e(Event::Delete);
172 }
173 Operator::ShiftLeft => {
174 ctx.e(Event::ShiftLeft);
175 }
176 Operator::ShiftRight => {
177 ctx.e(Event::ShiftRight);
178 }
179 Operator::SwapCase => {
180 ctx.e(Event::SwapCase);
181 }
182 Operator::Yank => {
183 ctx.e(Event::Yank { register });
184 }
185 }
186
187 ctx.e(Event::SelectClear);
188 if enter_insert_mode {
189 ctx.set_mode = Some(ViMode::Insert);
190 } else {
191 ctx.finish_change();
192 ctx.set_mode = Some(ViMode::Normal);
193 }
194 }
195 None => match motion {
196 Motion::Around => ctx.e(Event::SelectTextObject(
197 text_object.expect("no text object"),
198 true,
199 )),
200 Motion::Inside => ctx.e(Event::SelectTextObject(
201 text_object.expect("no text object"),
202 false,
203 )),
204 _ => {
205 for _ in 0..count {
206 ctx.e(Event::Motion(motion));
207 }
208 }
209 },
210 }
211
212 true
213 }
214}
215
216#[derive(Clone, Debug, Eq, PartialEq)]
217pub enum ViMode {
218 Normal,
220 Extra(char),
222 Insert,
224 Replace,
226 Visual,
228 VisualLine,
230 Command { value: String },
232 Search { value: String, forwards: bool },
234}
235
236#[derive(Debug)]
237pub struct ViParser {
238 pub mode: ViMode,
239 pub cmd: ViCmd,
240 pub register_mode: ViMode,
241 pub semicolon_motion: Option<Motion>,
242 pub pending_change: Option<Vec<Event>>,
243 pub last_change: Option<Vec<Event>>,
244}
245
246impl ViParser {
247 pub fn new() -> Self {
248 Self {
249 mode: ViMode::Normal,
250 cmd: ViCmd::default(),
251 register_mode: ViMode::Normal,
252 semicolon_motion: None,
253 pending_change: None,
254 last_change: None,
255 }
256 }
257}
258
259impl Parser for ViParser {
260 fn reset(&mut self) {
261 self.mode = ViMode::Normal;
262 self.cmd = ViCmd::default();
263 }
264
265 fn parse<F: FnMut(Event)>(&mut self, key: Key, selection: bool, callback: F) {
266 let cmd = &mut self.cmd;
268 let key = key.normalize();
270 let mut ctx = ViContext {
272 selection,
273 callback,
274 pending_change: self.pending_change.take(),
275 change: None,
276 set_mode: None,
277 };
278 let ctx = &mut ctx;
279 match self.mode {
280 ViMode::Normal | ViMode::Visual | ViMode::VisualLine => match key {
281 Key::Backspace => cmd.motion(Motion::Left, ctx),
282 Key::Backtab => (),
284 Key::Delete => {
285 ctx.start_change();
286 cmd.repeat(|_| ctx.e(Event::DeleteInLine));
287 ctx.finish_change();
288 }
289 Key::Down => cmd.motion(Motion::Down, ctx),
290 Key::End => cmd.motion(Motion::End, ctx),
291 Key::Enter => {
292 cmd.motion(Motion::Down, ctx);
293 cmd.motion(Motion::SoftHome, ctx);
294 }
295 Key::Escape => {
296 self.reset();
297 ctx.e(Event::Escape);
298 }
299 Key::Home => cmd.motion(Motion::Home, ctx),
300 Key::Left => cmd.motion(Motion::LeftInLine, ctx),
301 Key::PageDown => cmd.motion(Motion::PageDown, ctx),
302 Key::PageUp => cmd.motion(Motion::PageUp, ctx),
303 Key::Right => cmd.motion(Motion::RightInLine, ctx),
304 Key::Tab => (),
306 Key::Up => cmd.motion(Motion::Up, ctx),
307 Key::Char(c) => match c {
308 'a' => {
310 if cmd.operator.is_some() || self.mode != ViMode::Normal {
311 cmd.motion(Motion::Around, ctx);
312 } else {
313 ctx.start_change();
314 ViCmd::default().motion(Motion::Right, ctx);
315 self.mode = ViMode::Insert;
316 }
317 }
318 'A' => {
320 ctx.start_change();
321 ViCmd::default().motion(Motion::End, ctx);
322 self.mode = ViMode::Insert;
323 }
324 'b' => {
326 if !cmd.text_object(TextObject::Block, ctx) {
327 cmd.motion(Motion::PreviousWordStart(Word::Lower), ctx);
328 }
329 }
330 'B' => {
333 if !cmd.text_object(TextObject::Block, ctx) {
334 cmd.motion(Motion::PreviousWordStart(Word::Upper), ctx);
335 }
336 }
337 'c' => {
339 cmd.operator(Operator::Change, ctx);
340 }
341 'C' => {
343 cmd.operator(Operator::Change, ctx);
344 cmd.motion(Motion::End, ctx);
345 }
346 'd' => {
348 cmd.operator(Operator::Delete, ctx);
349 }
350 'D' => {
352 cmd.operator(Operator::Delete, ctx);
353 cmd.motion(Motion::End, ctx);
354 }
355 'e' => cmd.motion(Motion::NextWordEnd(Word::Lower), ctx),
357 'E' => cmd.motion(Motion::NextWordEnd(Word::Upper), ctx),
359 'f' => {
361 self.mode = ViMode::Extra(c);
362 }
363 'F' => {
365 self.mode = ViMode::Extra(c);
366 }
367 'g' => {
369 self.mode = ViMode::Extra(c);
370 }
371 'G' => match cmd.count.take() {
373 Some(line) => cmd.motion(Motion::GotoLine(line), ctx),
374 None => cmd.motion(Motion::GotoEof, ctx),
375 },
376 'h' => cmd.motion(Motion::LeftInLine, ctx),
378 'H' => cmd.motion(Motion::ScreenHigh, ctx),
380 'i' => {
382 if cmd.operator.is_some() || self.mode != ViMode::Normal {
383 cmd.motion(Motion::Inside, ctx);
384 } else {
385 ctx.start_change();
386 self.mode = ViMode::Insert;
387 }
388 }
389 'I' => {
391 ctx.start_change();
392 ViCmd::default().motion(Motion::SoftHome, ctx);
393 self.mode = ViMode::Insert;
394 }
395 'j' => cmd.motion(Motion::Down, ctx),
397 'J' => {}
399 'k' => cmd.motion(Motion::Up, ctx),
401 'K' => {}
403 'l' => cmd.motion(Motion::RightInLine, ctx),
405 'L' => cmd.motion(Motion::ScreenLow, ctx),
407 'm' => {}
409 'M' => cmd.motion(Motion::ScreenMiddle, ctx),
411 'n' => cmd.motion(Motion::NextSearch, ctx),
413 'N' => cmd.motion(Motion::PreviousSearch, ctx),
415 'o' => {
417 ctx.start_change();
418 ViCmd::default().motion(Motion::End, ctx);
419 ctx.e(Event::NewLine);
420 self.mode = ViMode::Insert;
421 }
422 'O' => {
424 ctx.start_change();
425 ViCmd::default().motion(Motion::Home, ctx);
426 ctx.e(Event::NewLine);
427 ViCmd::default().motion(Motion::Up, ctx);
428 self.mode = ViMode::Insert;
429 }
430 'p' => {
432 if !cmd.text_object(TextObject::Paragraph, ctx) {
433 let register = cmd.register.unwrap_or(VI_DEFAULT_REGISTER);
434 ctx.e(Event::Put {
435 register,
436 after: true,
437 });
438 }
439 }
440 'P' => {
442 let register = cmd.register.unwrap_or(VI_DEFAULT_REGISTER);
443 ctx.e(Event::Put {
444 register,
445 after: false,
446 });
447 }
448 'r' => {
451 self.mode = ViMode::Extra(c);
452 }
453 'R' => {
455 ctx.start_change();
456 self.mode = ViMode::Replace;
457 }
458 's' => {
460 if !cmd.text_object(TextObject::Sentence, ctx) {
461 ctx.start_change();
462 cmd.repeat(|_| ctx.e(Event::DeleteInLine));
463 self.mode = ViMode::Insert;
464 }
465 }
466 'S' => {
468 cmd.operator(Operator::Change, ctx);
469 cmd.motion(Motion::Line, ctx);
470 }
471 't' => {
473 if !cmd.text_object(TextObject::Tag, ctx) {
474 self.mode = ViMode::Extra(c);
475 }
476 }
477 'T' => {
479 self.mode = ViMode::Extra(c);
480 }
481 'u' => {
483 ctx.e(Event::Undo);
484 }
485 'v' => {
488 if self.mode == ViMode::Visual {
490 ctx.e(Event::SelectClear);
491 self.mode = ViMode::Normal;
492 } else {
493 ctx.e(Event::SelectStart);
494 self.mode = ViMode::Visual;
495 }
496 }
497 'V' => {
499 if self.mode == ViMode::VisualLine {
500 ctx.e(Event::SelectClear);
501 self.mode = ViMode::Normal;
502 } else {
503 ctx.e(Event::SelectLineStart);
504 self.mode = ViMode::VisualLine;
505 }
506 }
507 'w' => {
509 if !cmd.text_object(TextObject::Word(Word::Lower), ctx) {
510 cmd.motion(Motion::NextWordStart(Word::Lower), ctx);
511 }
512 }
513 'W' => {
515 if !cmd.text_object(TextObject::Word(Word::Upper), ctx) {
516 cmd.motion(Motion::NextWordStart(Word::Upper), ctx);
517 }
518 }
519 'x' => {
521 ctx.start_change();
522 cmd.repeat(|_| ctx.e(Event::DeleteInLine));
523 ctx.finish_change();
524 }
525 'X' => {
527 ctx.start_change();
528 cmd.repeat(|_| ctx.e(Event::BackspaceInLine));
529 ctx.finish_change();
530 }
531 'y' => cmd.operator(Operator::Yank, ctx),
533 'Y' => {
535 cmd.operator(Operator::Yank, ctx);
536 cmd.motion(Motion::Line, ctx);
537 }
538 'z' => {
540 self.mode = ViMode::Extra(c);
541 }
542 'Z' => {
544 self.mode = ViMode::Extra(c);
545 }
546 '0' => match cmd.count {
548 Some(ref mut count) => {
549 *count = count.saturating_mul(10);
550 }
551 None => {
552 cmd.motion(Motion::Home, ctx);
553 }
554 },
555 '1'..='9' => {
557 let number = (c as u32).saturating_sub('0' as u32) as usize;
558 cmd.count = Some(match cmd.count.take() {
559 Some(count) => count.saturating_mul(10).saturating_add(number),
560 None => number,
561 });
562 }
563 '`' => if !cmd.text_object(TextObject::Ticks, ctx) {},
565 '~' => cmd.operator(Operator::SwapCase, ctx),
567 '$' => cmd.motion(Motion::End, ctx),
570 '^' => cmd.motion(Motion::SoftHome, ctx),
573 '(' => if !cmd.text_object(TextObject::Parentheses, ctx) {},
576 ')' => if !cmd.text_object(TextObject::Parentheses, ctx) {},
578 '-' => {
580 cmd.motion(Motion::Up, ctx);
581 cmd.motion(Motion::SoftHome, ctx);
582 }
583 '+' => {
585 cmd.motion(Motion::Down, ctx);
586 cmd.motion(Motion::SoftHome, ctx);
587 }
588 '=' => cmd.operator(Operator::AutoIndent, ctx),
590 '[' => if !cmd.text_object(TextObject::SquareBrackets, ctx) {},
592 '{' => if !cmd.text_object(TextObject::CurlyBrackets, ctx) {},
594 ']' => if !cmd.text_object(TextObject::SquareBrackets, ctx) {},
596 '}' => if !cmd.text_object(TextObject::CurlyBrackets, ctx) {},
598 ';' => {
600 if let Some(motion) = self.semicolon_motion {
601 cmd.motion(motion, ctx);
602 }
603 }
604 ':' => {
606 self.mode = ViMode::Command {
607 value: String::new(),
608 };
609 }
610 '\'' => if !cmd.text_object(TextObject::SingleQuotes, ctx) {},
612 '"' => {
614 if !cmd.text_object(TextObject::DoubleQuotes, ctx) {
615 self.register_mode = self.mode.clone();
616 self.mode = ViMode::Extra(c);
617 }
618 }
619 ',' => {
621 if let Some(motion) = self.semicolon_motion {
622 if let Some(reverse) = motion.reverse() {
623 cmd.motion(reverse, ctx);
624 }
625 }
626 }
627 '<' => {
629 if !cmd.text_object(TextObject::AngleBrackets, ctx) {
630 cmd.operator(Operator::ShiftLeft, ctx);
631 }
632 }
633 '.' => {
635 if let Some(change) = &self.last_change {
636 ctx.start_change();
637 for event in change.iter() {
638 ctx.e(event.clone());
639 }
640 ctx.finish_change();
641 }
642 }
643 '>' => {
645 if !cmd.text_object(TextObject::AngleBrackets, ctx) {
646 cmd.operator(Operator::ShiftRight, ctx);
647 }
648 }
649 '/' => {
651 self.mode = ViMode::Search {
652 value: String::new(),
653 forwards: true,
654 };
655 }
656 '?' => {
658 self.mode = ViMode::Search {
659 value: String::new(),
660 forwards: false,
661 };
662 }
663 ' ' => cmd.motion(Motion::Right, ctx),
665 _ => {}
666 },
667 Key::Ctrl(c) => {
668 }
670 },
671 ViMode::Extra(extra) => match extra {
672 'f' | 'F' | 't' | 'T' => {
674 match key {
675 Key::Char(c) => {
676 let motion = match extra {
677 'f' => Motion::NextChar(c),
678 'F' => Motion::PreviousChar(c),
679 't' => Motion::NextCharTill(c),
680 'T' => Motion::PreviousCharTill(c),
681 _ => unreachable!(),
682 };
683 cmd.motion(motion, ctx);
684 self.semicolon_motion = Some(motion);
685 }
686 _ => {}
687 }
688 self.reset();
689 }
690 'g' => {
692 match key {
693 Key::Char(c) => match c {
694 'e' => cmd.motion(Motion::PreviousWordEnd(Word::Lower), ctx),
696 'E' => cmd.motion(Motion::PreviousWordEnd(Word::Upper), ctx),
698 'g' => match cmd.count.take() {
699 Some(line) => cmd.motion(Motion::GotoLine(line), ctx),
700 None => cmd.motion(Motion::GotoLine(1), ctx),
701 },
702 'n' => {
703 cmd.motion(Motion::Inside, ctx);
704 cmd.text_object(TextObject::Search { forwards: true }, ctx);
705 }
706 'N' => {
707 cmd.motion(Motion::Inside, ctx);
708 cmd.text_object(TextObject::Search { forwards: false }, ctx);
709 }
710 _ => {}
712 },
713 _ => {}
715 }
716 self.reset();
717 }
718 'r' => {
720 match key {
721 Key::Char(c) => {
722 ctx.start_change();
724 ctx.e(Event::Delete);
725 ctx.e(Event::Insert(c));
726 ViCmd::default().motion(Motion::LeftInLine, ctx);
727 ctx.finish_change();
728 }
729 _ => {}
730 }
731 self.reset();
732 }
733 '"' => {
735 match key {
736 Key::Char(c) => {
737 cmd.register = Some(c);
738 }
739 _ => {}
740 }
741 self.mode = self.register_mode.clone();
742 self.register_mode = ViMode::Normal;
743 }
744 _ => {
745 log::info!("TODO: extra command {:?}{:?}", extra, key);
747 self.reset();
748 }
749 },
750 ViMode::Insert | ViMode::Replace => match key {
751 Key::Backspace => ctx.e(Event::Backspace),
753 Key::Backtab => ctx.e(Event::ShiftLeft),
754 Key::Char(c) => {
755 if self.mode == ViMode::Replace {
756 ctx.e(Event::Delete);
757 }
758 ctx.e(Event::Insert(c));
759 }
760 Key::Ctrl(c) => {
761 }
763 Key::Down => ViCmd::default().motion(Motion::Down, ctx),
764 Key::Delete => ctx.e(Event::Delete),
765 Key::End => ViCmd::default().motion(Motion::End, ctx),
766 Key::Enter => ctx.e(Event::NewLine),
767 Key::Escape => {
768 ViCmd::default().motion(Motion::LeftInLine, ctx);
769 ctx.finish_change();
770 self.reset();
771 }
772 Key::Home => ViCmd::default().motion(Motion::Home, ctx),
773 Key::Left => ViCmd::default().motion(Motion::LeftInLine, ctx),
774 Key::PageDown => ViCmd::default().motion(Motion::PageDown, ctx),
775 Key::PageUp => ViCmd::default().motion(Motion::PageUp, ctx),
776 Key::Right => ViCmd::default().motion(Motion::RightInLine, ctx),
777 Key::Tab => ctx.e(Event::ShiftRight),
778 Key::Up => ViCmd::default().motion(Motion::Up, ctx),
779 },
780 ViMode::Command { ref mut value } => match key {
781 Key::Escape => {
782 self.reset();
783 }
784 Key::Enter => {
785 self.reset();
787 }
788 Key::Backspace => {
789 if value.pop().is_none() {
790 self.reset();
791 }
792 }
793 Key::Char(c) => {
794 value.push(c);
795 }
796 _ => {
797 }
799 },
800 ViMode::Search {
801 ref mut value,
802 forwards,
803 } => match key {
804 Key::Escape => {
805 self.reset();
806 }
807 Key::Enter => {
808 let mut tmp = String::new();
810 mem::swap(value, &mut tmp);
811 ctx.e(Event::SetSearch(tmp, forwards));
812 self.reset();
813 ViCmd::default().motion(Motion::NextSearch, ctx);
814 }
815 Key::Backspace => {
816 if value.pop().is_none() {
817 self.reset();
818 }
819 }
820 Key::Char(c) => {
821 value.push(c);
822 }
823 _ => {
824 }
826 },
827 }
828
829 if let Some(mode) = ctx.set_mode.take() {
831 self.mode = mode;
832 }
833
834 self.pending_change = ctx.pending_change.take();
836 if let Some(change) = ctx.change.take() {
837 self.last_change = Some(change);
838 }
839
840 ctx.e(Event::Redraw);
842 }
843}