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_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.shared_state().await.assert_eq(indoc! {"
745 one
746
747 twˇo"});
748
749 cx.simulate_shared_keystrokes("{ { {").await;
750 cx.shared_state().await.assert_eq(indoc! {"
751 ˇone
752
753 two"});
754}
755
756#[gpui::test]
757async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
758 let mut cx = VimTestContext::new(cx, true).await;
759
760 cx.set_state(
761 indoc! {"
762 defmodule Test do
763 def test(a, ˇ[_, _] = b), do: IO.puts('hi')
764 end
765 "},
766 Mode::Normal,
767 );
768 cx.simulate_keystrokes("g a");
769 cx.assert_state(
770 indoc! {"
771 defmodule Test do
772 def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
773 end
774 "},
775 Mode::Visual,
776 );
777}
778
779#[gpui::test]
780async fn test_jk(cx: &mut gpui::TestAppContext) {
781 let mut cx = NeovimBackedTestContext::new(cx).await;
782
783 cx.update(|cx| {
784 cx.bind_keys([KeyBinding::new(
785 "j k",
786 NormalBefore,
787 Some("vim_mode == insert"),
788 )])
789 });
790 cx.neovim.exec("imap jk <esc>").await;
791
792 cx.set_shared_state("ˇhello").await;
793 cx.simulate_shared_keystrokes("i j o j k").await;
794 cx.shared_state().await.assert_eq("jˇohello");
795}
796
797#[gpui::test]
798async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
799 let mut cx = VimTestContext::new(cx, true).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
809 cx.set_state("ˇhello", Mode::Normal);
810 cx.simulate_keystrokes("i j");
811 cx.executor().advance_clock(Duration::from_millis(500));
812 cx.run_until_parked();
813 cx.assert_state("ˇhello", Mode::Insert);
814 cx.executor().advance_clock(Duration::from_millis(500));
815 cx.run_until_parked();
816 cx.assert_state("jˇhello", Mode::Insert);
817 cx.simulate_keystrokes("k j k");
818 cx.assert_state("jˇkhello", Mode::Normal);
819}
820
821#[gpui::test]
822async fn test_comma_w(cx: &mut gpui::TestAppContext) {
823 let mut cx = NeovimBackedTestContext::new(cx).await;
824
825 cx.update(|cx| {
826 cx.bind_keys([KeyBinding::new(
827 ", w",
828 motion::Down {
829 display_lines: false,
830 },
831 Some("vim_mode == normal"),
832 )])
833 });
834 cx.neovim.exec("map ,w j").await;
835
836 cx.set_shared_state("ˇhello hello\nhello hello").await;
837 cx.simulate_shared_keystrokes("f o ; , w").await;
838 cx.shared_state()
839 .await
840 .assert_eq("hello hello\nhello hellˇo");
841
842 cx.set_shared_state("ˇhello hello\nhello hello").await;
843 cx.simulate_shared_keystrokes("f o ; , i").await;
844 cx.shared_state()
845 .await
846 .assert_eq("hellˇo hello\nhello hello");
847}
848
849#[gpui::test]
850async fn test_rename(cx: &mut gpui::TestAppContext) {
851 let mut cx = VimTestContext::new_typescript(cx).await;
852
853 cx.set_state("const beˇfore = 2; console.log(before)", Mode::Normal);
854 let def_range = cx.lsp_range("const «beforeˇ» = 2; console.log(before)");
855 let tgt_range = cx.lsp_range("const before = 2; console.log(«beforeˇ»)");
856 let mut prepare_request =
857 cx.handle_request::<lsp::request::PrepareRenameRequest, _, _>(move |_, _, _| async move {
858 Ok(Some(lsp::PrepareRenameResponse::Range(def_range)))
859 });
860 let mut rename_request =
861 cx.handle_request::<lsp::request::Rename, _, _>(move |url, params, _| async move {
862 Ok(Some(lsp::WorkspaceEdit {
863 changes: Some(
864 [(
865 url.clone(),
866 vec![
867 lsp::TextEdit::new(def_range, params.new_name.clone()),
868 lsp::TextEdit::new(tgt_range, params.new_name),
869 ],
870 )]
871 .into(),
872 ),
873 ..Default::default()
874 }))
875 });
876
877 cx.simulate_keystrokes("c d");
878 prepare_request.next().await.unwrap();
879 cx.simulate_input("after");
880 cx.simulate_keystrokes("enter");
881 rename_request.next().await.unwrap();
882 cx.assert_state("const afterˇ = 2; console.log(after)", Mode::Normal)
883}
884
885// TODO: this test is flaky on our linux CI machines
886#[cfg(target_os = "macos")]
887#[gpui::test]
888async fn test_remap(cx: &mut gpui::TestAppContext) {
889 let mut cx = VimTestContext::new(cx, true).await;
890
891 // test moving the cursor
892 cx.update(|cx| {
893 cx.bind_keys([KeyBinding::new(
894 "g z",
895 workspace::SendKeystrokes("l l l l".to_string()),
896 None,
897 )])
898 });
899 cx.set_state("ˇ123456789", Mode::Normal);
900 cx.simulate_keystrokes("g z");
901 cx.assert_state("1234ˇ56789", Mode::Normal);
902
903 // test switching modes
904 cx.update(|cx| {
905 cx.bind_keys([KeyBinding::new(
906 "g y",
907 workspace::SendKeystrokes("i f o o escape l".to_string()),
908 None,
909 )])
910 });
911 cx.set_state("ˇ123456789", Mode::Normal);
912 cx.simulate_keystrokes("g y");
913 cx.assert_state("fooˇ123456789", Mode::Normal);
914
915 // test recursion
916 cx.update(|cx| {
917 cx.bind_keys([KeyBinding::new(
918 "g x",
919 workspace::SendKeystrokes("g z g y".to_string()),
920 None,
921 )])
922 });
923 cx.set_state("ˇ123456789", Mode::Normal);
924 cx.simulate_keystrokes("g x");
925 cx.assert_state("1234fooˇ56789", Mode::Normal);
926
927 cx.executor().allow_parking();
928
929 // test command
930 cx.update(|cx| {
931 cx.bind_keys([KeyBinding::new(
932 "g w",
933 workspace::SendKeystrokes(": j enter".to_string()),
934 None,
935 )])
936 });
937 cx.set_state("ˇ1234\n56789", Mode::Normal);
938 cx.simulate_keystrokes("g w");
939 cx.assert_state("1234ˇ 56789", Mode::Normal);
940
941 // test leaving command
942 cx.update(|cx| {
943 cx.bind_keys([KeyBinding::new(
944 "g u",
945 workspace::SendKeystrokes("g w g z".to_string()),
946 None,
947 )])
948 });
949 cx.set_state("ˇ1234\n56789", Mode::Normal);
950 cx.simulate_keystrokes("g u");
951 cx.assert_state("1234 567ˇ89", Mode::Normal);
952
953 // test leaving command
954 cx.update(|cx| {
955 cx.bind_keys([KeyBinding::new(
956 "g t",
957 workspace::SendKeystrokes("i space escape".to_string()),
958 None,
959 )])
960 });
961 cx.set_state("12ˇ34", Mode::Normal);
962 cx.simulate_keystrokes("g t");
963 cx.assert_state("12ˇ 34", Mode::Normal);
964}
965
966#[gpui::test]
967async fn test_undo(cx: &mut gpui::TestAppContext) {
968 let mut cx = NeovimBackedTestContext::new(cx).await;
969
970 cx.set_shared_state("hello quˇoel world").await;
971 cx.simulate_shared_keystrokes("v i w s c o escape u").await;
972 cx.shared_state().await.assert_eq("hello ˇquoel world");
973 cx.simulate_shared_keystrokes("ctrl-r").await;
974 cx.shared_state().await.assert_eq("hello ˇco world");
975 cx.simulate_shared_keystrokes("a o right l escape").await;
976 cx.shared_state().await.assert_eq("hello cooˇl world");
977 cx.simulate_shared_keystrokes("u").await;
978 cx.shared_state().await.assert_eq("hello cooˇ world");
979 cx.simulate_shared_keystrokes("u").await;
980 cx.shared_state().await.assert_eq("hello cˇo world");
981 cx.simulate_shared_keystrokes("u").await;
982 cx.shared_state().await.assert_eq("hello ˇquoel world");
983
984 cx.set_shared_state("hello quˇoel world").await;
985 cx.simulate_shared_keystrokes("v i w ~ u").await;
986 cx.shared_state().await.assert_eq("hello ˇquoel world");
987
988 cx.set_shared_state("\nhello quˇoel world\n").await;
989 cx.simulate_shared_keystrokes("shift-v s c escape u").await;
990 cx.shared_state().await.assert_eq("\nˇhello quoel world\n");
991
992 cx.set_shared_state(indoc! {"
993 ˇ1
994 2
995 3"})
996 .await;
997
998 cx.simulate_shared_keystrokes("ctrl-v shift-g ctrl-a").await;
999 cx.shared_state().await.assert_eq(indoc! {"
1000 ˇ2
1001 3
1002 4"});
1003
1004 cx.simulate_shared_keystrokes("u").await;
1005 cx.shared_state().await.assert_eq(indoc! {"
1006 ˇ1
1007 2
1008 3"});
1009}
1010
1011#[gpui::test]
1012async fn test_mouse_selection(cx: &mut TestAppContext) {
1013 let mut cx = VimTestContext::new(cx, true).await;
1014
1015 cx.set_state("ˇone two three", Mode::Normal);
1016
1017 let start_point = cx.pixel_position("one twˇo three");
1018 let end_point = cx.pixel_position("one ˇtwo three");
1019
1020 cx.simulate_mouse_down(start_point, MouseButton::Left, Modifiers::none());
1021 cx.simulate_mouse_move(end_point, MouseButton::Left, Modifiers::none());
1022 cx.simulate_mouse_up(end_point, MouseButton::Left, Modifiers::none());
1023
1024 cx.assert_state("one «ˇtwo» three", Mode::Visual)
1025}
1026
1027#[gpui::test]
1028async fn test_lowercase_marks(cx: &mut TestAppContext) {
1029 let mut cx = NeovimBackedTestContext::new(cx).await;
1030
1031 cx.set_shared_state("line one\nline ˇtwo\nline three").await;
1032 cx.simulate_shared_keystrokes("m a l ' a").await;
1033 cx.shared_state()
1034 .await
1035 .assert_eq("line one\nˇline two\nline three");
1036 cx.simulate_shared_keystrokes("` a").await;
1037 cx.shared_state()
1038 .await
1039 .assert_eq("line one\nline ˇtwo\nline three");
1040
1041 cx.simulate_shared_keystrokes("^ d ` a").await;
1042 cx.shared_state()
1043 .await
1044 .assert_eq("line one\nˇtwo\nline three");
1045}
1046
1047#[gpui::test]
1048async fn test_lt_gt_marks(cx: &mut TestAppContext) {
1049 let mut cx = NeovimBackedTestContext::new(cx).await;
1050
1051 cx.set_shared_state(indoc!(
1052 "
1053 Line one
1054 Line two
1055 Line ˇthree
1056 Line four
1057 Line five
1058 "
1059 ))
1060 .await;
1061
1062 cx.simulate_shared_keystrokes("v j escape k k").await;
1063
1064 cx.simulate_shared_keystrokes("' <").await;
1065 cx.shared_state().await.assert_eq(indoc! {"
1066 Line one
1067 Line two
1068 ˇLine three
1069 Line four
1070 Line five
1071 "});
1072
1073 cx.simulate_shared_keystrokes("` <").await;
1074 cx.shared_state().await.assert_eq(indoc! {"
1075 Line one
1076 Line two
1077 Line ˇthree
1078 Line four
1079 Line five
1080 "});
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
1092 cx.simulate_shared_keystrokes("` >").await;
1093 cx.shared_state().await.assert_eq(indoc! {"
1094 Line one
1095 Line two
1096 Line three
1097 Line ˇfour
1098 Line five
1099 "
1100 });
1101
1102 cx.simulate_shared_keystrokes("v i w o escape").await;
1103 cx.simulate_shared_keystrokes("` >").await;
1104 cx.shared_state().await.assert_eq(indoc! {"
1105 Line one
1106 Line two
1107 Line three
1108 Line fouˇr
1109 Line five
1110 "
1111 });
1112 cx.simulate_shared_keystrokes("` <").await;
1113 cx.shared_state().await.assert_eq(indoc! {"
1114 Line one
1115 Line two
1116 Line three
1117 Line ˇfour
1118 Line five
1119 "
1120 });
1121}
1122
1123#[gpui::test]
1124async fn test_caret_mark(cx: &mut TestAppContext) {
1125 let mut cx = NeovimBackedTestContext::new(cx).await;
1126
1127 cx.set_shared_state(indoc!(
1128 "
1129 Line one
1130 Line two
1131 Line three
1132 ˇLine four
1133 Line five
1134 "
1135 ))
1136 .await;
1137
1138 cx.simulate_shared_keystrokes("c w shift-s t r a i g h t space t h i n g escape j j")
1139 .await;
1140
1141 cx.simulate_shared_keystrokes("' ^").await;
1142 cx.shared_state().await.assert_eq(indoc! {"
1143 Line one
1144 Line two
1145 Line three
1146 ˇStraight thing four
1147 Line five
1148 "
1149 });
1150
1151 cx.simulate_shared_keystrokes("` ^").await;
1152 cx.shared_state().await.assert_eq(indoc! {"
1153 Line one
1154 Line two
1155 Line three
1156 Straight thingˇ four
1157 Line five
1158 "
1159 });
1160
1161 cx.simulate_shared_keystrokes("k a ! escape k g i ?").await;
1162 cx.shared_state().await.assert_eq(indoc! {"
1163 Line one
1164 Line two
1165 Line three!?ˇ
1166 Straight thing four
1167 Line five
1168 "
1169 });
1170}
1171
1172#[cfg(target_os = "macos")]
1173#[gpui::test]
1174async fn test_dw_eol(cx: &mut gpui::TestAppContext) {
1175 let mut cx = NeovimBackedTestContext::new(cx).await;
1176
1177 cx.set_shared_wrap(12).await;
1178 cx.set_shared_state("twelve ˇchar twelve char\ntwelve char")
1179 .await;
1180 cx.simulate_shared_keystrokes("d w").await;
1181 cx.shared_state()
1182 .await
1183 .assert_eq("twelve ˇtwelve char\ntwelve char");
1184}
1185
1186#[gpui::test]
1187async fn test_toggle_comments(cx: &mut gpui::TestAppContext) {
1188 let mut cx = VimTestContext::new(cx, true).await;
1189
1190 let language = std::sync::Arc::new(language::Language::new(
1191 language::LanguageConfig {
1192 line_comments: vec!["// ".into(), "//! ".into(), "/// ".into()],
1193 ..Default::default()
1194 },
1195 Some(language::tree_sitter_rust::LANGUAGE.into()),
1196 ));
1197 cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
1198
1199 // works in normal model
1200 cx.set_state(
1201 indoc! {"
1202 ˇone
1203 two
1204 three
1205 "},
1206 Mode::Normal,
1207 );
1208 cx.simulate_keystrokes("g c c");
1209 cx.assert_state(
1210 indoc! {"
1211 // ˇone
1212 two
1213 three
1214 "},
1215 Mode::Normal,
1216 );
1217
1218 // works in visual mode
1219 cx.simulate_keystrokes("v j g c");
1220 cx.assert_state(
1221 indoc! {"
1222 // // ˇone
1223 // two
1224 three
1225 "},
1226 Mode::Normal,
1227 );
1228
1229 // works in visual line mode
1230 cx.simulate_keystrokes("shift-v j g c");
1231 cx.assert_state(
1232 indoc! {"
1233 // ˇone
1234 two
1235 three
1236 "},
1237 Mode::Normal,
1238 );
1239
1240 // works with count
1241 cx.simulate_keystrokes("g c 2 j");
1242 cx.assert_state(
1243 indoc! {"
1244 // // ˇone
1245 // two
1246 // three
1247 "},
1248 Mode::Normal,
1249 );
1250
1251 // works with motion object
1252 cx.simulate_keystrokes("shift-g");
1253 cx.simulate_keystrokes("g c g g");
1254 cx.assert_state(
1255 indoc! {"
1256 // one
1257 two
1258 three
1259 ˇ"},
1260 Mode::Normal,
1261 );
1262}
1263
1264#[gpui::test]
1265async fn test_find_multibyte(cx: &mut gpui::TestAppContext) {
1266 let mut cx = NeovimBackedTestContext::new(cx).await;
1267
1268 cx.set_shared_state(r#"<label for="guests">ˇPočet hostů</label>"#)
1269 .await;
1270
1271 cx.simulate_shared_keystrokes("c t < o escape").await;
1272 cx.shared_state()
1273 .await
1274 .assert_eq(r#"<label for="guests">ˇo</label>"#);
1275}
1276
1277#[gpui::test]
1278async fn test_plus_minus(cx: &mut gpui::TestAppContext) {
1279 let mut cx = NeovimBackedTestContext::new(cx).await;
1280
1281 cx.set_shared_state(indoc! {
1282 "one
1283 two
1284 thrˇee
1285 "})
1286 .await;
1287
1288 cx.simulate_shared_keystrokes("-").await;
1289 cx.shared_state().await.assert_matches();
1290 cx.simulate_shared_keystrokes("-").await;
1291 cx.shared_state().await.assert_matches();
1292 cx.simulate_shared_keystrokes("+").await;
1293 cx.shared_state().await.assert_matches();
1294}
1295
1296#[gpui::test]
1297async fn test_command_alias(cx: &mut gpui::TestAppContext) {
1298 let mut cx = VimTestContext::new(cx, true).await;
1299 cx.update_global(|store: &mut SettingsStore, cx| {
1300 store.update_user_settings::<WorkspaceSettings>(cx, |s| {
1301 let mut aliases = HashMap::default();
1302 aliases.insert("Q".to_string(), "upper".to_string());
1303 s.command_aliases = Some(aliases)
1304 });
1305 });
1306
1307 cx.set_state("ˇhello world", Mode::Normal);
1308 cx.simulate_keystrokes(": Q");
1309 cx.set_state("ˇHello world", Mode::Normal);
1310}
1311
1312#[gpui::test]
1313async fn test_remap_adjacent_dog_cat(cx: &mut gpui::TestAppContext) {
1314 let mut cx = NeovimBackedTestContext::new(cx).await;
1315 cx.update(|cx| {
1316 cx.bind_keys([
1317 KeyBinding::new(
1318 "d o g",
1319 workspace::SendKeystrokes("🐶".to_string()),
1320 Some("vim_mode == insert"),
1321 ),
1322 KeyBinding::new(
1323 "c a t",
1324 workspace::SendKeystrokes("🐱".to_string()),
1325 Some("vim_mode == insert"),
1326 ),
1327 ])
1328 });
1329 cx.neovim.exec("imap dog 🐶").await;
1330 cx.neovim.exec("imap cat 🐱").await;
1331
1332 cx.set_shared_state("ˇ").await;
1333 cx.simulate_shared_keystrokes("i d o g").await;
1334 cx.shared_state().await.assert_eq("🐶ˇ");
1335
1336 cx.set_shared_state("ˇ").await;
1337 cx.simulate_shared_keystrokes("i d o d o g").await;
1338 cx.shared_state().await.assert_eq("do🐶ˇ");
1339
1340 cx.set_shared_state("ˇ").await;
1341 cx.simulate_shared_keystrokes("i d o c a t").await;
1342 cx.shared_state().await.assert_eq("do🐱ˇ");
1343}
1344
1345#[gpui::test]
1346async fn test_remap_nested_pineapple(cx: &mut gpui::TestAppContext) {
1347 let mut cx = NeovimBackedTestContext::new(cx).await;
1348 cx.update(|cx| {
1349 cx.bind_keys([
1350 KeyBinding::new(
1351 "p i n",
1352 workspace::SendKeystrokes("📌".to_string()),
1353 Some("vim_mode == insert"),
1354 ),
1355 KeyBinding::new(
1356 "p i n e",
1357 workspace::SendKeystrokes("🌲".to_string()),
1358 Some("vim_mode == insert"),
1359 ),
1360 KeyBinding::new(
1361 "p i n e a p p l e",
1362 workspace::SendKeystrokes("🍍".to_string()),
1363 Some("vim_mode == insert"),
1364 ),
1365 ])
1366 });
1367 cx.neovim.exec("imap pin 📌").await;
1368 cx.neovim.exec("imap pine 🌲").await;
1369 cx.neovim.exec("imap pineapple 🍍").await;
1370
1371 cx.set_shared_state("ˇ").await;
1372 cx.simulate_shared_keystrokes("i p i n").await;
1373 cx.executor().advance_clock(Duration::from_millis(1000));
1374 cx.run_until_parked();
1375 cx.shared_state().await.assert_eq("📌ˇ");
1376
1377 cx.set_shared_state("ˇ").await;
1378 cx.simulate_shared_keystrokes("i p i n e").await;
1379 cx.executor().advance_clock(Duration::from_millis(1000));
1380 cx.run_until_parked();
1381 cx.shared_state().await.assert_eq("🌲ˇ");
1382
1383 cx.set_shared_state("ˇ").await;
1384 cx.simulate_shared_keystrokes("i p i n e a p p l e").await;
1385 cx.shared_state().await.assert_eq("🍍ˇ");
1386}
1387
1388#[gpui::test]
1389async fn test_escape_while_waiting(cx: &mut gpui::TestAppContext) {
1390 let mut cx = NeovimBackedTestContext::new(cx).await;
1391 cx.set_shared_state("ˇhi").await;
1392 cx.simulate_shared_keystrokes("\" + escape x").await;
1393 cx.shared_state().await.assert_eq("ˇi");
1394}
1395
1396#[gpui::test]
1397async fn test_ctrl_w_override(cx: &mut gpui::TestAppContext) {
1398 let mut cx = NeovimBackedTestContext::new(cx).await;
1399 cx.update(|cx| {
1400 cx.bind_keys([KeyBinding::new("ctrl-w", DeleteLine, None)]);
1401 });
1402 cx.neovim.exec("map <c-w> D").await;
1403 cx.set_shared_state("ˇhi").await;
1404 cx.simulate_shared_keystrokes("ctrl-w").await;
1405 cx.shared_state().await.assert_eq("ˇ");
1406}
1407
1408#[gpui::test]
1409async fn test_visual_indent_count(cx: &mut gpui::TestAppContext) {
1410 let mut cx = VimTestContext::new(cx, true).await;
1411 cx.set_state("ˇhi", Mode::Normal);
1412 cx.simulate_keystrokes("shift-v 3 >");
1413 cx.assert_state(" ˇhi", Mode::Normal);
1414 cx.simulate_keystrokes("shift-v 2 <");
1415 cx.assert_state(" ˇhi", Mode::Normal);
1416}
1417
1418#[gpui::test]
1419async fn test_record_replay_recursion(cx: &mut gpui::TestAppContext) {
1420 let mut cx = NeovimBackedTestContext::new(cx).await;
1421
1422 cx.set_shared_state("ˇhello world").await;
1423 cx.simulate_shared_keystrokes(">").await;
1424 cx.simulate_shared_keystrokes(".").await;
1425 cx.simulate_shared_keystrokes(".").await;
1426 cx.simulate_shared_keystrokes(".").await;
1427 cx.shared_state().await.assert_eq("ˇhello world");
1428}
1429
1430#[gpui::test]
1431async fn test_blackhole_register(cx: &mut gpui::TestAppContext) {
1432 let mut cx = NeovimBackedTestContext::new(cx).await;
1433
1434 cx.set_shared_state("ˇhello world").await;
1435 cx.simulate_shared_keystrokes("d i w \" _ d a w").await;
1436 cx.simulate_shared_keystrokes("p").await;
1437 cx.shared_state().await.assert_eq("hellˇo");
1438}
1439
1440#[gpui::test]
1441async fn test_sentence_backwards(cx: &mut gpui::TestAppContext) {
1442 let mut cx = NeovimBackedTestContext::new(cx).await;
1443
1444 cx.set_shared_state("one\n\ntwo\nthree\nˇ\nfour").await;
1445 cx.simulate_shared_keystrokes("(").await;
1446 cx.shared_state()
1447 .await
1448 .assert_eq("one\n\nˇtwo\nthree\n\nfour");
1449
1450 cx.set_shared_state("hello.\n\n\nworˇld.").await;
1451 cx.simulate_shared_keystrokes("(").await;
1452 cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1453 cx.simulate_shared_keystrokes("(").await;
1454 cx.shared_state().await.assert_eq("hello.\n\nˇ\nworld.");
1455 cx.simulate_shared_keystrokes("(").await;
1456 cx.shared_state().await.assert_eq("ˇhello.\n\n\nworld.");
1457
1458 cx.set_shared_state("hello. worlˇd.").await;
1459 cx.simulate_shared_keystrokes("(").await;
1460 cx.shared_state().await.assert_eq("hello. ˇworld.");
1461 cx.simulate_shared_keystrokes("(").await;
1462 cx.shared_state().await.assert_eq("ˇhello. world.");
1463
1464 cx.set_shared_state(". helˇlo.").await;
1465 cx.simulate_shared_keystrokes("(").await;
1466 cx.shared_state().await.assert_eq(". ˇhello.");
1467 cx.simulate_shared_keystrokes("(").await;
1468 cx.shared_state().await.assert_eq(". ˇhello.");
1469
1470 cx.set_shared_state(indoc! {
1471 "{
1472 hello_world();
1473 ˇ}"
1474 })
1475 .await;
1476 cx.simulate_shared_keystrokes("(").await;
1477 cx.shared_state().await.assert_eq(indoc! {
1478 "ˇ{
1479 hello_world();
1480 }"
1481 });
1482
1483 cx.set_shared_state(indoc! {
1484 "Hello! World..?
1485
1486 \tHello! World... ˇ"
1487 })
1488 .await;
1489 cx.simulate_shared_keystrokes("(").await;
1490 cx.shared_state().await.assert_eq(indoc! {
1491 "Hello! World..?
1492
1493 \tHello! ˇWorld... "
1494 });
1495 cx.simulate_shared_keystrokes("(").await;
1496 cx.shared_state().await.assert_eq(indoc! {
1497 "Hello! World..?
1498
1499 \tˇHello! World... "
1500 });
1501 cx.simulate_shared_keystrokes("(").await;
1502 cx.shared_state().await.assert_eq(indoc! {
1503 "Hello! World..?
1504 ˇ
1505 \tHello! World... "
1506 });
1507 cx.simulate_shared_keystrokes("(").await;
1508 cx.shared_state().await.assert_eq(indoc! {
1509 "Hello! ˇWorld..?
1510
1511 \tHello! World... "
1512 });
1513}
1514
1515#[gpui::test]
1516async fn test_sentence_forwards(cx: &mut gpui::TestAppContext) {
1517 let mut cx = NeovimBackedTestContext::new(cx).await;
1518
1519 cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1520 cx.simulate_shared_keystrokes(")").await;
1521 cx.shared_state().await.assert_eq("hello.\nˇ\n\nworld.");
1522 cx.simulate_shared_keystrokes(")").await;
1523 cx.shared_state().await.assert_eq("hello.\n\n\nˇworld.");
1524 cx.simulate_shared_keystrokes(")").await;
1525 cx.shared_state().await.assert_eq("hello.\n\n\nworldˇ.");
1526
1527 cx.set_shared_state("helˇlo.\n\n\nworld.").await;
1528}