normal.rs

  1mod change;
  2mod delete;
  3
  4use crate::{motion::Motion, state::Operator, Vim};
  5use change::init as change_init;
  6use gpui::{actions, MutableAppContext};
  7
  8use self::{change::change_over, delete::delete_over};
  9
 10actions!(vim, [InsertLineAbove, InsertLineBelow, InsertAfter]);
 11
 12pub fn init(cx: &mut MutableAppContext) {
 13    change_init(cx);
 14}
 15
 16pub fn normal_motion(motion: Motion, cx: &mut MutableAppContext) {
 17    Vim::update(cx, |vim, cx| {
 18        match vim.state.operator_stack.pop() {
 19            None => move_cursor(vim, motion, cx),
 20            Some(Operator::Change) => change_over(vim, motion, cx),
 21            Some(Operator::Delete) => delete_over(vim, motion, cx),
 22            Some(Operator::Namespace(_)) => {
 23                // Can't do anything for a namespace operator. Ignoring
 24            }
 25        }
 26        vim.clear_operator(cx);
 27    });
 28}
 29
 30fn move_cursor(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
 31    vim.update_active_editor(cx, |editor, cx| {
 32        editor.move_cursors(cx, |map, cursor, goal| motion.move_point(map, cursor, goal))
 33    });
 34}
 35
 36#[cfg(test)]
 37mod test {
 38    use indoc::indoc;
 39    use util::test::marked_text;
 40
 41    use crate::{
 42        state::{
 43            Mode::{self, *},
 44            Namespace, Operator,
 45        },
 46        vim_test_context::VimTestContext,
 47    };
 48
 49    #[gpui::test]
 50    async fn test_h(cx: &mut gpui::TestAppContext) {
 51        let cx = VimTestContext::new(cx, true).await;
 52        let mut cx = cx.binding(["h"]);
 53        cx.assert("The q|uick", "The |quick");
 54        cx.assert("|The quick", "|The quick");
 55        cx.assert(
 56            indoc! {"
 57                The quick
 58                |brown"},
 59            indoc! {"
 60                The quick
 61                |brown"},
 62        );
 63    }
 64
 65    #[gpui::test]
 66    async fn test_l(cx: &mut gpui::TestAppContext) {
 67        let cx = VimTestContext::new(cx, true).await;
 68        let mut cx = cx.binding(["l"]);
 69        cx.assert("The q|uick", "The qu|ick");
 70        cx.assert("The quic|k", "The quic|k");
 71        cx.assert(
 72            indoc! {"
 73                The quic|k
 74                brown"},
 75            indoc! {"
 76                The quic|k
 77                brown"},
 78        );
 79    }
 80
 81    #[gpui::test]
 82    async fn test_j(cx: &mut gpui::TestAppContext) {
 83        let cx = VimTestContext::new(cx, true).await;
 84        let mut cx = cx.binding(["j"]);
 85        cx.assert(
 86            indoc! {"
 87                The |quick
 88                brown fox"},
 89            indoc! {"
 90                The quick
 91                brow|n fox"},
 92        );
 93        cx.assert(
 94            indoc! {"
 95                The quick
 96                brow|n fox"},
 97            indoc! {"
 98                The quick
 99                brow|n fox"},
100        );
101        cx.assert(
102            indoc! {"
103                The quic|k
104                brown"},
105            indoc! {"
106                The quick
107                brow|n"},
108        );
109        cx.assert(
110            indoc! {"
111                The quick
112                |brown"},
113            indoc! {"
114                The quick
115                |brown"},
116        );
117    }
118
119    #[gpui::test]
120    async fn test_k(cx: &mut gpui::TestAppContext) {
121        let cx = VimTestContext::new(cx, true).await;
122        let mut cx = cx.binding(["k"]);
123        cx.assert(
124            indoc! {"
125                The |quick
126                brown fox"},
127            indoc! {"
128                The |quick
129                brown fox"},
130        );
131        cx.assert(
132            indoc! {"
133                The quick
134                brow|n fox"},
135            indoc! {"
136                The |quick
137                brown fox"},
138        );
139        cx.assert(
140            indoc! {"
141                The
142                quic|k"},
143            indoc! {"
144                Th|e
145                quick"},
146        );
147    }
148
149    #[gpui::test]
150    async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
151        let cx = VimTestContext::new(cx, true).await;
152        let mut cx = cx.binding(["shift-$"]);
153        cx.assert("T|est test", "Test tes|t");
154        cx.assert("Test tes|t", "Test tes|t");
155        cx.assert(
156            indoc! {"
157                The |quick
158                brown"},
159            indoc! {"
160                The quic|k
161                brown"},
162        );
163        cx.assert(
164            indoc! {"
165                The quic|k
166                brown"},
167            indoc! {"
168                The quic|k
169                brown"},
170        );
171
172        let mut cx = cx.binding(["0"]);
173        cx.assert("Test |test", "|Test test");
174        cx.assert("|Test test", "|Test test");
175        cx.assert(
176            indoc! {"
177                The |quick
178                brown"},
179            indoc! {"
180                |The quick
181                brown"},
182        );
183        cx.assert(
184            indoc! {"
185                |The quick
186                brown"},
187            indoc! {"
188                |The quick
189                brown"},
190        );
191    }
192
193    #[gpui::test]
194    async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
195        let cx = VimTestContext::new(cx, true).await;
196        let mut cx = cx.binding(["shift-G"]);
197
198        cx.assert(
199            indoc! {"
200                The |quick
201                
202                brown fox jumps
203                over the lazy dog"},
204            indoc! {"
205                The quick
206                
207                brown fox jumps
208                over| the lazy dog"},
209        );
210        cx.assert(
211            indoc! {"
212                The quick
213                
214                brown fox jumps
215                over| the lazy dog"},
216            indoc! {"
217                The quick
218                
219                brown fox jumps
220                over| the lazy dog"},
221        );
222        cx.assert(
223            indoc! {"
224            The qui|ck
225            
226            brown"},
227            indoc! {"
228            The quick
229            
230            brow|n"},
231        );
232        cx.assert(
233            indoc! {"
234            The qui|ck
235            
236            "},
237            indoc! {"
238            The quick
239            
240            |"},
241        );
242    }
243
244    #[gpui::test]
245    async fn test_next_word_start(cx: &mut gpui::TestAppContext) {
246        let mut cx = VimTestContext::new(cx, true).await;
247        let (_, cursor_offsets) = marked_text(indoc! {"
248            The |quick|-|brown
249            |
250            |
251            |fox_jumps |over
252            |th||e"});
253        cx.set_state(
254            indoc! {"
255            |The quick-brown
256            
257            
258            fox_jumps over
259            the"},
260            Mode::Normal,
261        );
262
263        for cursor_offset in cursor_offsets {
264            cx.simulate_keystroke("w");
265            cx.assert_newest_selection_head_offset(cursor_offset);
266        }
267
268        // Reset and test ignoring punctuation
269        let (_, cursor_offsets) = marked_text(indoc! {"
270            The |quick-brown
271            |
272            |
273            |fox_jumps |over
274            |th||e"});
275        cx.set_state(
276            indoc! {"
277            |The quick-brown
278            
279            
280            fox_jumps over
281            the"},
282            Mode::Normal,
283        );
284
285        for cursor_offset in cursor_offsets {
286            cx.simulate_keystroke("shift-W");
287            cx.assert_newest_selection_head_offset(cursor_offset);
288        }
289    }
290
291    #[gpui::test]
292    async fn test_next_word_end(cx: &mut gpui::TestAppContext) {
293        let mut cx = VimTestContext::new(cx, true).await;
294        let (_, cursor_offsets) = marked_text(indoc! {"
295            Th|e quic|k|-brow|n
296            
297            
298            fox_jump|s ove|r
299            th|e"});
300        cx.set_state(
301            indoc! {"
302            |The quick-brown
303            
304            
305            fox_jumps over
306            the"},
307            Mode::Normal,
308        );
309
310        for cursor_offset in cursor_offsets {
311            cx.simulate_keystroke("e");
312            cx.assert_newest_selection_head_offset(cursor_offset);
313        }
314
315        // Reset and test ignoring punctuation
316        let (_, cursor_offsets) = marked_text(indoc! {"
317            Th|e quick-brow|n
318            
319            
320            fox_jump|s ove|r
321            th||e"});
322        cx.set_state(
323            indoc! {"
324            |The quick-brown
325            
326            
327            fox_jumps over
328            the"},
329            Mode::Normal,
330        );
331        for cursor_offset in cursor_offsets {
332            cx.simulate_keystroke("shift-E");
333            cx.assert_newest_selection_head_offset(cursor_offset);
334        }
335    }
336
337    #[gpui::test]
338    async fn test_previous_word_start(cx: &mut gpui::TestAppContext) {
339        let mut cx = VimTestContext::new(cx, true).await;
340        let (_, cursor_offsets) = marked_text(indoc! {"
341            ||The |quick|-|brown
342            |
343            |
344            |fox_jumps |over
345            |the"});
346        cx.set_state(
347            indoc! {"
348            The quick-brown
349            
350            
351            fox_jumps over
352            th|e"},
353            Mode::Normal,
354        );
355
356        for cursor_offset in cursor_offsets.into_iter().rev() {
357            cx.simulate_keystroke("b");
358            cx.assert_newest_selection_head_offset(cursor_offset);
359        }
360
361        // Reset and test ignoring punctuation
362        let (_, cursor_offsets) = marked_text(indoc! {"
363            ||The |quick-brown
364            |
365            |
366            |fox_jumps |over
367            |the"});
368        cx.set_state(
369            indoc! {"
370            The quick-brown
371            
372            
373            fox_jumps over
374            th|e"},
375            Mode::Normal,
376        );
377        for cursor_offset in cursor_offsets.into_iter().rev() {
378            cx.simulate_keystroke("shift-B");
379            cx.assert_newest_selection_head_offset(cursor_offset);
380        }
381    }
382
383    #[gpui::test]
384    async fn test_g_prefix_and_abort(cx: &mut gpui::TestAppContext) {
385        let mut cx = VimTestContext::new(cx, true).await;
386
387        // Can abort with escape to get back to normal mode
388        cx.simulate_keystroke("g");
389        assert_eq!(cx.mode(), Normal);
390        assert_eq!(
391            cx.active_operator(),
392            Some(Operator::Namespace(Namespace::G))
393        );
394        cx.simulate_keystroke("escape");
395        assert_eq!(cx.mode(), Normal);
396        assert_eq!(cx.active_operator(), None);
397    }
398
399    #[gpui::test]
400    async fn test_move_to_start(cx: &mut gpui::TestAppContext) {
401        let cx = VimTestContext::new(cx, true).await;
402        let mut cx = cx.binding(["g", "g"]);
403        cx.assert(
404            indoc! {"
405                The quick
406            
407                brown fox jumps
408                over |the lazy dog"},
409            indoc! {"
410                The q|uick
411            
412                brown fox jumps
413                over the lazy dog"},
414        );
415        cx.assert(
416            indoc! {"
417                The q|uick
418            
419                brown fox jumps
420                over the lazy dog"},
421            indoc! {"
422                The q|uick
423            
424                brown fox jumps
425                over the lazy dog"},
426        );
427        cx.assert(
428            indoc! {"
429                The quick
430            
431                brown fox jumps
432                over the la|zy dog"},
433            indoc! {"
434                The quic|k
435            
436                brown fox jumps
437                over the lazy dog"},
438        );
439        cx.assert(
440            indoc! {"
441                
442            
443                brown fox jumps
444                over the la|zy dog"},
445            indoc! {"
446                |
447            
448                brown fox jumps
449                over the lazy dog"},
450        );
451    }
452}