1mod neovim_backed_test_context;
2mod neovim_connection;
3mod vim_test_context;
4
5use std::time::Duration;
6
7use collections::HashMap;
8use command_palette::CommandPalette;
9use editor::{actions::DeleteLine, display_map::DisplayRow, DisplayPoint};
10use futures::StreamExt;
11use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext};
12pub use neovim_backed_test_context::*;
13use settings::SettingsStore;
14pub use vim_test_context::*;
15
16use indoc::indoc;
17use search::BufferSearchBar;
18use workspace::WorkspaceSettings;
19
20use crate::{insert::NormalBefore, motion, state::Mode};
21
22#[gpui::test]
23async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
24 let mut cx = VimTestContext::new(cx, false).await;
25 cx.simulate_keystrokes("h j k l");
26 cx.assert_editor_state("hjklˇ");
27}
28
29#[gpui::test]
30async fn test_neovim(cx: &mut gpui::TestAppContext) {
31 let mut cx = NeovimBackedTestContext::new(cx).await;
32
33 cx.simulate_shared_keystrokes("i").await;
34 cx.shared_state().await.assert_matches();
35 cx.simulate_shared_keystrokes("shift-t e s t space t e s t escape 0 d w")
36 .await;
37 cx.shared_state().await.assert_matches();
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_keystrokes("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.cx.set_state("«hjklˇ»");
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_keystrokes("/");
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_end_of_line_with_times(cx: &mut gpui::TestAppContext) {
156 let mut cx = VimTestContext::new(cx, true).await;
157
158 // goes to current line end
159 cx.set_state(indoc! {"ˇaa\nbb\ncc"}, Mode::Normal);
160 cx.simulate_keystrokes("$");
161 cx.assert_editor_state("aˇa\nbb\ncc");
162
163 // goes to next line end
164 cx.simulate_keystrokes("2 $");
165 cx.assert_editor_state("aa\nbˇb\ncc");
166
167 // try to exceed the final line.
168 cx.simulate_keystrokes("4 $");
169 cx.assert_editor_state("aa\nbb\ncˇc");
170}
171
172#[gpui::test]
173async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
174 let mut cx = VimTestContext::new(cx, true).await;
175
176 // works in normal mode
177 cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
178 cx.simulate_keystrokes("> >");
179 cx.assert_editor_state("aa\n bˇb\ncc");
180 cx.simulate_keystrokes("< <");
181 cx.assert_editor_state("aa\nbˇb\ncc");
182
183 // works in visual mode
184 cx.simulate_keystrokes("shift-v down >");
185 cx.assert_editor_state("aa\n bˇb\n cc");
186
187 // works as operator
188 cx.set_state("aa\nbˇb\ncc\n", Mode::Normal);
189 cx.simulate_keystrokes("> j");
190 cx.assert_editor_state("aa\n bˇb\n cc\n");
191 cx.simulate_keystrokes("< k");
192 cx.assert_editor_state("aa\nbˇb\n cc\n");
193 cx.simulate_keystrokes("> i p");
194 cx.assert_editor_state(" aa\n bˇb\n cc\n");
195 cx.simulate_keystrokes("< i p");
196 cx.assert_editor_state("aa\nbˇb\n cc\n");
197 cx.simulate_keystrokes("< i p");
198 cx.assert_editor_state("aa\nbˇb\ncc\n");
199
200 cx.set_state("ˇaa\nbb\ncc\n", Mode::Normal);
201 cx.simulate_keystrokes("> 2 j");
202 cx.assert_editor_state(" ˇaa\n bb\n cc\n");
203
204 cx.set_state("aa\nbb\nˇcc\n", Mode::Normal);
205 cx.simulate_keystrokes("> 2 k");
206 cx.assert_editor_state(" aa\n bb\n ˇcc\n");
207
208 // works with repeat
209 cx.set_state("a\nb\nccˇc\n", Mode::Normal);
210 cx.simulate_keystrokes("> 2 k");
211 cx.assert_editor_state(" a\n b\n ccˇc\n");
212 cx.simulate_keystrokes(".");
213 cx.assert_editor_state(" a\n b\n ccˇc\n");
214 cx.simulate_keystrokes("v k <");
215 cx.assert_editor_state(" a\n bˇ\n ccc\n");
216 cx.simulate_keystrokes(".");
217 cx.assert_editor_state(" a\nbˇ\nccc\n");
218}
219
220#[gpui::test]
221async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
222 let mut cx = VimTestContext::new(cx, true).await;
223
224 cx.set_state("aˇbc\n", Mode::Normal);
225 cx.simulate_keystrokes("i cmd-shift-p");
226
227 assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
228 cx.simulate_keystrokes("escape");
229 cx.run_until_parked();
230 assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
231 cx.assert_state("aˇbc\n", Mode::Insert);
232}
233
234#[gpui::test]
235async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
236 let mut cx = VimTestContext::new(cx, true).await;
237
238 cx.set_state("aˇbˇc", Mode::Normal);
239 cx.simulate_keystrokes("escape");
240
241 cx.assert_state("aˇbc", Mode::Normal);
242}
243
244#[gpui::test]
245async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
246 let mut cx = VimTestContext::new(cx, true).await;
247
248 cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
249 cx.simulate_keystrokes("/ c c");
250
251 let search_bar = cx.workspace(|workspace, cx| {
252 workspace
253 .active_pane()
254 .read(cx)
255 .toolbar()
256 .read(cx)
257 .item_of_type::<BufferSearchBar>()
258 .expect("Buffer search bar should be deployed")
259 });
260
261 cx.update_view(search_bar, |bar, cx| {
262 assert_eq!(bar.query(cx), "cc");
263 });
264
265 cx.update_editor(|editor, cx| {
266 let highlights = editor.all_text_background_highlights(cx);
267 assert_eq!(3, highlights.len());
268 assert_eq!(
269 DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 2),
270 highlights[0].0
271 )
272 });
273 cx.simulate_keystrokes("enter");
274
275 cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
276 cx.simulate_keystrokes("n");
277 cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
278 cx.simulate_keystrokes("shift-n");
279 cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
280}
281
282#[gpui::test]
283async fn test_word_characters(cx: &mut gpui::TestAppContext) {
284 let mut cx = VimTestContext::new_typescript(cx).await;
285 cx.set_state(
286 indoc! { "
287 class A {
288 #ˇgoop = 99;
289 $ˇgoop () { return this.#gˇoop };
290 };
291 console.log(new A().$gooˇp())
292 "},
293 Mode::Normal,
294 );
295 cx.simulate_keystrokes("v i w");
296 cx.assert_state(
297 indoc! {"
298 class A {
299 «#goopˇ» = 99;
300 «$goopˇ» () { return this.«#goopˇ» };
301 };
302 console.log(new A().«$goopˇ»())
303 "},
304 Mode::Visual,
305 )
306}
307
308#[gpui::test]
309async fn test_kebab_case(cx: &mut gpui::TestAppContext) {
310 let mut cx = VimTestContext::new_html(cx).await;
311 cx.set_state(
312 indoc! { r#"
313 <div><a class="bg-rˇed"></a></div>
314 "#},
315 Mode::Normal,
316 );
317 cx.simulate_keystrokes("v i w");
318 cx.assert_state(
319 indoc! { r#"
320 <div><a class="bg-«redˇ»"></a></div>
321 "#
322 },
323 Mode::Visual,
324 )
325}
326
327#[gpui::test]
328async fn test_join_lines(cx: &mut gpui::TestAppContext) {
329 let mut cx = NeovimBackedTestContext::new(cx).await;
330
331 cx.set_shared_state(indoc! {"
332 ˇone
333 two
334 three
335 four
336 five
337 six
338 "})
339 .await;
340 cx.simulate_shared_keystrokes("shift-j").await;
341 cx.shared_state().await.assert_eq(indoc! {"
342 oneˇ two
343 three
344 four
345 five
346 six
347 "});
348 cx.simulate_shared_keystrokes("3 shift-j").await;
349 cx.shared_state().await.assert_eq(indoc! {"
350 one two threeˇ four
351 five
352 six
353 "});
354
355 cx.set_shared_state(indoc! {"
356 ˇone
357 two
358 three
359 four
360 five
361 six
362 "})
363 .await;
364 cx.simulate_shared_keystrokes("j v 3 j shift-j").await;
365 cx.shared_state().await.assert_eq(indoc! {"
366 one
367 two three fourˇ five
368 six
369 "});
370}
371
372#[cfg(target_os = "macos")]
373#[gpui::test]
374async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
375 let mut cx = NeovimBackedTestContext::new(cx).await;
376
377 cx.set_shared_wrap(12).await;
378 // tests line wrap as follows:
379 // 1: twelve char
380 // twelve char
381 // 2: twelve char
382 cx.set_shared_state(indoc! { "
383 tˇwelve char twelve char
384 twelve char
385 "})
386 .await;
387 cx.simulate_shared_keystrokes("j").await;
388 cx.shared_state().await.assert_eq(indoc! {"
389 twelve char twelve char
390 tˇwelve char
391 "});
392 cx.simulate_shared_keystrokes("k").await;
393 cx.shared_state().await.assert_eq(indoc! {"
394 tˇwelve char twelve char
395 twelve char
396 "});
397 cx.simulate_shared_keystrokes("g j").await;
398 cx.shared_state().await.assert_eq(indoc! {"
399 twelve char tˇwelve char
400 twelve char
401 "});
402 cx.simulate_shared_keystrokes("g j").await;
403 cx.shared_state().await.assert_eq(indoc! {"
404 twelve char twelve char
405 tˇwelve char
406 "});
407
408 cx.simulate_shared_keystrokes("g k").await;
409 cx.shared_state().await.assert_eq(indoc! {"
410 twelve char tˇwelve char
411 twelve char
412 "});
413
414 cx.simulate_shared_keystrokes("g ^").await;
415 cx.shared_state().await.assert_eq(indoc! {"
416 twelve char ˇtwelve char
417 twelve char
418 "});
419
420 cx.simulate_shared_keystrokes("^").await;
421 cx.shared_state().await.assert_eq(indoc! {"
422 ˇtwelve char twelve char
423 twelve char
424 "});
425
426 cx.simulate_shared_keystrokes("g $").await;
427 cx.shared_state().await.assert_eq(indoc! {"
428 twelve charˇ twelve char
429 twelve char
430 "});
431 cx.simulate_shared_keystrokes("$").await;
432 cx.shared_state().await.assert_eq(indoc! {"
433 twelve char twelve chaˇr
434 twelve char
435 "});
436
437 cx.set_shared_state(indoc! { "
438 tˇwelve char twelve char
439 twelve char
440 "})
441 .await;
442 cx.simulate_shared_keystrokes("enter").await;
443 cx.shared_state().await.assert_eq(indoc! {"
444 twelve char twelve char
445 ˇtwelve char
446 "});
447
448 cx.set_shared_state(indoc! { "
449 twelve char
450 tˇwelve char twelve char
451 twelve char
452 "})
453 .await;
454 cx.simulate_shared_keystrokes("o o escape").await;
455 cx.shared_state().await.assert_eq(indoc! {"
456 twelve char
457 twelve char twelve char
458 ˇo
459 twelve char
460 "});
461
462 cx.set_shared_state(indoc! { "
463 twelve char
464 tˇwelve char twelve char
465 twelve char
466 "})
467 .await;
468 cx.simulate_shared_keystrokes("shift-a a escape").await;
469 cx.shared_state().await.assert_eq(indoc! {"
470 twelve char
471 twelve char twelve charˇa
472 twelve char
473 "});
474 cx.simulate_shared_keystrokes("shift-i i escape").await;
475 cx.shared_state().await.assert_eq(indoc! {"
476 twelve char
477 ˇitwelve char twelve chara
478 twelve char
479 "});
480 cx.simulate_shared_keystrokes("shift-d").await;
481 cx.shared_state().await.assert_eq(indoc! {"
482 twelve char
483 ˇ
484 twelve char
485 "});
486
487 cx.set_shared_state(indoc! { "
488 twelve char
489 twelve char tˇwelve char
490 twelve char
491 "})
492 .await;
493 cx.simulate_shared_keystrokes("shift-o o escape").await;
494 cx.shared_state().await.assert_eq(indoc! {"
495 twelve char
496 ˇo
497 twelve char twelve char
498 twelve char
499 "});
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.shared_state().await.assert_eq(indoc! {"
514 fourteenˇ•
515 fourteen char
516 "});
517 cx.simulate_shared_keystrokes("j shift-f e f r").await;
518 cx.shared_state().await.assert_eq(indoc! {"
519 fourteen•
520 fourteen chaˇr
521 "});
522}
523
524#[gpui::test]
525async fn test_folds(cx: &mut gpui::TestAppContext) {
526 let mut cx = NeovimBackedTestContext::new(cx).await;
527 cx.set_neovim_option("foldmethod=manual").await;
528
529 cx.set_shared_state(indoc! { "
530 fn boop() {
531 ˇbarp()
532 bazp()
533 }
534 "})
535 .await;
536 cx.simulate_shared_keystrokes("shift-v j z f").await;
537
538 // visual display is now:
539 // fn boop () {
540 // [FOLDED]
541 // }
542
543 // TODO: this should not be needed but currently zf does not
544 // return to normal mode.
545 cx.simulate_shared_keystrokes("escape").await;
546
547 // skip over fold downward
548 cx.simulate_shared_keystrokes("g g").await;
549 cx.shared_state().await.assert_eq(indoc! {"
550 ˇfn boop() {
551 barp()
552 bazp()
553 }
554 "});
555
556 cx.simulate_shared_keystrokes("j j").await;
557 cx.shared_state().await.assert_eq(indoc! {"
558 fn boop() {
559 barp()
560 bazp()
561 ˇ}
562 "});
563
564 // skip over fold upward
565 cx.simulate_shared_keystrokes("2 k").await;
566 cx.shared_state().await.assert_eq(indoc! {"
567 ˇfn boop() {
568 barp()
569 bazp()
570 }
571 "});
572
573 // yank the fold
574 cx.simulate_shared_keystrokes("down y y").await;
575 cx.shared_clipboard()
576 .await
577 .assert_eq(" barp()\n bazp()\n");
578
579 // re-open
580 cx.simulate_shared_keystrokes("z o").await;
581 cx.shared_state().await.assert_eq(indoc! {"
582 fn boop() {
583 ˇ barp()
584 bazp()
585 }
586 "});
587}
588
589#[gpui::test]
590async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
591 let mut cx = NeovimBackedTestContext::new(cx).await;
592 cx.set_neovim_option("foldmethod=manual").await;
593
594 cx.set_shared_state(indoc! { "
595 fn boop() {
596 ˇbarp()
597 bazp()
598 }
599 "})
600 .await;
601 cx.simulate_shared_keystrokes("shift-v j z f").await;
602 cx.simulate_shared_keystrokes("escape").await;
603 cx.simulate_shared_keystrokes("g g").await;
604 cx.simulate_shared_keystrokes("5 d j").await;
605 cx.shared_state().await.assert_eq("ˇ");
606 cx.set_shared_state(indoc! {"
607 fn boop() {
608 ˇbarp()
609 bazp()
610 }
611 "})
612 .await;
613 cx.simulate_shared_keystrokes("shift-v j j z f").await;
614 cx.simulate_shared_keystrokes("escape").await;
615 cx.simulate_shared_keystrokes("shift-g shift-v").await;
616 cx.shared_state().await.assert_eq(indoc! {"
617 fn boop() {
618 barp()
619 bazp()
620 }
621 ˇ"});
622}
623
624#[gpui::test]
625async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
626 let mut cx = NeovimBackedTestContext::new(cx).await;
627
628 cx.set_shared_state(indoc! {"
629 The quick brown
630 fox juˇmps over
631 the lazy dog"})
632 .await;
633
634 cx.simulate_shared_keystrokes("4 escape 3 d l").await;
635 cx.shared_state().await.assert_eq(indoc! {"
636 The quick brown
637 fox juˇ over
638 the lazy dog"});
639}
640
641#[gpui::test]
642async fn test_zero(cx: &mut gpui::TestAppContext) {
643 let mut cx = NeovimBackedTestContext::new(cx).await;
644
645 cx.set_shared_state(indoc! {"
646 The quˇick brown
647 fox jumps over
648 the lazy dog"})
649 .await;
650
651 cx.simulate_shared_keystrokes("0").await;
652 cx.shared_state().await.assert_eq(indoc! {"
653 ˇThe quick brown
654 fox jumps over
655 the lazy dog"});
656
657 cx.simulate_shared_keystrokes("1 0 l").await;
658 cx.shared_state().await.assert_eq(indoc! {"
659 The quick ˇbrown
660 fox jumps over
661 the lazy dog"});
662}
663
664#[gpui::test]
665async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
666 let mut cx = NeovimBackedTestContext::new(cx).await;
667
668 cx.set_shared_state(indoc! {"
669 ;;ˇ;
670 Lorem Ipsum"})
671 .await;
672
673 cx.simulate_shared_keystrokes("a down up ; down up").await;
674 cx.shared_state().await.assert_eq(indoc! {"
675 ;;;;ˇ
676 Lorem Ipsum"});
677}
678
679#[cfg(target_os = "macos")]
680#[gpui::test]
681async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
682 let mut cx = NeovimBackedTestContext::new(cx).await;
683
684 cx.set_shared_wrap(12).await;
685
686 cx.set_shared_state(indoc! {"
687 aaˇaa
688 😃😃"
689 })
690 .await;
691 cx.simulate_shared_keystrokes("j").await;
692 cx.shared_state().await.assert_eq(indoc! {"
693 aaaa
694 😃ˇ😃"
695 });
696
697 cx.set_shared_state(indoc! {"
698 123456789012aaˇaa
699 123456789012😃😃"
700 })
701 .await;
702 cx.simulate_shared_keystrokes("j").await;
703 cx.shared_state().await.assert_eq(indoc! {"
704 123456789012aaaa
705 123456789012😃ˇ😃"
706 });
707
708 cx.set_shared_state(indoc! {"
709 123456789012aaˇaa
710 123456789012😃😃"
711 })
712 .await;
713 cx.simulate_shared_keystrokes("j").await;
714 cx.shared_state().await.assert_eq(indoc! {"
715 123456789012aaaa
716 123456789012😃ˇ😃"
717 });
718
719 cx.set_shared_state(indoc! {"
720 123456789012aaaaˇaaaaaaaa123456789012
721 wow
722 123456789012😃😃😃😃😃😃123456789012"
723 })
724 .await;
725 cx.simulate_shared_keystrokes("j j").await;
726 cx.shared_state().await.assert_eq(indoc! {"
727 123456789012aaaaaaaaaaaa123456789012
728 wow
729 123456789012😃😃ˇ😃😃😃😃123456789012"
730 });
731}
732
733#[gpui::test]
734async fn test_wrapped_delete_end_document(cx: &mut gpui::TestAppContext) {
735 let mut cx = NeovimBackedTestContext::new(cx).await;
736
737 cx.set_shared_wrap(12).await;
738
739 cx.set_shared_state(indoc! {"
740 aaˇaaaaaaaaaaaaaaaaaa
741 bbbbbbbbbbbbbbbbbbbb
742 cccccccccccccccccccc"
743 })
744 .await;
745 cx.simulate_shared_keystrokes("d shift-g i z z z").await;
746 cx.shared_state().await.assert_eq(indoc! {"
747 zzzˇ"
748 });
749}
750
751#[gpui::test]
752async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
753 let mut cx = NeovimBackedTestContext::new(cx).await;
754
755 cx.set_shared_state(indoc! {"
756 one
757 ˇ
758 two"})
759 .await;
760
761 cx.simulate_shared_keystrokes("} }").await;
762 cx.shared_state().await.assert_eq(indoc! {"
763 one
764
765 twˇo"});
766
767 cx.simulate_shared_keystrokes("{ { {").await;
768 cx.shared_state().await.assert_eq(indoc! {"
769 ˇone
770
771 two"});
772}
773
774#[gpui::test]
775async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
776 let mut cx = VimTestContext::new(cx, true).await;
777
778 cx.set_state(
779 indoc! {"
780 defmodule Test do
781 def test(a, ˇ[_, _] = b), do: IO.puts('hi')
782 end
783 "},
784 Mode::Normal,
785 );
786 cx.simulate_keystrokes("g a");
787 cx.assert_state(
788 indoc! {"
789 defmodule Test do
790 def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
791 end
792 "},
793 Mode::Visual,
794 );
795}
796
797#[gpui::test]
798async fn test_jk(cx: &mut gpui::TestAppContext) {
799 let mut cx = NeovimBackedTestContext::new(cx).await;
800
801 cx.update(|cx| {
802 cx.bind_keys([KeyBinding::new(
803 "j k",
804 NormalBefore,
805 Some("vim_mode == insert"),
806 )])
807 });
808 cx.neovim.exec("imap jk <esc>").await;
809
810 cx.set_shared_state("ˇhello").await;
811 cx.simulate_shared_keystrokes("i j o j k").await;
812 cx.shared_state().await.assert_eq("jˇohello");
813}
814
815#[gpui::test]
816async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
817 let mut cx = VimTestContext::new(cx, true).await;
818
819 cx.update(|cx| {
820 cx.bind_keys([KeyBinding::new(
821 "j k",
822 NormalBefore,
823 Some("vim_mode == insert"),
824 )])
825 });
826
827 cx.set_state("ˇhello", Mode::Normal);
828 cx.simulate_keystrokes("i j");
829 cx.executor().advance_clock(Duration::from_millis(500));
830 cx.run_until_parked();
831 cx.assert_state("ˇhello", Mode::Insert);
832 cx.executor().advance_clock(Duration::from_millis(500));
833 cx.run_until_parked();
834 cx.assert_state("jˇhello", Mode::Insert);
835 cx.simulate_keystrokes("k j k");
836 cx.assert_state("jˇkhello", Mode::Normal);
837}
838
839#[gpui::test]
840async fn test_comma_w(cx: &mut gpui::TestAppContext) {
841 let mut cx = NeovimBackedTestContext::new(cx).await;
842
843 cx.update(|cx| {
844 cx.bind_keys([KeyBinding::new(
845 ", w",
846 motion::Down {
847 display_lines: false,
848 },
849 Some("vim_mode == normal"),
850 )])
851 });
852 cx.neovim.exec("map ,w j").await;
853
854 cx.set_shared_state("ˇhello hello\nhello hello").await;
855 cx.simulate_shared_keystrokes("f o ; , w").await;
856 cx.shared_state()
857 .await
858 .assert_eq("hello hello\nhello hellˇo");
859
860 cx.set_shared_state("ˇhello hello\nhello hello").await;
861 cx.simulate_shared_keystrokes("f o ; , i").await;
862 cx.shared_state()
863 .await
864 .assert_eq("hellˇo hello\nhello hello");
865}
866
867#[gpui::test]
868async fn test_rename(cx: &mut gpui::TestAppContext) {
869 let mut cx = VimTestContext::new_typescript(cx).await;
870
871 cx.set_state("const beˇfore = 2; console.log(before)", Mode::Normal);
872 let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
873 let tgt_range = cx.lsp_range("const before = 2; console.log(«beforeˇ»)");
874 let mut prepare_request =
875 cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
876 Ok(Some(lsp::PrepareRenameResponse::Range(def_range)))
877 });
878 let mut rename_request =
879 cx.handle_request::<lsp::request::Rename, _, _>(move |url, params, _| async move {
880 Ok(Some(lsp::WorkspaceEdit {
881 changes: Some(
882 [(
883 url.clone(),
884 vec![
885 lsp::TextEdit::new(def_range, params.new_name.clone()),
886 lsp::TextEdit::new(tgt_range, params.new_name),
887 ],
888 )]
889 .into(),
890 ),
891 ..Default::default()
892 }))
893 });
894
895 cx.simulate_keystrokes("c d");
896 prepare_request.next().await.unwrap();
897 cx.simulate_input("after");
898 cx.simulate_keystrokes("enter");
899 rename_request.next().await.unwrap();
900 cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
901}
902
903// TODO: this test is flaky on our linux CI machines
904#[cfg(target_os = "macos")]
905#[gpui::test]
906async fn test_remap(cx: &mut gpui::TestAppContext) {
907 let mut cx = VimTestContext::new(cx, true).await;
908
909 // test moving the cursor
910 cx.update(|cx| {
911 cx.bind_keys([KeyBinding::new(
912 "g z",
913 workspace::SendKeystrokes("l l l l".to_string()),
914 None,
915 )])
916 });
917 cx.set_state("ˇ123456789", Mode::Normal);
918 cx.simulate_keystrokes("g z");
919 cx.assert_state("1234ˇ56789", Mode::Normal);
920
921 // test switching modes
922 cx.update(|cx| {
923 cx.bind_keys([KeyBinding::new(
924 "g y",
925 workspace::SendKeystrokes("i f o o escape l".to_string()),
926 None,
927 )])
928 });
929 cx.set_state("ˇ123456789", Mode::Normal);
930 cx.simulate_keystrokes("g y");
931 cx.assert_state("fooˇ123456789", Mode::Normal);
932
933 // test recursion
934 cx.update(|cx| {
935 cx.bind_keys([KeyBinding::new(
936 "g x",
937 workspace::SendKeystrokes("g z g y".to_string()),
938 None,
939 )])
940 });
941 cx.set_state("ˇ123456789", Mode::Normal);
942 cx.simulate_keystrokes("g x");
943 cx.assert_state("1234fooˇ56789", Mode::Normal);
944
945 cx.executor().allow_parking();
946
947 // test command
948 cx.update(|cx| {
949 cx.bind_keys([KeyBinding::new(
950 "g w",
951 workspace::SendKeystrokes(": j enter".to_string()),
952 None,
953 )])
954 });
955 cx.set_state("ˇ1234\n56789", Mode::Normal);
956 cx.simulate_keystrokes("g w");
957 cx.assert_state("1234ˇ 56789", Mode::Normal);
958
959 // test leaving command
960 cx.update(|cx| {
961 cx.bind_keys([KeyBinding::new(
962 "g u",
963 workspace::SendKeystrokes("g w g z".to_string()),
964 None,
965 )])
966 });
967 cx.set_state("ˇ1234\n56789", Mode::Normal);
968 cx.simulate_keystrokes("g u");
969 cx.assert_state("1234 567ˇ89", Mode::Normal);
970
971 // test leaving command
972 cx.update(|cx| {
973 cx.bind_keys([KeyBinding::new(
974 "g t",
975 workspace::SendKeystrokes("i space escape".to_string()),
976 None,
977 )])
978 });
979 cx.set_state("12ˇ34", Mode::Normal);
980 cx.simulate_keystrokes("g t");
981 cx.assert_state("12ˇ 34", Mode::Normal);
982}
983
984#[gpui::test]
985async fn test_undo(cx: &mut gpui::TestAppContext) {
986 let mut cx = NeovimBackedTestContext::new(cx).await;
987
988 cx.set_shared_state("hello quˇoel world").await;
989 cx.simulate_shared_keystrokes("v i w s c o escape u").await;
990 cx.shared_state().await.assert_eq("hello ˇquoel world");
991 cx.simulate_shared_keystrokes("ctrl-r").await;
992 cx.shared_state().await.assert_eq("hello ˇco world");
993 cx.simulate_shared_keystrokes("a o right l escape").await;
994 cx.shared_state().await.assert_eq("hello cooˇl world");
995 cx.simulate_shared_keystrokes("u").await;
996 cx.shared_state().await.assert_eq("hello cooˇ world");
997 cx.simulate_shared_keystrokes("u").await;
998 cx.shared_state().await.assert_eq("hello cˇo world");
999 cx.simulate_shared_keystrokes("u").await;
1000 cx.shared_state().await.assert_eq("hello ˇquoel world");
1001
1002 cx.set_shared_state("hello quˇoel world").await;
1003 cx.simulate_shared_keystrokes("v i w ~ u").await;
1004 cx.shared_state().await.assert_eq("hello ˇquoel world");
1005
1006 cx.set_shared_state("\nhello quˇoel world\n").await;
1007 cx.simulate_shared_keystrokes("shift-v s c escape u").await;
1008 cx.shared_state().await.assert_eq("\nˇhello quoel world\n");
1009
1010 cx.set_shared_state(indoc! {"
1011 ˇ1
1012 2
1013 3"})
1014 .await;
1015
1016 cx.simulate_shared_keystrokes("ctrl-v shift-g ctrl-a").await;
1017 cx.shared_state().await.assert_eq(indoc! {"
1018 ˇ2
1019 3
1020 4"});
1021
1022 cx.simulate_shared_keystrokes("u").await;
1023 cx.shared_state().await.assert_eq(indoc! {"
1024 ˇ1
1025 2
1026 3"});
1027}
1028
1029#[gpui::test]
1030async fn test_mouse_selection(cx: &mut TestAppContext) {
1031 let mut cx = VimTestContext::new(cx, true).await;
1032
1033 cx.set_state("ˇone two three", Mode::Normal);
1034
1035 let start_point = cx.pixel_position("one twˇo three");
1036 let end_point = cx.pixel_position("one ˇtwo three");
1037
1038 cx.simulate_mouse_down(start_point, MouseButton::Left, Modifiers::none());
1039 cx.simulate_mouse_move(end_point, MouseButton::Left, Modifiers::none());
1040 cx.simulate_mouse_up(end_point, MouseButton::Left, Modifiers::none());
1041
1042 cx.assert_state("one «ˇtwo» three", Mode::Visual)
1043}
1044
1045#[gpui::test]
1046async fn test_lowercase_marks(cx: &mut TestAppContext) {
1047 let mut cx = NeovimBackedTestContext::new(cx).await;
1048
1049 cx.set_shared_state("line one\nline ˇtwo\nline three").await;
1050 cx.simulate_shared_keystrokes("m a l ' a").await;
1051 cx.shared_state()
1052 .await
1053 .assert_eq("line one\nˇline two\nline three");
1054 cx.simulate_shared_keystrokes("` a").await;
1055 cx.shared_state()
1056 .await
1057 .assert_eq("line one\nline ˇtwo\nline three");
1058
1059 cx.simulate_shared_keystrokes("^ d ` a").await;
1060 cx.shared_state()
1061 .await
1062 .assert_eq("line one\nˇtwo\nline three");
1063}
1064
1065#[gpui::test]
1066async fn test_lt_gt_marks(cx: &mut TestAppContext) {
1067 let mut cx = NeovimBackedTestContext::new(cx).await;
1068
1069 cx.set_shared_state(indoc!(
1070 "
1071 Line one
1072 Line two
1073 Line ˇthree
1074 Line four
1075 Line five
1076 "
1077 ))
1078 .await;
1079
1080 cx.simulate_shared_keystrokes("v j escape k k").await;
1081
1082 cx.simulate_shared_keystrokes("' <").await;
1083 cx.shared_state().await.assert_eq(indoc! {"
1084 Line one
1085 Line two
1086 ˇLine three
1087 Line four
1088 Line five
1089 "});
1090
1091 cx.simulate_shared_keystrokes("` <").await;
1092 cx.shared_state().await.assert_eq(indoc! {"
1093 Line one
1094 Line two
1095 Line ˇthree
1096 Line four
1097 Line five
1098 "});
1099
1100 cx.simulate_shared_keystrokes("' >").await;
1101 cx.shared_state().await.assert_eq(indoc! {"
1102 Line one
1103 Line two
1104 Line three
1105 ˇLine four
1106 Line five
1107 "
1108 });
1109
1110 cx.simulate_shared_keystrokes("` >").await;
1111 cx.shared_state().await.assert_eq(indoc! {"
1112 Line one
1113 Line two
1114 Line three
1115 Line ˇfour
1116 Line five
1117 "
1118 });
1119
1120 cx.simulate_shared_keystrokes("v i w o escape").await;
1121 cx.simulate_shared_keystrokes("` >").await;
1122 cx.shared_state().await.assert_eq(indoc! {"
1123 Line one
1124 Line two
1125 Line three
1126 Line fouˇr
1127 Line five
1128 "
1129 });
1130 cx.simulate_shared_keystrokes("` <").await;
1131 cx.shared_state().await.assert_eq(indoc! {"
1132 Line one
1133 Line two
1134 Line three
1135 Line ˇfour
1136 Line five
1137 "
1138 });
1139}
1140
1141#[gpui::test]
1142async fn test_caret_mark(cx: &mut TestAppContext) {
1143 let mut cx = NeovimBackedTestContext::new(cx).await;
1144
1145 cx.set_shared_state(indoc!(
1146 "
1147 Line one
1148 Line two
1149 Line three
1150 ˇLine four
1151 Line five
1152 "
1153 ))
1154 .await;
1155
1156 cx.simulate_shared_keystrokes("c w shift-s t r a i g h t space t h i n g escape j j")
1157 .await;
1158
1159 cx.simulate_shared_keystrokes("' ^").await;
1160 cx.shared_state().await.assert_eq(indoc! {"
1161 Line one
1162 Line two
1163 Line three
1164 ˇStraight thing four
1165 Line five
1166 "
1167 });
1168
1169 cx.simulate_shared_keystrokes("` ^").await;
1170 cx.shared_state().await.assert_eq(indoc! {"
1171 Line one
1172 Line two
1173 Line three
1174 Straight thingˇ four
1175 Line five
1176 "
1177 });
1178
1179 cx.simulate_shared_keystrokes("k a ! escape k g i ?").await;
1180 cx.shared_state().await.assert_eq(indoc! {"
1181 Line one
1182 Line two
1183 Line three!?ˇ
1184 Straight thing four
1185 Line five
1186 "
1187 });
1188}
1189
1190#[cfg(target_os = "macos")]
1191#[gpui::test]
1192async fn test_dw_eol(cx: &mut gpui::TestAppContext) {
1193 let mut cx = NeovimBackedTestContext::new(cx).await;
1194
1195 cx.set_shared_wrap(12).await;
1196 cx.set_shared_state("twelve ˇchar twelve char\ntwelve char")
1197 .await;
1198 cx.simulate_shared_keystrokes("d w").await;
1199 cx.shared_state()
1200 .await
1201 .assert_eq("twelve ˇtwelve char\ntwelve char");
1202}
1203
1204#[gpui::test]
1205async fn test_toggle_comments(cx: &mut gpui::TestAppContext) {
1206 let mut cx = VimTestContext::new(cx, true).await;
1207
1208 let language = std::sync::Arc::new(language::Language::new(
1209 language::LanguageConfig {
1210 line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
1211 ..Default::default()
1212 },
1213 Some(language::tree_sitter_rust::LANGUAGE.into()),
1214 ));
1215 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1216
1217 // works in normal model
1218 cx.set_state(
1219 indoc! {"
1220 ˇone
1221 two
1222 three
1223 "},
1224 Mode::Normal,
1225 );
1226 cx.simulate_keystrokes("g c c");
1227 cx.assert_state(
1228 indoc! {"
1229 // ˇone
1230 two
1231 three
1232 "},
1233 Mode::Normal,
1234 );
1235
1236 // works in visual mode
1237 cx.simulate_keystrokes("v j g c");
1238 cx.assert_state(
1239 indoc! {"
1240 // // ˇone
1241 // two
1242 three
1243 "},
1244 Mode::Normal,
1245 );
1246
1247 // works in visual line mode
1248 cx.simulate_keystrokes("shift-v j g c");
1249 cx.assert_state(
1250 indoc! {"
1251 // ˇone
1252 two
1253 three
1254 "},
1255 Mode::Normal,
1256 );
1257
1258 // works with count
1259 cx.simulate_keystrokes("g c 2 j");
1260 cx.assert_state(
1261 indoc! {"
1262 // // ˇone
1263 // two
1264 // three
1265 "},
1266 Mode::Normal,
1267 );
1268
1269 // works with motion object
1270 cx.simulate_keystrokes("shift-g");
1271 cx.simulate_keystrokes("g c g g");
1272 cx.assert_state(
1273 indoc! {"
1274 // one
1275 two
1276 three
1277 ˇ"},
1278 Mode::Normal,
1279 );
1280}
1281
1282#[gpui::test]
1283async fn test_find_multibyte(cx: &mut gpui::TestAppContext) {
1284 let mut cx = NeovimBackedTestContext::new(cx).await;
1285
1286 cx.set_shared_state(r#"<label for="guests">ˇPočet hostů</label>"#)
1287 .await;
1288
1289 cx.simulate_shared_keystrokes("c t < o escape").await;
1290 cx.shared_state()
1291 .await
1292 .assert_eq(r#"<label for="guests">ˇo</label>"#);
1293}
1294
1295#[gpui::test]
1296async fn test_plus_minus(cx: &mut gpui::TestAppContext) {
1297 let mut cx = NeovimBackedTestContext::new(cx).await;
1298
1299 cx.set_shared_state(indoc! {
1300 "one
1301 two
1302 thrˇee
1303 "})
1304 .await;
1305
1306 cx.simulate_shared_keystrokes("-").await;
1307 cx.shared_state().await.assert_matches();
1308 cx.simulate_shared_keystrokes("-").await;
1309 cx.shared_state().await.assert_matches();
1310 cx.simulate_shared_keystrokes("+").await;
1311 cx.shared_state().await.assert_matches();
1312}
1313
1314#[gpui::test]
1315async fn test_command_alias(cx: &mut gpui::TestAppContext) {
1316 let mut cx = VimTestContext::new(cx, true).await;
1317 cx.update_global(|store: &mut SettingsStore, cx| {
1318 store.update_user_settings::<WorkspaceSettings>(cx, |s| {
1319 let mut aliases = HashMap::default();
1320 aliases.insert("Q".to_string(), "upper".to_string());
1321 s.command_aliases = Some(aliases)
1322 });
1323 });
1324
1325 cx.set_state("ˇhello world", Mode::Normal);
1326 cx.simulate_keystrokes(": Q");
1327 cx.set_state("ˇHello world", Mode::Normal);
1328}
1329
1330#[gpui::test]
1331async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
1332 let mut cx = NeovimBackedTestContext::new(cx).await;
1333 cx.update(|cx| {
1334 cx.bind_keys([
1335 KeyBinding::new(
1336 "d o g",
1337 workspace::SendKeystrokes("🐶".to_string()),
1338 Some("vim_mode == insert"),
1339 ),
1340 KeyBinding::new(
1341 "c a t",
1342 workspace::SendKeystrokes("🐱".to_string()),
1343 Some("vim_mode == insert"),
1344 ),
1345 ])
1346 });
1347 cx.neovim.exec("imap dog 🐶").await;
1348 cx.neovim.exec("imap cat 🐱").await;
1349
1350 cx.set_shared_state("ˇ").await;
1351 cx.simulate_shared_keystrokes("i d o g").await;
1352 cx.shared_state().await.assert_eq("🐶ˇ");
1353
1354 cx.set_shared_state("ˇ").await;
1355 cx.simulate_shared_keystrokes("i d o d o g").await;
1356 cx.shared_state().await.assert_eq("do🐶ˇ");
1357
1358 cx.set_shared_state("ˇ").await;
1359 cx.simulate_shared_keystrokes("i d o c a t").await;
1360 cx.shared_state().await.assert_eq("do🐱ˇ");
1361}
1362
1363#[gpui::test]
1364async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
1365 let mut cx = NeovimBackedTestContext::new(cx).await;
1366 cx.update(|cx| {
1367 cx.bind_keys([
1368 KeyBinding::new(
1369 "p i n",
1370 workspace::SendKeystrokes("📌".to_string()),
1371 Some("vim_mode == insert"),
1372 ),
1373 KeyBinding::new(
1374 "p i n e",
1375 workspace::SendKeystrokes("🌲".to_string()),
1376 Some("vim_mode == insert"),
1377 ),
1378 KeyBinding::new(
1379 "p i n e a p p l e",
1380 workspace::SendKeystrokes("🍍".to_string()),
1381 Some("vim_mode == insert"),
1382 ),
1383 ])
1384 });
1385 cx.neovim.exec("imap pin 📌").await;
1386 cx.neovim.exec("imap pine 🌲").await;
1387 cx.neovim.exec("imap pineapple 🍍").await;
1388
1389 cx.set_shared_state("ˇ").await;
1390 cx.simulate_shared_keystrokes("i p i n").await;
1391 cx.executor().advance_clock(Duration::from_millis(1000));
1392 cx.run_until_parked();
1393 cx.shared_state().await.assert_eq("📌ˇ");
1394
1395 cx.set_shared_state("ˇ").await;
1396 cx.simulate_shared_keystrokes("i p i n e").await;
1397 cx.executor().advance_clock(Duration::from_millis(1000));
1398 cx.run_until_parked();
1399 cx.shared_state().await.assert_eq("🌲ˇ");
1400
1401 cx.set_shared_state("ˇ").await;
1402 cx.simulate_shared_keystrokes("i p i n e a p p l e").await;
1403 cx.shared_state().await.assert_eq("🍍ˇ");
1404}
1405
1406#[gpui::test]
1407async fn test_remap_recursion(cx: &mut gpui::TestAppContext) {
1408 let mut cx = NeovimBackedTestContext::new(cx).await;
1409 cx.update(|cx| {
1410 cx.bind_keys([KeyBinding::new(
1411 "x",
1412 workspace::SendKeystrokes("\" _ x".to_string()),
1413 Some("VimControl"),
1414 )]);
1415 cx.bind_keys([KeyBinding::new(
1416 "y",
1417 workspace::SendKeystrokes("2 x".to_string()),
1418 Some("VimControl"),
1419 )])
1420 });
1421 cx.neovim.exec("noremap x \"_x").await;
1422 cx.neovim.exec("map y 2x").await;
1423
1424 cx.set_shared_state("ˇhello").await;
1425 cx.simulate_shared_keystrokes("d l").await;
1426 cx.shared_clipboard().await.assert_eq("h");
1427 cx.simulate_shared_keystrokes("y").await;
1428 cx.shared_clipboard().await.assert_eq("h");
1429 cx.shared_state().await.assert_eq("ˇlo");
1430}
1431
1432#[gpui::test]
1433async fn test_escape_while_waiting(cx: &mut gpui::TestAppContext) {
1434 let mut cx = NeovimBackedTestContext::new(cx).await;
1435 cx.set_shared_state("ˇhi").await;
1436 cx.simulate_shared_keystrokes("\" + escape x").await;
1437 cx.shared_state().await.assert_eq("ˇi");
1438}
1439
1440#[gpui::test]
1441async fn test_ctrl_w_override(cx: &mut gpui::TestAppContext) {
1442 let mut cx = NeovimBackedTestContext::new(cx).await;
1443 cx.update(|cx| {
1444 cx.bind_keys([KeyBinding::new("ctrl-w", DeleteLine, None)]);
1445 });
1446 cx.neovim.exec("map <c-w> D").await;
1447 cx.set_shared_state("ˇhi").await;
1448 cx.simulate_shared_keystrokes("ctrl-w").await;
1449 cx.shared_state().await.assert_eq("ˇ");
1450}
1451
1452#[gpui::test]
1453async fn test_visual_indent_count(cx: &mut gpui::TestAppContext) {
1454 let mut cx = VimTestContext::new(cx, true).await;
1455 cx.set_state("ˇhi", Mode::Normal);
1456 cx.simulate_keystrokes("shift-v 3 >");
1457 cx.assert_state(" ˇhi", Mode::Normal);
1458 cx.simulate_keystrokes("shift-v 2 <");
1459 cx.assert_state(" ˇhi", Mode::Normal);
1460}
1461
1462#[gpui::test]
1463async fn test_record_replay_recursion(cx: &mut gpui::TestAppContext) {
1464 let mut cx = NeovimBackedTestContext::new(cx).await;
1465
1466 cx.set_shared_state("ˇhello world").await;
1467 cx.simulate_shared_keystrokes(">").await;
1468 cx.simulate_shared_keystrokes(".").await;
1469 cx.simulate_shared_keystrokes(".").await;
1470 cx.simulate_shared_keystrokes(".").await;
1471 cx.shared_state().await.assert_eq("ˇhello world");
1472}
1473
1474#[gpui::test]
1475async fn test_blackhole_register(cx: &mut gpui::TestAppContext) {
1476 let mut cx = NeovimBackedTestContext::new(cx).await;
1477
1478 cx.set_shared_state("ˇhello world").await;
1479 cx.simulate_shared_keystrokes("d i w \" _ d a w").await;
1480 cx.simulate_shared_keystrokes("p").await;
1481 cx.shared_state().await.assert_eq("hellˇo");
1482}
1483
1484#[gpui::test]
1485async fn test_sentence_backwards(cx: &mut gpui::TestAppContext) {
1486 let mut cx = NeovimBackedTestContext::new(cx).await;
1487
1488 cx.set_shared_state("one\n\ntwo\nthree\nˇ\nfour").await;
1489 cx.simulate_shared_keystrokes("(").await;
1490 cx.shared_state()
1491 .await
1492 .assert_eq("one\n\nˇtwo\nthree\n\nfour");
1493
1494 cx.set_shared_state("hello.\n\n\nworˇld.").await;
1495 cx.simulate_shared_keystrokes("(").await;
1496 cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1497 cx.simulate_shared_keystrokes("(").await;
1498 cx.shared_state().await.assert_eq("hello.\n\nˇ\nworld.");
1499 cx.simulate_shared_keystrokes("(").await;
1500 cx.shared_state().await.assert_eq("ˇhello.\n\n\nworld.");
1501
1502 cx.set_shared_state("hello. worlˇd.").await;
1503 cx.simulate_shared_keystrokes("(").await;
1504 cx.shared_state().await.assert_eq("hello. ˇworld.");
1505 cx.simulate_shared_keystrokes("(").await;
1506 cx.shared_state().await.assert_eq("ˇhello. world.");
1507
1508 cx.set_shared_state(". helˇlo.").await;
1509 cx.simulate_shared_keystrokes("(").await;
1510 cx.shared_state().await.assert_eq(". ˇhello.");
1511 cx.simulate_shared_keystrokes("(").await;
1512 cx.shared_state().await.assert_eq(". ˇhello.");
1513
1514 cx.set_shared_state(indoc! {
1515 "{
1516 hello_world();
1517 ˇ}"
1518 })
1519 .await;
1520 cx.simulate_shared_keystrokes("(").await;
1521 cx.shared_state().await.assert_eq(indoc! {
1522 "ˇ{
1523 hello_world();
1524 }"
1525 });
1526
1527 cx.set_shared_state(indoc! {
1528 "Hello! World..?
1529
1530 \tHello! World... ˇ"
1531 })
1532 .await;
1533 cx.simulate_shared_keystrokes("(").await;
1534 cx.shared_state().await.assert_eq(indoc! {
1535 "Hello! World..?
1536
1537 \tHello! ˇWorld... "
1538 });
1539 cx.simulate_shared_keystrokes("(").await;
1540 cx.shared_state().await.assert_eq(indoc! {
1541 "Hello! World..?
1542
1543 \tˇHello! World... "
1544 });
1545 cx.simulate_shared_keystrokes("(").await;
1546 cx.shared_state().await.assert_eq(indoc! {
1547 "Hello! World..?
1548 ˇ
1549 \tHello! World... "
1550 });
1551 cx.simulate_shared_keystrokes("(").await;
1552 cx.shared_state().await.assert_eq(indoc! {
1553 "Hello! ˇWorld..?
1554
1555 \tHello! World... "
1556 });
1557}
1558
1559#[gpui::test]
1560async fn test_sentence_forwards(cx: &mut gpui::TestAppContext) {
1561 let mut cx = NeovimBackedTestContext::new(cx).await;
1562
1563 cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1564 cx.simulate_shared_keystrokes(")").await;
1565 cx.shared_state().await.assert_eq("hello.\nˇ\n\nworld.");
1566 cx.simulate_shared_keystrokes(")").await;
1567 cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1568 cx.simulate_shared_keystrokes(")").await;
1569 cx.shared_state().await.assert_eq("hello.\n\n\nworldˇ.");
1570
1571 cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1572}
1573
1574#[gpui::test]
1575async fn test_ctrl_o_visual(cx: &mut gpui::TestAppContext) {
1576 let mut cx = NeovimBackedTestContext::new(cx).await;
1577
1578 cx.set_shared_state("helloˇ world.").await;
1579 cx.simulate_shared_keystrokes("i ctrl-o v b r l").await;
1580 cx.shared_state().await.assert_eq("ˇllllllworld.");
1581 cx.simulate_shared_keystrokes("ctrl-o v f w d").await;
1582 cx.shared_state().await.assert_eq("ˇorld.");
1583}
1584
1585#[gpui::test]
1586async fn test_ctrl_o_position(cx: &mut gpui::TestAppContext) {
1587 let mut cx = NeovimBackedTestContext::new(cx).await;
1588
1589 cx.set_shared_state("helˇlo world.").await;
1590 cx.simulate_shared_keystrokes("i ctrl-o d i w").await;
1591 cx.shared_state().await.assert_eq("ˇ world.");
1592 cx.simulate_shared_keystrokes("ctrl-o p").await;
1593 cx.shared_state().await.assert_eq(" helloˇworld.");
1594}
1595
1596#[gpui::test]
1597async fn test_ctrl_o_dot(cx: &mut gpui::TestAppContext) {
1598 let mut cx = NeovimBackedTestContext::new(cx).await;
1599
1600 cx.set_shared_state("heˇllo world.").await;
1601 cx.simulate_shared_keystrokes("x i ctrl-o .").await;
1602 cx.shared_state().await.assert_eq("heˇo world.");
1603 cx.simulate_shared_keystrokes("l l escape .").await;
1604 cx.shared_state().await.assert_eq("hellˇllo world.");
1605}