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