test.rs

  1mod neovim_backed_binding_test_context;
  2mod neovim_backed_test_context;
  3mod neovim_connection;
  4mod vim_test_context;
  5
  6use std::time::Duration;
  7
  8use command_palette::CommandPalette;
  9use editor::DisplayPoint;
 10use gpui::KeyBinding;
 11pub use neovim_backed_binding_test_context::*;
 12pub use neovim_backed_test_context::*;
 13pub use vim_test_context::*;
 14
 15use indoc::indoc;
 16use search::BufferSearchBar;
 17
 18use crate::{insert::NormalBefore, motion, state::Mode, ModeIndicator};
 19
 20#[gpui::test]
 21async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
 22    let mut cx = VimTestContext::new(cx, false).await;
 23    cx.simulate_keystrokes(["h", "j", "k", "l"]);
 24    cx.assert_editor_state("hjklˇ");
 25}
 26
 27#[gpui::test]
 28async fn test_neovim(cx: &mut gpui::TestAppContext) {
 29    let mut cx = NeovimBackedTestContext::new(cx).await;
 30
 31    cx.simulate_shared_keystroke("i").await;
 32    cx.assert_state_matches().await;
 33    cx.simulate_shared_keystrokes([
 34        "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w",
 35    ])
 36    .await;
 37    cx.assert_state_matches().await;
 38    cx.assert_editor_state("ˇtest");
 39}
 40
 41#[gpui::test]
 42async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
 43    let mut cx = VimTestContext::new(cx, true).await;
 44
 45    cx.simulate_keystroke("i");
 46    assert_eq!(cx.mode(), Mode::Insert);
 47
 48    // Editor acts as though vim is disabled
 49    cx.disable_vim();
 50    cx.simulate_keystrokes(["h", "j", "k", "l"]);
 51    cx.assert_editor_state("hjklˇ");
 52
 53    // Selections aren't changed if editor is blurred but vim-mode is still disabled.
 54    cx.set_state("«hjklˇ»", Mode::Normal);
 55    cx.assert_editor_state("«hjklˇ»");
 56    cx.update_editor(|_, cx| cx.blur());
 57    cx.assert_editor_state("«hjklˇ»");
 58    cx.update_editor(|_, cx| cx.focus_self());
 59    cx.assert_editor_state("«hjklˇ»");
 60
 61    // Enabling dynamically sets vim mode again and restores normal mode
 62    cx.enable_vim();
 63    assert_eq!(cx.mode(), Mode::Normal);
 64    cx.simulate_keystrokes(["h", "h", "h", "l"]);
 65    assert_eq!(cx.buffer_text(), "hjkl".to_owned());
 66    cx.assert_editor_state("hˇjkl");
 67    cx.simulate_keystrokes(["i", "T", "e", "s", "t"]);
 68    cx.assert_editor_state("hTestˇjkl");
 69
 70    // Disabling and enabling resets to normal mode
 71    assert_eq!(cx.mode(), Mode::Insert);
 72    cx.disable_vim();
 73    cx.enable_vim();
 74    assert_eq!(cx.mode(), Mode::Normal);
 75}
 76
 77#[gpui::test]
 78async fn test_cancel_selection(cx: &mut gpui::TestAppContext) {
 79    let mut cx = VimTestContext::new(cx, true).await;
 80
 81    cx.set_state(
 82        indoc! {"The quick brown fox juˇmps over the lazy dog"},
 83        Mode::Normal,
 84    );
 85    // jumps
 86    cx.simulate_keystrokes(["v", "l", "l"]);
 87    cx.assert_editor_state("The quick brown fox ju«mpsˇ» over the lazy dog");
 88
 89    cx.simulate_keystrokes(["escape"]);
 90    cx.assert_editor_state("The quick brown fox jumpˇs over the lazy dog");
 91
 92    // go back to the same selection state
 93    cx.simulate_keystrokes(["v", "h", "h"]);
 94    cx.assert_editor_state("The quick brown fox ju«ˇmps» over the lazy dog");
 95
 96    // Ctrl-[ should behave like Esc
 97    cx.simulate_keystrokes(["ctrl-["]);
 98    cx.assert_editor_state("The quick brown fox juˇmps over the lazy dog");
 99}
100
101#[gpui::test]
102async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
103    let mut cx = VimTestContext::new(cx, true).await;
104
105    cx.set_state(
106        indoc! {"
107            The quick brown
108            fox juˇmps over
109            the lazy dog"},
110        Mode::Normal,
111    );
112    cx.simulate_keystroke("/");
113
114    let search_bar = cx.workspace(|workspace, cx| {
115        workspace
116            .active_pane()
117            .read(cx)
118            .toolbar()
119            .read(cx)
120            .item_of_type::<BufferSearchBar>()
121            .expect("Buffer search bar should be deployed")
122    });
123
124    cx.update_view(search_bar, |bar, cx| {
125        assert_eq!(bar.query(cx), "");
126    })
127}
128
129#[gpui::test]
130async fn test_count_down(cx: &mut gpui::TestAppContext) {
131    let mut cx = VimTestContext::new(cx, true).await;
132
133    cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
134    cx.simulate_keystrokes(["2", "down"]);
135    cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
136    cx.simulate_keystrokes(["9", "down"]);
137    cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
138}
139
140#[gpui::test]
141async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
142    let mut cx = VimTestContext::new(cx, true).await;
143
144    // goes to end by default
145    cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
146    cx.simulate_keystrokes(["shift-g"]);
147    cx.assert_editor_state("aa\nbb\ncˇc");
148
149    // can go to line 1 (https://github.com/zed-industries/zed/issues/5812)
150    cx.simulate_keystrokes(["1", "shift-g"]);
151    cx.assert_editor_state("aˇa\nbb\ncc");
152}
153
154#[gpui::test]
155async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
156    let mut cx = VimTestContext::new(cx, true).await;
157
158    // works in normal mode
159    cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
160    cx.simulate_keystrokes([">", ">"]);
161    cx.assert_editor_state("aa\n    bˇb\ncc");
162    cx.simulate_keystrokes(["<", "<"]);
163    cx.assert_editor_state("aa\nbˇb\ncc");
164
165    // works in visuial mode
166    cx.simulate_keystrokes(["shift-v", "down", ">"]);
167    cx.assert_editor_state("aa\n    b«b\n    ccˇ»");
168}
169
170#[gpui::test]
171async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
172    let mut cx = VimTestContext::new(cx, true).await;
173
174    cx.set_state("aˇbc\n", Mode::Normal);
175    cx.simulate_keystrokes(["i", "cmd-shift-p"]);
176
177    assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
178    cx.simulate_keystroke("escape");
179    cx.run_until_parked();
180    assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
181    cx.assert_state("aˇbc\n", Mode::Insert);
182}
183
184#[gpui::test]
185async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
186    let mut cx = VimTestContext::new(cx, true).await;
187
188    cx.set_state("aˇbˇc", Mode::Normal);
189    cx.simulate_keystrokes(["escape"]);
190
191    cx.assert_state("aˇbc", Mode::Normal);
192}
193
194#[gpui::test]
195async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
196    let mut cx = VimTestContext::new(cx, true).await;
197
198    cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
199    cx.simulate_keystrokes(["/", "c", "c"]);
200
201    let search_bar = cx.workspace(|workspace, cx| {
202        workspace
203            .active_pane()
204            .read(cx)
205            .toolbar()
206            .read(cx)
207            .item_of_type::<BufferSearchBar>()
208            .expect("Buffer search bar should be deployed")
209    });
210
211    cx.update_view(search_bar, |bar, cx| {
212        assert_eq!(bar.query(cx), "cc");
213    });
214
215    cx.update_editor(|editor, cx| {
216        let highlights = editor.all_text_background_highlights(cx);
217        assert_eq!(3, highlights.len());
218        assert_eq!(
219            DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
220            highlights[0].0
221        )
222    });
223    cx.simulate_keystrokes(["enter"]);
224
225    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
226    cx.simulate_keystrokes(["n"]);
227    cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
228    cx.simulate_keystrokes(["shift-n"]);
229    cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
230}
231
232#[gpui::test]
233async fn test_status_indicator(cx: &mut gpui::TestAppContext) {
234    let mut cx = VimTestContext::new(cx, true).await;
235
236    let mode_indicator = cx.workspace(|workspace, cx| {
237        let status_bar = workspace.status_bar().read(cx);
238        let mode_indicator = status_bar.item_of_type::<ModeIndicator>();
239        assert!(mode_indicator.is_some());
240        mode_indicator.unwrap()
241    });
242
243    assert_eq!(
244        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
245        Some(Mode::Normal)
246    );
247
248    // shows the correct mode
249    cx.simulate_keystrokes(["i"]);
250    assert_eq!(
251        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
252        Some(Mode::Insert)
253    );
254
255    // shows even in search
256    cx.simulate_keystrokes(["escape", "v", "/"]);
257    assert_eq!(
258        cx.workspace(|_, cx| mode_indicator.read(cx).mode),
259        Some(Mode::Visual)
260    );
261
262    // hides if vim mode is disabled
263    cx.disable_vim();
264    cx.run_until_parked();
265    cx.workspace(|workspace, cx| {
266        let status_bar = workspace.status_bar().read(cx);
267        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
268        assert!(mode_indicator.read(cx).mode.is_none());
269    });
270
271    cx.enable_vim();
272    cx.run_until_parked();
273    cx.workspace(|workspace, cx| {
274        let status_bar = workspace.status_bar().read(cx);
275        let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
276        assert!(mode_indicator.read(cx).mode.is_some());
277    });
278}
279
280#[gpui::test]
281async fn test_word_characters(cx: &mut gpui::TestAppContext) {
282    let mut cx = VimTestContext::new_typescript(cx).await;
283    cx.set_state(
284        indoc! { "
285        class A {
286            #ˇgoop = 99;
287            $ˇgoop () { return this.#gˇoop };
288        };
289        console.log(new A().$gooˇp())
290    "},
291        Mode::Normal,
292    );
293    cx.simulate_keystrokes(["v", "i", "w"]);
294    cx.assert_state(
295        indoc! {"
296        class A {
297            «#goopˇ» = 99;
298            «$goopˇ» () { return this.«#goopˇ» };
299        };
300        console.log(new A().«$goopˇ»())
301    "},
302        Mode::Visual,
303    )
304}
305
306#[gpui::test]
307async fn test_join_lines(cx: &mut gpui::TestAppContext) {
308    let mut cx = NeovimBackedTestContext::new(cx).await;
309
310    cx.set_shared_state(indoc! {"
311      ˇone
312      two
313      three
314      four
315      five
316      six
317      "})
318        .await;
319    cx.simulate_shared_keystrokes(["shift-j"]).await;
320    cx.assert_shared_state(indoc! {"
321          oneˇ two
322          three
323          four
324          five
325          six
326          "})
327        .await;
328    cx.simulate_shared_keystrokes(["3", "shift-j"]).await;
329    cx.assert_shared_state(indoc! {"
330          one two threeˇ four
331          five
332          six
333          "})
334        .await;
335
336    cx.set_shared_state(indoc! {"
337      ˇone
338      two
339      three
340      four
341      five
342      six
343      "})
344        .await;
345    cx.simulate_shared_keystrokes(["j", "v", "3", "j", "shift-j"])
346        .await;
347    cx.assert_shared_state(indoc! {"
348      one
349      two three fourˇ five
350      six
351      "})
352        .await;
353}
354
355#[gpui::test]
356async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
357    let mut cx = NeovimBackedTestContext::new(cx).await;
358
359    cx.set_shared_wrap(12).await;
360    // tests line wrap as follows:
361    //  1: twelve char
362    //     twelve char
363    //  2: twelve char
364    cx.set_shared_state(indoc! { "
365        tˇwelve char twelve char
366        twelve char
367    "})
368        .await;
369    cx.simulate_shared_keystrokes(["j"]).await;
370    cx.assert_shared_state(indoc! { "
371        twelve char twelve char
372        tˇwelve char
373    "})
374        .await;
375    cx.simulate_shared_keystrokes(["k"]).await;
376    cx.assert_shared_state(indoc! { "
377        tˇwelve char twelve char
378        twelve char
379    "})
380        .await;
381    cx.simulate_shared_keystrokes(["g", "j"]).await;
382    cx.assert_shared_state(indoc! { "
383        twelve char tˇwelve char
384        twelve char
385    "})
386        .await;
387    cx.simulate_shared_keystrokes(["g", "j"]).await;
388    cx.assert_shared_state(indoc! { "
389        twelve char twelve char
390        tˇwelve char
391    "})
392        .await;
393
394    cx.simulate_shared_keystrokes(["g", "k"]).await;
395    cx.assert_shared_state(indoc! { "
396        twelve char tˇwelve char
397        twelve char
398    "})
399        .await;
400
401    cx.simulate_shared_keystrokes(["g", "^"]).await;
402    cx.assert_shared_state(indoc! { "
403        twelve char ˇtwelve char
404        twelve char
405    "})
406        .await;
407
408    cx.simulate_shared_keystrokes(["^"]).await;
409    cx.assert_shared_state(indoc! { "
410        ˇtwelve char twelve char
411        twelve char
412    "})
413        .await;
414
415    cx.simulate_shared_keystrokes(["g", "$"]).await;
416    cx.assert_shared_state(indoc! { "
417        twelve charˇ twelve char
418        twelve char
419    "})
420        .await;
421    cx.simulate_shared_keystrokes(["$"]).await;
422    cx.assert_shared_state(indoc! { "
423        twelve char twelve chaˇr
424        twelve char
425    "})
426        .await;
427
428    cx.set_shared_state(indoc! { "
429        tˇwelve char twelve char
430        twelve char
431    "})
432        .await;
433    cx.simulate_shared_keystrokes(["enter"]).await;
434    cx.assert_shared_state(indoc! { "
435            twelve char twelve char
436            ˇtwelve char
437        "})
438        .await;
439
440    cx.set_shared_state(indoc! { "
441        twelve char
442        tˇwelve char twelve char
443        twelve char
444    "})
445        .await;
446    cx.simulate_shared_keystrokes(["o", "o", "escape"]).await;
447    cx.assert_shared_state(indoc! { "
448        twelve char
449        twelve char twelve char
450        ˇo
451        twelve char
452    "})
453        .await;
454
455    cx.set_shared_state(indoc! { "
456        twelve char
457        tˇwelve char twelve char
458        twelve char
459    "})
460        .await;
461    cx.simulate_shared_keystrokes(["shift-a", "a", "escape"])
462        .await;
463    cx.assert_shared_state(indoc! { "
464        twelve char
465        twelve char twelve charˇa
466        twelve char
467    "})
468        .await;
469    cx.simulate_shared_keystrokes(["shift-i", "i", "escape"])
470        .await;
471    cx.assert_shared_state(indoc! { "
472        twelve char
473        ˇitwelve char twelve chara
474        twelve char
475    "})
476        .await;
477    cx.simulate_shared_keystrokes(["shift-d"]).await;
478    cx.assert_shared_state(indoc! { "
479        twelve char
480        ˇ
481        twelve char
482    "})
483        .await;
484
485    cx.set_shared_state(indoc! { "
486        twelve char
487        twelve char tˇwelve char
488        twelve char
489    "})
490        .await;
491    cx.simulate_shared_keystrokes(["shift-o", "o", "escape"])
492        .await;
493    cx.assert_shared_state(indoc! { "
494        twelve char
495        ˇo
496        twelve char twelve char
497        twelve char
498    "})
499        .await;
500
501    // line wraps as:
502    // fourteen ch
503    // ar
504    // fourteen ch
505    // ar
506    cx.set_shared_state(indoc! { "
507        fourteen chaˇr
508        fourteen char
509    "})
510        .await;
511
512    cx.simulate_shared_keystrokes(["d", "i", "w"]).await;
513    cx.assert_shared_state(indoc! {"
514        fourteenˇ•
515        fourteen char
516    "})
517        .await;
518    cx.simulate_shared_keystrokes(["j", "shift-f", "e", "f", "r"])
519        .await;
520    cx.assert_shared_state(indoc! {"
521        fourteen•
522        fourteen chaˇr
523    "})
524        .await;
525}
526
527#[gpui::test]
528async fn test_folds(cx: &mut gpui::TestAppContext) {
529    let mut cx = NeovimBackedTestContext::new(cx).await;
530    cx.set_neovim_option("foldmethod=manual").await;
531
532    cx.set_shared_state(indoc! { "
533        fn boop() {
534          ˇbarp()
535          bazp()
536        }
537    "})
538        .await;
539    cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
540        .await;
541
542    // visual display is now:
543    // fn boop () {
544    //  [FOLDED]
545    // }
546
547    // TODO: this should not be needed but currently zf does not
548    // return to normal mode.
549    cx.simulate_shared_keystrokes(["escape"]).await;
550
551    // skip over fold downward
552    cx.simulate_shared_keystrokes(["g", "g"]).await;
553    cx.assert_shared_state(indoc! { "
554        ˇfn boop() {
555          barp()
556          bazp()
557        }
558    "})
559        .await;
560
561    cx.simulate_shared_keystrokes(["j", "j"]).await;
562    cx.assert_shared_state(indoc! { "
563        fn boop() {
564          barp()
565          bazp()
566        ˇ}
567    "})
568        .await;
569
570    // skip over fold upward
571    cx.simulate_shared_keystrokes(["2", "k"]).await;
572    cx.assert_shared_state(indoc! { "
573        ˇfn boop() {
574          barp()
575          bazp()
576        }
577    "})
578        .await;
579
580    // yank the fold
581    cx.simulate_shared_keystrokes(["down", "y", "y"]).await;
582    cx.assert_shared_clipboard("  barp()\n  bazp()\n").await;
583
584    // re-open
585    cx.simulate_shared_keystrokes(["z", "o"]).await;
586    cx.assert_shared_state(indoc! { "
587        fn boop() {
588        ˇ  barp()
589          bazp()
590        }
591    "})
592        .await;
593}
594
595#[gpui::test]
596async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
597    let mut cx = NeovimBackedTestContext::new(cx).await;
598    cx.set_neovim_option("foldmethod=manual").await;
599
600    cx.set_shared_state(indoc! { "
601        fn boop() {
602          ˇbarp()
603          bazp()
604        }
605    "})
606        .await;
607    cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
608        .await;
609    cx.simulate_shared_keystrokes(["escape"]).await;
610    cx.simulate_shared_keystrokes(["g", "g"]).await;
611    cx.simulate_shared_keystrokes(["5", "d", "j"]).await;
612    cx.assert_shared_state(indoc! { "ˇ"}).await;
613}
614
615#[gpui::test]
616async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
617    let mut cx = NeovimBackedTestContext::new(cx).await;
618
619    cx.set_shared_state(indoc! {"
620        The quick brown
621        fox juˇmps over
622        the lazy dog"})
623        .await;
624
625    cx.simulate_shared_keystrokes(["4", "escape", "3", "d", "l"])
626        .await;
627    cx.assert_shared_state(indoc! {"
628        The quick brown
629        fox juˇ over
630        the lazy dog"})
631        .await;
632}
633
634#[gpui::test]
635async fn test_zero(cx: &mut gpui::TestAppContext) {
636    let mut cx = NeovimBackedTestContext::new(cx).await;
637
638    cx.set_shared_state(indoc! {"
639        The quˇick brown
640        fox jumps over
641        the lazy dog"})
642        .await;
643
644    cx.simulate_shared_keystrokes(["0"]).await;
645    cx.assert_shared_state(indoc! {"
646        ˇThe quick brown
647        fox jumps over
648        the lazy dog"})
649        .await;
650
651    cx.simulate_shared_keystrokes(["1", "0", "l"]).await;
652    cx.assert_shared_state(indoc! {"
653        The quick ˇbrown
654        fox jumps over
655        the lazy dog"})
656        .await;
657}
658
659#[gpui::test]
660async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
661    let mut cx = NeovimBackedTestContext::new(cx).await;
662
663    cx.set_shared_state(indoc! {"
664        ;;ˇ;
665        Lorem Ipsum"})
666        .await;
667
668    cx.simulate_shared_keystrokes(["a", "down", "up", ";", "down", "up"])
669        .await;
670    cx.assert_shared_state(indoc! {"
671        ;;;;ˇ
672        Lorem Ipsum"})
673        .await;
674}
675
676#[gpui::test]
677async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
678    let mut cx = NeovimBackedTestContext::new(cx).await;
679
680    cx.set_shared_wrap(12).await;
681
682    cx.set_shared_state(indoc! {"
683                aaˇaa
684                😃😃"
685    })
686    .await;
687    cx.simulate_shared_keystrokes(["j"]).await;
688    cx.assert_shared_state(indoc! {"
689                aaaa
690                😃ˇ😃"
691    })
692    .await;
693
694    cx.set_shared_state(indoc! {"
695                123456789012aaˇaa
696                123456789012😃😃"
697    })
698    .await;
699    cx.simulate_shared_keystrokes(["j"]).await;
700    cx.assert_shared_state(indoc! {"
701        123456789012aaaa
702        123456789012😃ˇ😃"
703    })
704    .await;
705
706    cx.set_shared_state(indoc! {"
707                123456789012aaˇaa
708                123456789012😃😃"
709    })
710    .await;
711    cx.simulate_shared_keystrokes(["j"]).await;
712    cx.assert_shared_state(indoc! {"
713        123456789012aaaa
714        123456789012😃ˇ😃"
715    })
716    .await;
717
718    cx.set_shared_state(indoc! {"
719        123456789012aaaaˇaaaaaaaa123456789012
720        wow
721        123456789012😃😃😃😃😃😃123456789012"
722    })
723    .await;
724    cx.simulate_shared_keystrokes(["j", "j"]).await;
725    cx.assert_shared_state(indoc! {"
726        123456789012aaaaaaaaaaaa123456789012
727        wow
728        123456789012😃😃ˇ😃😃😃😃123456789012"
729    })
730    .await;
731}
732
733#[gpui::test]
734async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
735    let mut cx = NeovimBackedTestContext::new(cx).await;
736
737    cx.set_shared_state(indoc! {"
738        one
739        ˇ
740        two"})
741        .await;
742
743    cx.simulate_shared_keystrokes(["}", "}"]).await;
744    cx.assert_shared_state(indoc! {"
745        one
746
747        twˇo"})
748        .await;
749
750    cx.simulate_shared_keystrokes(["{", "{", "{"]).await;
751    cx.assert_shared_state(indoc! {"
752        ˇone
753
754        two"})
755        .await;
756}
757
758#[gpui::test]
759async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
760    let mut cx = VimTestContext::new(cx, true).await;
761
762    cx.set_state(
763        indoc! {"
764        defmodule Test do
765            def test(a, ˇ[_, _] = b), do: IO.puts('hi')
766        end
767    "},
768        Mode::Normal,
769    );
770    cx.simulate_keystrokes(["g", "a"]);
771    cx.assert_state(
772        indoc! {"
773        defmodule Test do
774            def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
775        end
776    "},
777        Mode::Visual,
778    );
779}
780
781#[gpui::test]
782async fn test_jk(cx: &mut gpui::TestAppContext) {
783    let mut cx = NeovimBackedTestContext::new(cx).await;
784
785    cx.update(|cx| {
786        cx.bind_keys([KeyBinding::new(
787            "j k",
788            NormalBefore,
789            Some("vim_mode == insert"),
790        )])
791    });
792    cx.neovim.exec("imap jk <esc>").await;
793
794    cx.set_shared_state("ˇhello").await;
795    cx.simulate_shared_keystrokes(["i", "j", "o", "j", "k"])
796        .await;
797    cx.assert_shared_state("jˇohello").await;
798}
799
800#[gpui::test]
801async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
802    let mut cx = VimTestContext::new(cx, true).await;
803
804    cx.update(|cx| {
805        cx.bind_keys([KeyBinding::new(
806            "j k",
807            NormalBefore,
808            Some("vim_mode == insert"),
809        )])
810    });
811
812    cx.set_state("ˇhello", Mode::Normal);
813    cx.simulate_keystrokes(["i", "j"]);
814    cx.executor().advance_clock(Duration::from_millis(500));
815    cx.run_until_parked();
816    cx.assert_state("ˇhello", Mode::Insert);
817    cx.executor().advance_clock(Duration::from_millis(500));
818    cx.run_until_parked();
819    cx.assert_state("jˇhello", Mode::Insert);
820    cx.simulate_keystrokes(["k", "j", "k"]);
821    cx.assert_state("jˇkhello", Mode::Normal);
822}
823
824#[gpui::test]
825async fn test_comma_w(cx: &mut gpui::TestAppContext) {
826    let mut cx = NeovimBackedTestContext::new(cx).await;
827
828    cx.update(|cx| {
829        cx.bind_keys([KeyBinding::new(
830            ", w",
831            motion::Down {
832                display_lines: false,
833            },
834            Some("vim_mode == normal"),
835        )])
836    });
837    cx.neovim.exec("map ,w j").await;
838
839    cx.set_shared_state("ˇhello hello\nhello hello").await;
840    cx.simulate_shared_keystrokes(["f", "o", ";", ",", "w"])
841        .await;
842    cx.assert_shared_state("hello hello\nhello hellˇo").await;
843
844    cx.set_shared_state("ˇhello hello\nhello hello").await;
845    cx.simulate_shared_keystrokes(["f", "o", ";", ",", "i"])
846        .await;
847    cx.assert_shared_state("hellˇo hello\nhello hello").await;
848    cx.assert_shared_mode(Mode::Insert).await;
849}