1mod change;
2mod delete;
3
4use crate::{
5 motion::Motion,
6 state::{Mode, Operator},
7 Vim,
8};
9use change::init as change_init;
10use collections::HashSet;
11use editor::{Bias, DisplayPoint};
12use gpui::{actions, MutableAppContext, ViewContext};
13use language::SelectionGoal;
14use workspace::Workspace;
15
16use self::{change::change_over, delete::delete_over};
17
18actions!(
19 vim,
20 [
21 InsertAfter,
22 InsertFirstNonWhitespace,
23 InsertEndOfLine,
24 InsertLineAbove,
25 InsertLineBelow,
26 DeleteLeft,
27 DeleteRight,
28 ChangeToEndOfLine,
29 DeleteToEndOfLine,
30 ]
31);
32
33pub fn init(cx: &mut MutableAppContext) {
34 cx.add_action(insert_after);
35 cx.add_action(insert_first_non_whitespace);
36 cx.add_action(insert_end_of_line);
37 cx.add_action(insert_line_above);
38 cx.add_action(insert_line_below);
39 cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
40 Vim::update(cx, |vim, cx| {
41 delete_over(vim, Motion::Left, cx);
42 })
43 });
44 cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| {
45 Vim::update(cx, |vim, cx| {
46 delete_over(vim, Motion::Right, cx);
47 })
48 });
49 cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
50 Vim::update(cx, |vim, cx| {
51 change_over(vim, Motion::EndOfLine, cx);
52 })
53 });
54 cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
55 Vim::update(cx, |vim, cx| {
56 delete_over(vim, Motion::EndOfLine, cx);
57 })
58 });
59
60 change_init(cx);
61}
62
63pub fn normal_motion(motion: Motion, cx: &mut MutableAppContext) {
64 Vim::update(cx, |vim, cx| {
65 match vim.state.operator_stack.pop() {
66 None => move_cursor(vim, motion, cx),
67 Some(Operator::Change) => change_over(vim, motion, cx),
68 Some(Operator::Delete) => delete_over(vim, motion, cx),
69 Some(Operator::Namespace(_)) => {
70 // Can't do anything for a namespace operator. Ignoring
71 }
72 }
73 vim.clear_operator(cx);
74 });
75}
76
77fn move_cursor(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
78 vim.update_active_editor(cx, |editor, cx| {
79 editor.move_cursors(cx, |map, cursor, goal| motion.move_point(map, cursor, goal))
80 });
81}
82
83fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspace>) {
84 Vim::update(cx, |vim, cx| {
85 vim.switch_mode(Mode::Insert, cx);
86 vim.update_active_editor(cx, |editor, cx| {
87 editor.move_cursors(cx, |map, cursor, goal| {
88 Motion::Right.move_point(map, cursor, goal)
89 });
90 });
91 });
92}
93
94fn insert_first_non_whitespace(
95 _: &mut Workspace,
96 _: &InsertFirstNonWhitespace,
97 cx: &mut ViewContext<Workspace>,
98) {
99 Vim::update(cx, |vim, cx| {
100 vim.switch_mode(Mode::Insert, cx);
101 vim.update_active_editor(cx, |editor, cx| {
102 editor.move_cursors(cx, |map, cursor, goal| {
103 Motion::FirstNonWhitespace.move_point(map, cursor, goal)
104 });
105 });
106 });
107}
108
109fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewContext<Workspace>) {
110 Vim::update(cx, |vim, cx| {
111 vim.switch_mode(Mode::Insert, cx);
112 vim.update_active_editor(cx, |editor, cx| {
113 editor.move_cursors(cx, |map, cursor, goal| {
114 Motion::EndOfLine.move_point(map, cursor, goal)
115 });
116 });
117 });
118}
119
120fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContext<Workspace>) {
121 Vim::update(cx, |vim, cx| {
122 vim.switch_mode(Mode::Insert, cx);
123 vim.update_active_editor(cx, |editor, cx| {
124 editor.transact(cx, |editor, cx| {
125 let (map, old_selections) = editor.display_selections(cx);
126 let selection_start_rows: HashSet<u32> = old_selections
127 .into_iter()
128 .map(|selection| selection.start.row())
129 .collect();
130 let edits = selection_start_rows.into_iter().map(|row| {
131 let (indent, _) = map.line_indent(row);
132 let start_of_line = map
133 .clip_point(DisplayPoint::new(row, 0), Bias::Left)
134 .to_point(&map);
135 let mut new_text = " ".repeat(indent as usize);
136 new_text.push('\n');
137 (start_of_line..start_of_line, new_text)
138 });
139 editor.edit_with_autoindent(edits, cx);
140 editor.move_cursors(cx, |map, mut cursor, _| {
141 *cursor.row_mut() -= 1;
142 *cursor.column_mut() = map.line_len(cursor.row());
143 (map.clip_point(cursor, Bias::Left), SelectionGoal::None)
144 });
145 });
146 });
147 });
148}
149
150fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContext<Workspace>) {
151 Vim::update(cx, |vim, cx| {
152 vim.switch_mode(Mode::Insert, cx);
153 vim.update_active_editor(cx, |editor, cx| {
154 editor.transact(cx, |editor, cx| {
155 let (map, old_selections) = editor.display_selections(cx);
156 let selection_end_rows: HashSet<u32> = old_selections
157 .into_iter()
158 .map(|selection| selection.end.row())
159 .collect();
160 let edits = selection_end_rows.into_iter().map(|row| {
161 let (indent, _) = map.line_indent(row);
162 let end_of_line = map
163 .clip_point(DisplayPoint::new(row, map.line_len(row)), Bias::Left)
164 .to_point(&map);
165 let mut new_text = "\n".to_string();
166 new_text.push_str(&" ".repeat(indent as usize));
167 (end_of_line..end_of_line, new_text)
168 });
169 editor.move_cursors(cx, |map, cursor, goal| {
170 Motion::EndOfLine.move_point(map, cursor, goal)
171 });
172 editor.edit_with_autoindent(edits, cx);
173 });
174 });
175 });
176}
177
178#[cfg(test)]
179mod test {
180 use indoc::indoc;
181 use language::Selection;
182 use util::test::marked_text;
183
184 use crate::{
185 state::{
186 Mode::{self, *},
187 Namespace, Operator,
188 },
189 vim_test_context::VimTestContext,
190 };
191
192 #[gpui::test]
193 async fn test_h(cx: &mut gpui::TestAppContext) {
194 let cx = VimTestContext::new(cx, true).await;
195 let mut cx = cx.binding(["h"]);
196 cx.assert("The q|uick", "The |quick");
197 cx.assert("|The quick", "|The quick");
198 cx.assert(
199 indoc! {"
200 The quick
201 |brown"},
202 indoc! {"
203 The quick
204 |brown"},
205 );
206 }
207
208 #[gpui::test]
209 async fn test_backspace(cx: &mut gpui::TestAppContext) {
210 let cx = VimTestContext::new(cx, true).await;
211 let mut cx = cx.binding(["backspace"]);
212 cx.assert("The q|uick", "The |quick");
213 cx.assert("|The quick", "|The quick");
214 cx.assert(
215 indoc! {"
216 The quick
217 |brown"},
218 indoc! {"
219 The quick
220 |brown"},
221 );
222 }
223
224 #[gpui::test]
225 async fn test_j(cx: &mut gpui::TestAppContext) {
226 let cx = VimTestContext::new(cx, true).await;
227 let mut cx = cx.binding(["j"]);
228 cx.assert(
229 indoc! {"
230 The |quick
231 brown fox"},
232 indoc! {"
233 The quick
234 brow|n fox"},
235 );
236 cx.assert(
237 indoc! {"
238 The quick
239 brow|n fox"},
240 indoc! {"
241 The quick
242 brow|n fox"},
243 );
244 cx.assert(
245 indoc! {"
246 The quic|k
247 brown"},
248 indoc! {"
249 The quick
250 brow|n"},
251 );
252 cx.assert(
253 indoc! {"
254 The quick
255 |brown"},
256 indoc! {"
257 The quick
258 |brown"},
259 );
260 }
261
262 #[gpui::test]
263 async fn test_k(cx: &mut gpui::TestAppContext) {
264 let cx = VimTestContext::new(cx, true).await;
265 let mut cx = cx.binding(["k"]);
266 cx.assert(
267 indoc! {"
268 The |quick
269 brown fox"},
270 indoc! {"
271 The |quick
272 brown fox"},
273 );
274 cx.assert(
275 indoc! {"
276 The quick
277 brow|n fox"},
278 indoc! {"
279 The |quick
280 brown fox"},
281 );
282 cx.assert(
283 indoc! {"
284 The
285 quic|k"},
286 indoc! {"
287 Th|e
288 quick"},
289 );
290 }
291
292 #[gpui::test]
293 async fn test_l(cx: &mut gpui::TestAppContext) {
294 let cx = VimTestContext::new(cx, true).await;
295 let mut cx = cx.binding(["l"]);
296 cx.assert("The q|uick", "The qu|ick");
297 cx.assert("The quic|k", "The quic|k");
298 cx.assert(
299 indoc! {"
300 The quic|k
301 brown"},
302 indoc! {"
303 The quic|k
304 brown"},
305 );
306 }
307
308 #[gpui::test]
309 async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
310 let cx = VimTestContext::new(cx, true).await;
311 let mut cx = cx.binding(["shift-$"]);
312 cx.assert("T|est test", "Test tes|t");
313 cx.assert("Test tes|t", "Test tes|t");
314 cx.assert(
315 indoc! {"
316 The |quick
317 brown"},
318 indoc! {"
319 The quic|k
320 brown"},
321 );
322 cx.assert(
323 indoc! {"
324 The quic|k
325 brown"},
326 indoc! {"
327 The quic|k
328 brown"},
329 );
330
331 let mut cx = cx.binding(["0"]);
332 cx.assert("Test |test", "|Test test");
333 cx.assert("|Test test", "|Test test");
334 cx.assert(
335 indoc! {"
336 The |quick
337 brown"},
338 indoc! {"
339 |The quick
340 brown"},
341 );
342 cx.assert(
343 indoc! {"
344 |The quick
345 brown"},
346 indoc! {"
347 |The quick
348 brown"},
349 );
350 }
351
352 #[gpui::test]
353 async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
354 let cx = VimTestContext::new(cx, true).await;
355 let mut cx = cx.binding(["shift-G"]);
356
357 cx.assert(
358 indoc! {"
359 The |quick
360
361 brown fox jumps
362 over the lazy dog"},
363 indoc! {"
364 The quick
365
366 brown fox jumps
367 over| the lazy dog"},
368 );
369 cx.assert(
370 indoc! {"
371 The quick
372
373 brown fox jumps
374 over| the lazy dog"},
375 indoc! {"
376 The quick
377
378 brown fox jumps
379 over| the lazy dog"},
380 );
381 cx.assert(
382 indoc! {"
383 The qui|ck
384
385 brown"},
386 indoc! {"
387 The quick
388
389 brow|n"},
390 );
391 cx.assert(
392 indoc! {"
393 The qui|ck
394
395 "},
396 indoc! {"
397 The quick
398
399 |"},
400 );
401 }
402
403 #[gpui::test]
404 async fn test_w(cx: &mut gpui::TestAppContext) {
405 let mut cx = VimTestContext::new(cx, true).await;
406 let (_, cursor_offsets) = marked_text(indoc! {"
407 The |quick|-|brown
408 |
409 |
410 |fox_jumps |over
411 |th||e"});
412 cx.set_state(
413 indoc! {"
414 |The quick-brown
415
416
417 fox_jumps over
418 the"},
419 Mode::Normal,
420 );
421
422 for cursor_offset in cursor_offsets {
423 cx.simulate_keystroke("w");
424 cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
425 }
426
427 // Reset and test ignoring punctuation
428 let (_, cursor_offsets) = marked_text(indoc! {"
429 The |quick-brown
430 |
431 |
432 |fox_jumps |over
433 |th||e"});
434 cx.set_state(
435 indoc! {"
436 |The quick-brown
437
438
439 fox_jumps over
440 the"},
441 Mode::Normal,
442 );
443
444 for cursor_offset in cursor_offsets {
445 cx.simulate_keystroke("shift-W");
446 cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
447 }
448 }
449
450 #[gpui::test]
451 async fn test_e(cx: &mut gpui::TestAppContext) {
452 let mut cx = VimTestContext::new(cx, true).await;
453 let (_, cursor_offsets) = marked_text(indoc! {"
454 Th|e quic|k|-brow|n
455
456
457 fox_jump|s ove|r
458 th|e"});
459 cx.set_state(
460 indoc! {"
461 |The quick-brown
462
463
464 fox_jumps over
465 the"},
466 Mode::Normal,
467 );
468
469 for cursor_offset in cursor_offsets {
470 cx.simulate_keystroke("e");
471 cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
472 }
473
474 // Reset and test ignoring punctuation
475 let (_, cursor_offsets) = marked_text(indoc! {"
476 Th|e quick-brow|n
477
478
479 fox_jump|s ove|r
480 th||e"});
481 cx.set_state(
482 indoc! {"
483 |The quick-brown
484
485
486 fox_jumps over
487 the"},
488 Mode::Normal,
489 );
490 for cursor_offset in cursor_offsets {
491 cx.simulate_keystroke("shift-E");
492 cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
493 }
494 }
495
496 #[gpui::test]
497 async fn test_b(cx: &mut gpui::TestAppContext) {
498 let mut cx = VimTestContext::new(cx, true).await;
499 let (_, cursor_offsets) = marked_text(indoc! {"
500 ||The |quick|-|brown
501 |
502 |
503 |fox_jumps |over
504 |the"});
505 cx.set_state(
506 indoc! {"
507 The quick-brown
508
509
510 fox_jumps over
511 th|e"},
512 Mode::Normal,
513 );
514
515 for cursor_offset in cursor_offsets.into_iter().rev() {
516 cx.simulate_keystroke("b");
517 cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
518 }
519
520 // Reset and test ignoring punctuation
521 let (_, cursor_offsets) = marked_text(indoc! {"
522 ||The |quick-brown
523 |
524 |
525 |fox_jumps |over
526 |the"});
527 cx.set_state(
528 indoc! {"
529 The quick-brown
530
531
532 fox_jumps over
533 th|e"},
534 Mode::Normal,
535 );
536 for cursor_offset in cursor_offsets.into_iter().rev() {
537 cx.simulate_keystroke("shift-B");
538 cx.assert_editor_selections(vec![Selection::from_offset(cursor_offset)]);
539 }
540 }
541
542 #[gpui::test]
543 async fn test_g_prefix_and_abort(cx: &mut gpui::TestAppContext) {
544 let mut cx = VimTestContext::new(cx, true).await;
545
546 // Can abort with escape to get back to normal mode
547 cx.simulate_keystroke("g");
548 assert_eq!(cx.mode(), Normal);
549 assert_eq!(
550 cx.active_operator(),
551 Some(Operator::Namespace(Namespace::G))
552 );
553 cx.simulate_keystroke("escape");
554 assert_eq!(cx.mode(), Normal);
555 assert_eq!(cx.active_operator(), None);
556 }
557
558 #[gpui::test]
559 async fn test_gg(cx: &mut gpui::TestAppContext) {
560 let cx = VimTestContext::new(cx, true).await;
561 let mut cx = cx.binding(["g", "g"]);
562 cx.assert(
563 indoc! {"
564 The quick
565
566 brown fox jumps
567 over |the lazy dog"},
568 indoc! {"
569 The q|uick
570
571 brown fox jumps
572 over the lazy dog"},
573 );
574 cx.assert(
575 indoc! {"
576 The q|uick
577
578 brown fox jumps
579 over the lazy dog"},
580 indoc! {"
581 The q|uick
582
583 brown fox jumps
584 over the lazy dog"},
585 );
586 cx.assert(
587 indoc! {"
588 The quick
589
590 brown fox jumps
591 over the la|zy dog"},
592 indoc! {"
593 The quic|k
594
595 brown fox jumps
596 over the lazy dog"},
597 );
598 cx.assert(
599 indoc! {"
600
601
602 brown fox jumps
603 over the la|zy dog"},
604 indoc! {"
605 |
606
607 brown fox jumps
608 over the lazy dog"},
609 );
610 }
611
612 #[gpui::test]
613 async fn test_a(cx: &mut gpui::TestAppContext) {
614 let cx = VimTestContext::new(cx, true).await;
615 let mut cx = cx.binding(["a"]).mode_after(Mode::Insert);
616
617 cx.assert("The q|uick", "The qu|ick");
618 cx.assert("The quic|k", "The quick|");
619 }
620
621 #[gpui::test]
622 async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
623 let cx = VimTestContext::new(cx, true).await;
624 let mut cx = cx.binding(["shift-A"]).mode_after(Mode::Insert);
625 cx.assert("The q|uick", "The quick|");
626 cx.assert("The q|uick ", "The quick |");
627 cx.assert("|", "|");
628 cx.assert(
629 indoc! {"
630 The q|uick
631 brown fox"},
632 indoc! {"
633 The quick|
634 brown fox"},
635 );
636 cx.assert(
637 indoc! {"
638 |
639 The quick"},
640 indoc! {"
641 |
642 The quick"},
643 );
644 }
645
646 #[gpui::test]
647 async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
648 let cx = VimTestContext::new(cx, true).await;
649 let mut cx = cx.binding(["shift-^"]);
650 cx.assert("The q|uick", "|The quick");
651 cx.assert(" The q|uick", " |The quick");
652 cx.assert("|", "|");
653 cx.assert(
654 indoc! {"
655 The q|uick
656 brown fox"},
657 indoc! {"
658 |The quick
659 brown fox"},
660 );
661 cx.assert(
662 indoc! {"
663 |
664 The quick"},
665 indoc! {"
666 |
667 The quick"},
668 );
669 cx.assert(
670 indoc! {"
671 |
672 The quick"},
673 indoc! {"
674 |
675 The quick"},
676 );
677 }
678
679 #[gpui::test]
680 async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
681 let cx = VimTestContext::new(cx, true).await;
682 let mut cx = cx.binding(["shift-I"]).mode_after(Mode::Insert);
683 cx.assert("The q|uick", "|The quick");
684 cx.assert(" The q|uick", " |The quick");
685 cx.assert("|", "|");
686 cx.assert(
687 indoc! {"
688 The q|uick
689 brown fox"},
690 indoc! {"
691 |The quick
692 brown fox"},
693 );
694 cx.assert(
695 indoc! {"
696 |
697 The quick"},
698 indoc! {"
699 |
700 The quick"},
701 );
702 }
703
704 #[gpui::test]
705 async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
706 let cx = VimTestContext::new(cx, true).await;
707 let mut cx = cx.binding(["shift-D"]);
708 cx.assert(
709 indoc! {"
710 The q|uick
711 brown fox"},
712 indoc! {"
713 The |q
714 brown fox"},
715 );
716 cx.assert(
717 indoc! {"
718 The quick
719 |
720 brown fox"},
721 indoc! {"
722 The quick
723 |
724 brown fox"},
725 );
726 }
727
728 #[gpui::test]
729 async fn test_x(cx: &mut gpui::TestAppContext) {
730 let cx = VimTestContext::new(cx, true).await;
731 let mut cx = cx.binding(["x"]);
732 cx.assert("|Test", "|est");
733 cx.assert("Te|st", "Te|t");
734 cx.assert("Tes|t", "Te|s");
735 cx.assert(
736 indoc! {"
737 Tes|t
738 test"},
739 indoc! {"
740 Te|s
741 test"},
742 );
743 }
744
745 #[gpui::test]
746 async fn test_delete_left(cx: &mut gpui::TestAppContext) {
747 let cx = VimTestContext::new(cx, true).await;
748 let mut cx = cx.binding(["shift-X"]);
749 cx.assert("Te|st", "T|st");
750 cx.assert("T|est", "|est");
751 cx.assert("|Test", "|Test");
752 cx.assert(
753 indoc! {"
754 Test
755 |test"},
756 indoc! {"
757 Test
758 |test"},
759 );
760 }
761
762 #[gpui::test]
763 async fn test_o(cx: &mut gpui::TestAppContext) {
764 let cx = VimTestContext::new(cx, true).await;
765 let mut cx = cx.binding(["o"]).mode_after(Mode::Insert);
766
767 cx.assert(
768 "|",
769 indoc! {"
770
771 |"},
772 );
773 cx.assert(
774 "The |quick",
775 indoc! {"
776 The quick
777 |"},
778 );
779 cx.assert(
780 indoc! {"
781 The quick
782 brown |fox
783 jumps over"},
784 indoc! {"
785 The quick
786 brown fox
787 |
788 jumps over"},
789 );
790 cx.assert(
791 indoc! {"
792 The quick
793 brown fox
794 jumps |over"},
795 indoc! {"
796 The quick
797 brown fox
798 jumps over
799 |"},
800 );
801 cx.assert(
802 indoc! {"
803 The q|uick
804 brown fox
805 jumps over"},
806 indoc! {"
807 The quick
808 |
809 brown fox
810 jumps over"},
811 );
812 cx.assert(
813 indoc! {"
814 The quick
815 |
816 brown fox"},
817 indoc! {"
818 The quick
819
820 |
821 brown fox"},
822 );
823 cx.assert(
824 indoc! {"
825 fn test()
826 println!(|);"},
827 indoc! {"
828 fn test()
829 println!();
830 |"},
831 );
832 cx.assert(
833 indoc! {"
834 fn test(|)
835 println!();"},
836 indoc! {"
837 fn test()
838 |
839 println!();"},
840 );
841 }
842
843 #[gpui::test]
844 async fn test_insert_line_above(cx: &mut gpui::TestAppContext) {
845 let cx = VimTestContext::new(cx, true).await;
846 let mut cx = cx.binding(["shift-O"]).mode_after(Mode::Insert);
847
848 cx.assert(
849 "|",
850 indoc! {"
851 |
852 "},
853 );
854 cx.assert(
855 "The |quick",
856 indoc! {"
857 |
858 The quick"},
859 );
860 cx.assert(
861 indoc! {"
862 The quick
863 brown |fox
864 jumps over"},
865 indoc! {"
866 The quick
867 |
868 brown fox
869 jumps over"},
870 );
871 cx.assert(
872 indoc! {"
873 The quick
874 brown fox
875 jumps |over"},
876 indoc! {"
877 The quick
878 brown fox
879 |
880 jumps over"},
881 );
882 cx.assert(
883 indoc! {"
884 The q|uick
885 brown fox
886 jumps over"},
887 indoc! {"
888 |
889 The quick
890 brown fox
891 jumps over"},
892 );
893 cx.assert(
894 indoc! {"
895 The quick
896 |
897 brown fox"},
898 indoc! {"
899 The quick
900 |
901
902 brown fox"},
903 );
904 cx.assert(
905 indoc! {"
906 fn test()
907 println!(|);"},
908 indoc! {"
909 fn test()
910 |
911 println!();"},
912 );
913 cx.assert(
914 indoc! {"
915 fn test(|)
916 println!();"},
917 indoc! {"
918 |
919 fn test()
920 println!();"},
921 );
922 }
923
924 #[gpui::test]
925 async fn test_dd(cx: &mut gpui::TestAppContext) {
926 let cx = VimTestContext::new(cx, true).await;
927 let mut cx = cx.binding(["d", "d"]);
928
929 cx.assert("|", "|");
930 cx.assert("The |quick", "|");
931 cx.assert(
932 indoc! {"
933 The quick
934 brown |fox
935 jumps over"},
936 indoc! {"
937 The quick
938 jumps |over"},
939 );
940 cx.assert(
941 indoc! {"
942 The quick
943 brown fox
944 jumps |over"},
945 indoc! {"
946 The quick
947 brown |fox"},
948 );
949 cx.assert(
950 indoc! {"
951 The q|uick
952 brown fox
953 jumps over"},
954 indoc! {"
955 brown| fox
956 jumps over"},
957 );
958 cx.assert(
959 indoc! {"
960 The quick
961 |
962 brown fox"},
963 indoc! {"
964 The quick
965 |brown fox"},
966 );
967 }
968
969 #[gpui::test]
970 async fn test_cc(cx: &mut gpui::TestAppContext) {
971 let cx = VimTestContext::new(cx, true).await;
972 let mut cx = cx.binding(["c", "c"]).mode_after(Mode::Insert);
973
974 cx.assert("|", "|");
975 cx.assert("The |quick", "|");
976 cx.assert(
977 indoc! {"
978 The quick
979 brown |fox
980 jumps over"},
981 indoc! {"
982 The quick
983 |
984 jumps over"},
985 );
986 cx.assert(
987 indoc! {"
988 The quick
989 brown fox
990 jumps |over"},
991 indoc! {"
992 The quick
993 brown fox
994 |"},
995 );
996 cx.assert(
997 indoc! {"
998 The q|uick
999 brown fox
1000 jumps over"},
1001 indoc! {"
1002 |
1003 brown fox
1004 jumps over"},
1005 );
1006 cx.assert(
1007 indoc! {"
1008 The quick
1009 |
1010 brown fox"},
1011 indoc! {"
1012 The quick
1013 |
1014 brown fox"},
1015 );
1016 }
1017}