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}