1mod command;
2mod motion;
3mod parser;
4mod vi_keybindings;
5
6use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
7pub use vi_keybindings::{default_vi_insert_keybindings, default_vi_normal_keybindings};
8
9use self::motion::ViCharSearch;
10
11use super::EditMode;
12use crate::{
13 edit_mode::{keybindings::Keybindings, vi::parser::parse},
14 enums::{EditCommand, ReedlineEvent, ReedlineRawEvent},
15 PromptEditMode, PromptViMode,
16};
17
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19enum ViMode {
20 Normal,
21 Insert,
22}
23
24pub struct Vi {
26 cache: Vec<char>,
27 insert_keybindings: Keybindings,
28 normal_keybindings: Keybindings,
29 mode: ViMode,
30 previous: Option<ReedlineEvent>,
31 last_char_search: Option<ViCharSearch>,
33}
34
35impl Default for Vi {
36 fn default() -> Self {
37 Vi {
38 insert_keybindings: default_vi_insert_keybindings(),
39 normal_keybindings: default_vi_normal_keybindings(),
40 cache: Vec::new(),
41 mode: ViMode::Insert,
42 previous: None,
43 last_char_search: None,
44 }
45 }
46}
47
48impl Vi {
49 pub fn new(insert_keybindings: Keybindings, normal_keybindings: Keybindings) -> Self {
51 Self {
52 insert_keybindings,
53 normal_keybindings,
54 ..Default::default()
55 }
56 }
57}
58
59impl EditMode for Vi {
60 fn parse_event(&mut self, event: ReedlineRawEvent) -> ReedlineEvent {
61 match event.into() {
62 Event::Key(KeyEvent {
63 code, modifiers, ..
64 }) => match (self.mode, modifiers, code) {
65 (ViMode::Normal, modifier, KeyCode::Char(c)) => {
66 let c = c.to_ascii_lowercase();
67
68 if let Some(event) = self
69 .normal_keybindings
70 .find_binding(modifiers, KeyCode::Char(c))
71 {
72 event
73 } else if modifier == KeyModifiers::NONE || modifier == KeyModifiers::SHIFT {
74 self.cache.push(if modifier == KeyModifiers::SHIFT {
75 c.to_ascii_uppercase()
76 } else {
77 c
78 });
79
80 let res = parse(&mut self.cache.iter().peekable());
81
82 if !res.is_valid() {
83 self.cache.clear();
84 ReedlineEvent::None
85 } else if res.is_complete() {
86 if res.enters_insert_mode() {
87 self.mode = ViMode::Insert;
88 }
89
90 let event = res.to_reedline_event(self);
91 self.cache.clear();
92 event
93 } else {
94 ReedlineEvent::None
95 }
96 } else {
97 ReedlineEvent::None
98 }
99 }
100 (ViMode::Insert, modifier, KeyCode::Char(c)) => {
101 let c = match modifier {
110 KeyModifiers::NONE => c,
111 _ => c.to_ascii_lowercase(),
112 };
113
114 self.insert_keybindings
115 .find_binding(modifier, KeyCode::Char(c))
116 .unwrap_or_else(|| {
117 if modifier == KeyModifiers::NONE
118 || modifier == KeyModifiers::SHIFT
119 || modifier == KeyModifiers::CONTROL | KeyModifiers::ALT
120 || modifier
121 == KeyModifiers::CONTROL
122 | KeyModifiers::ALT
123 | KeyModifiers::SHIFT
124 {
125 ReedlineEvent::Edit(vec![EditCommand::InsertChar(
126 if modifier == KeyModifiers::SHIFT {
127 c.to_ascii_uppercase()
128 } else {
129 c
130 },
131 )])
132 } else {
133 ReedlineEvent::None
134 }
135 })
136 }
137 (_, KeyModifiers::NONE, KeyCode::Esc) => {
138 self.cache.clear();
139 self.mode = ViMode::Normal;
140 ReedlineEvent::Multiple(vec![ReedlineEvent::Esc, ReedlineEvent::Repaint])
141 }
142 (_, KeyModifiers::NONE, KeyCode::Enter) => {
143 self.mode = ViMode::Insert;
144 ReedlineEvent::Enter
145 }
146 (ViMode::Normal, _, _) => self
147 .normal_keybindings
148 .find_binding(modifiers, code)
149 .unwrap_or(ReedlineEvent::None),
150 (ViMode::Insert, _, _) => self
151 .insert_keybindings
152 .find_binding(modifiers, code)
153 .unwrap_or(ReedlineEvent::None),
154 },
155
156 Event::Mouse(_) => ReedlineEvent::Mouse,
157 Event::Resize(width, height) => ReedlineEvent::Resize(width, height),
158 Event::FocusGained => ReedlineEvent::None,
159 Event::FocusLost => ReedlineEvent::None,
160 Event::Paste(body) => ReedlineEvent::Edit(vec![EditCommand::InsertString(
161 body.replace("\r\n", "\n").replace('\r', "\n"),
162 )]),
163 }
164 }
165
166 fn edit_mode(&self) -> PromptEditMode {
167 match self.mode {
168 ViMode::Normal => PromptEditMode::Vi(PromptViMode::Normal),
169 ViMode::Insert => PromptEditMode::Vi(PromptViMode::Insert),
170 }
171 }
172}
173
174#[cfg(test)]
175mod test {
176 use super::*;
177 use pretty_assertions::assert_eq;
178
179 #[test]
180 fn esc_leads_to_normal_mode_test() {
181 let mut vi = Vi::default();
182 let esc = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
183 KeyCode::Esc,
184 KeyModifiers::NONE,
185 )))
186 .unwrap();
187 let result = vi.parse_event(esc);
188
189 assert_eq!(
190 result,
191 ReedlineEvent::Multiple(vec![ReedlineEvent::Esc, ReedlineEvent::Repaint])
192 );
193 assert!(matches!(vi.mode, ViMode::Normal));
194 }
195
196 #[test]
197 fn keybinding_without_modifier_test() {
198 let mut keybindings = default_vi_normal_keybindings();
199 keybindings.add_binding(
200 KeyModifiers::NONE,
201 KeyCode::Char('e'),
202 ReedlineEvent::ClearScreen,
203 );
204
205 let mut vi = Vi {
206 insert_keybindings: default_vi_insert_keybindings(),
207 normal_keybindings: keybindings,
208 mode: ViMode::Normal,
209 ..Default::default()
210 };
211
212 let esc = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
213 KeyCode::Char('e'),
214 KeyModifiers::NONE,
215 )))
216 .unwrap();
217 let result = vi.parse_event(esc);
218
219 assert_eq!(result, ReedlineEvent::ClearScreen);
220 }
221
222 #[test]
223 fn keybinding_with_shift_modifier_test() {
224 let mut keybindings = default_vi_normal_keybindings();
225 keybindings.add_binding(
226 KeyModifiers::SHIFT,
227 KeyCode::Char('$'),
228 ReedlineEvent::CtrlD,
229 );
230
231 let mut vi = Vi {
232 insert_keybindings: default_vi_insert_keybindings(),
233 normal_keybindings: keybindings,
234 mode: ViMode::Normal,
235 ..Default::default()
236 };
237
238 let esc = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
239 KeyCode::Char('$'),
240 KeyModifiers::SHIFT,
241 )))
242 .unwrap();
243 let result = vi.parse_event(esc);
244
245 assert_eq!(result, ReedlineEvent::CtrlD);
246 }
247
248 #[test]
249 fn non_register_modifier_test() {
250 let keybindings = default_vi_normal_keybindings();
251 let mut vi = Vi {
252 insert_keybindings: default_vi_insert_keybindings(),
253 normal_keybindings: keybindings,
254 mode: ViMode::Normal,
255 ..Default::default()
256 };
257
258 let esc = ReedlineRawEvent::convert_from(Event::Key(KeyEvent::new(
259 KeyCode::Char('q'),
260 KeyModifiers::NONE,
261 )))
262 .unwrap();
263 let result = vi.parse_event(esc);
264
265 assert_eq!(result, ReedlineEvent::None);
266 }
267}