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(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(edits, cx);
173 });
174 });
175 });
176}
177
178#[cfg(test)]
179mod test {
180 use indoc::indoc;
181 use util::test::marked_text;
182
183 use crate::{
184 state::{
185 Mode::{self, *},
186 Namespace, Operator,
187 },
188 vim_test_context::VimTestContext,
189 };
190
191 #[gpui::test]
192 async fn test_h(cx: &mut gpui::TestAppContext) {
193 let cx = VimTestContext::new(cx, true).await;
194 let mut cx = cx.binding(["h"]);
195 cx.assert("The q|uick", "The |quick");
196 cx.assert("|The quick", "|The quick");
197 cx.assert(
198 indoc! {"
199 The quick
200 |brown"},
201 indoc! {"
202 The quick
203 |brown"},
204 );
205 }
206
207 #[gpui::test]
208 async fn test_backspace(cx: &mut gpui::TestAppContext) {
209 let cx = VimTestContext::new(cx, true).await;
210 let mut cx = cx.binding(["backspace"]);
211 cx.assert("The q|uick", "The |quick");
212 cx.assert("|The quick", "|The quick");
213 cx.assert(
214 indoc! {"
215 The quick
216 |brown"},
217 indoc! {"
218 The quick
219 |brown"},
220 );
221 }
222
223 #[gpui::test]
224 async fn test_j(cx: &mut gpui::TestAppContext) {
225 let cx = VimTestContext::new(cx, true).await;
226 let mut cx = cx.binding(["j"]);
227 cx.assert(
228 indoc! {"
229 The |quick
230 brown fox"},
231 indoc! {"
232 The quick
233 brow|n fox"},
234 );
235 cx.assert(
236 indoc! {"
237 The quick
238 brow|n fox"},
239 indoc! {"
240 The quick
241 brow|n fox"},
242 );
243 cx.assert(
244 indoc! {"
245 The quic|k
246 brown"},
247 indoc! {"
248 The quick
249 brow|n"},
250 );
251 cx.assert(
252 indoc! {"
253 The quick
254 |brown"},
255 indoc! {"
256 The quick
257 |brown"},
258 );
259 }
260
261 #[gpui::test]
262 async fn test_k(cx: &mut gpui::TestAppContext) {
263 let cx = VimTestContext::new(cx, true).await;
264 let mut cx = cx.binding(["k"]);
265 cx.assert(
266 indoc! {"
267 The |quick
268 brown fox"},
269 indoc! {"
270 The |quick
271 brown fox"},
272 );
273 cx.assert(
274 indoc! {"
275 The quick
276 brow|n fox"},
277 indoc! {"
278 The |quick
279 brown fox"},
280 );
281 cx.assert(
282 indoc! {"
283 The
284 quic|k"},
285 indoc! {"
286 Th|e
287 quick"},
288 );
289 }
290
291 #[gpui::test]
292 async fn test_l(cx: &mut gpui::TestAppContext) {
293 let cx = VimTestContext::new(cx, true).await;
294 let mut cx = cx.binding(["l"]);
295 cx.assert("The q|uick", "The qu|ick");
296 cx.assert("The quic|k", "The quic|k");
297 cx.assert(
298 indoc! {"
299 The quic|k
300 brown"},
301 indoc! {"
302 The quic|k
303 brown"},
304 );
305 }
306
307 #[gpui::test]
308 async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
309 let cx = VimTestContext::new(cx, true).await;
310 let mut cx = cx.binding(["shift-$"]);
311 cx.assert("T|est test", "Test tes|t");
312 cx.assert("Test tes|t", "Test tes|t");
313 cx.assert(
314 indoc! {"
315 The |quick
316 brown"},
317 indoc! {"
318 The quic|k
319 brown"},
320 );
321 cx.assert(
322 indoc! {"
323 The quic|k
324 brown"},
325 indoc! {"
326 The quic|k
327 brown"},
328 );
329
330 let mut cx = cx.binding(["0"]);
331 cx.assert("Test |test", "|Test test");
332 cx.assert("|Test test", "|Test test");
333 cx.assert(
334 indoc! {"
335 The |quick
336 brown"},
337 indoc! {"
338 |The quick
339 brown"},
340 );
341 cx.assert(
342 indoc! {"
343 |The quick
344 brown"},
345 indoc! {"
346 |The quick
347 brown"},
348 );
349 }
350
351 #[gpui::test]
352 async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
353 let cx = VimTestContext::new(cx, true).await;
354 let mut cx = cx.binding(["shift-G"]);
355
356 cx.assert(
357 indoc! {"
358 The |quick
359
360 brown fox jumps
361 over the lazy dog"},
362 indoc! {"
363 The quick
364
365 brown fox jumps
366 over| the lazy dog"},
367 );
368 cx.assert(
369 indoc! {"
370 The quick
371
372 brown fox jumps
373 over| the lazy dog"},
374 indoc! {"
375 The quick
376
377 brown fox jumps
378 over| the lazy dog"},
379 );
380 cx.assert(
381 indoc! {"
382 The qui|ck
383
384 brown"},
385 indoc! {"
386 The quick
387
388 brow|n"},
389 );
390 cx.assert(
391 indoc! {"
392 The qui|ck
393
394 "},
395 indoc! {"
396 The quick
397
398 |"},
399 );
400 }
401
402 #[gpui::test]
403 async fn test_w(cx: &mut gpui::TestAppContext) {
404 let mut cx = VimTestContext::new(cx, true).await;
405 let (_, cursor_offsets) = marked_text(indoc! {"
406 The |quick|-|brown
407 |
408 |
409 |fox_jumps |over
410 |th||e"});
411 cx.set_state(
412 indoc! {"
413 |The quick-brown
414
415
416 fox_jumps over
417 the"},
418 Mode::Normal,
419 );
420
421 for cursor_offset in cursor_offsets {
422 cx.simulate_keystroke("w");
423 cx.assert_newest_selection_head_offset(cursor_offset);
424 }
425
426 // Reset and test ignoring punctuation
427 let (_, cursor_offsets) = marked_text(indoc! {"
428 The |quick-brown
429 |
430 |
431 |fox_jumps |over
432 |th||e"});
433 cx.set_state(
434 indoc! {"
435 |The quick-brown
436
437
438 fox_jumps over
439 the"},
440 Mode::Normal,
441 );
442
443 for cursor_offset in cursor_offsets {
444 cx.simulate_keystroke("shift-W");
445 cx.assert_newest_selection_head_offset(cursor_offset);
446 }
447 }
448
449 #[gpui::test]
450 async fn test_e(cx: &mut gpui::TestAppContext) {
451 let mut cx = VimTestContext::new(cx, true).await;
452 let (_, cursor_offsets) = marked_text(indoc! {"
453 Th|e quic|k|-brow|n
454
455
456 fox_jump|s ove|r
457 th|e"});
458 cx.set_state(
459 indoc! {"
460 |The quick-brown
461
462
463 fox_jumps over
464 the"},
465 Mode::Normal,
466 );
467
468 for cursor_offset in cursor_offsets {
469 cx.simulate_keystroke("e");
470 cx.assert_newest_selection_head_offset(cursor_offset);
471 }
472
473 // Reset and test ignoring punctuation
474 let (_, cursor_offsets) = marked_text(indoc! {"
475 Th|e quick-brow|n
476
477
478 fox_jump|s ove|r
479 th||e"});
480 cx.set_state(
481 indoc! {"
482 |The quick-brown
483
484
485 fox_jumps over
486 the"},
487 Mode::Normal,
488 );
489 for cursor_offset in cursor_offsets {
490 cx.simulate_keystroke("shift-E");
491 cx.assert_newest_selection_head_offset(cursor_offset);
492 }
493 }
494
495 #[gpui::test]
496 async fn test_b(cx: &mut gpui::TestAppContext) {
497 let mut cx = VimTestContext::new(cx, true).await;
498 let (_, cursor_offsets) = marked_text(indoc! {"
499 ||The |quick|-|brown
500 |
501 |
502 |fox_jumps |over
503 |the"});
504 cx.set_state(
505 indoc! {"
506 The quick-brown
507
508
509 fox_jumps over
510 th|e"},
511 Mode::Normal,
512 );
513
514 for cursor_offset in cursor_offsets.into_iter().rev() {
515 cx.simulate_keystroke("b");
516 cx.assert_newest_selection_head_offset(cursor_offset);
517 }
518
519 // Reset and test ignoring punctuation
520 let (_, cursor_offsets) = marked_text(indoc! {"
521 ||The |quick-brown
522 |
523 |
524 |fox_jumps |over
525 |the"});
526 cx.set_state(
527 indoc! {"
528 The quick-brown
529
530
531 fox_jumps over
532 th|e"},
533 Mode::Normal,
534 );
535 for cursor_offset in cursor_offsets.into_iter().rev() {
536 cx.simulate_keystroke("shift-B");
537 cx.assert_newest_selection_head_offset(cursor_offset);
538 }
539 }
540
541 #[gpui::test]
542 async fn test_g_prefix_and_abort(cx: &mut gpui::TestAppContext) {
543 let mut cx = VimTestContext::new(cx, true).await;
544
545 // Can abort with escape to get back to normal mode
546 cx.simulate_keystroke("g");
547 assert_eq!(cx.mode(), Normal);
548 assert_eq!(
549 cx.active_operator(),
550 Some(Operator::Namespace(Namespace::G))
551 );
552 cx.simulate_keystroke("escape");
553 assert_eq!(cx.mode(), Normal);
554 assert_eq!(cx.active_operator(), None);
555 }
556
557 #[gpui::test]
558 async fn test_gg(cx: &mut gpui::TestAppContext) {
559 let cx = VimTestContext::new(cx, true).await;
560 let mut cx = cx.binding(["g", "g"]);
561 cx.assert(
562 indoc! {"
563 The quick
564
565 brown fox jumps
566 over |the lazy dog"},
567 indoc! {"
568 The q|uick
569
570 brown fox jumps
571 over the lazy dog"},
572 );
573 cx.assert(
574 indoc! {"
575 The q|uick
576
577 brown fox jumps
578 over the lazy dog"},
579 indoc! {"
580 The q|uick
581
582 brown fox jumps
583 over the lazy dog"},
584 );
585 cx.assert(
586 indoc! {"
587 The quick
588
589 brown fox jumps
590 over the la|zy dog"},
591 indoc! {"
592 The quic|k
593
594 brown fox jumps
595 over the lazy dog"},
596 );
597 cx.assert(
598 indoc! {"
599
600
601 brown fox jumps
602 over the la|zy dog"},
603 indoc! {"
604 |
605
606 brown fox jumps
607 over the lazy dog"},
608 );
609 }
610
611 #[gpui::test]
612 async fn test_a(cx: &mut gpui::TestAppContext) {
613 let cx = VimTestContext::new(cx, true).await;
614 let mut cx = cx.binding(["a"]).mode_after(Mode::Insert);
615
616 cx.assert("The q|uick", "The qu|ick");
617 cx.assert("The quic|k", "The quick|");
618 }
619
620 #[gpui::test]
621 async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
622 let cx = VimTestContext::new(cx, true).await;
623 let mut cx = cx.binding(["shift-A"]).mode_after(Mode::Insert);
624 cx.assert("The q|uick", "The quick|");
625 cx.assert("The q|uick ", "The quick |");
626 cx.assert("|", "|");
627 cx.assert(
628 indoc! {"
629 The q|uick
630 brown fox"},
631 indoc! {"
632 The quick|
633 brown fox"},
634 );
635 cx.assert(
636 indoc! {"
637 |
638 The quick"},
639 indoc! {"
640 |
641 The quick"},
642 );
643 }
644
645 #[gpui::test]
646 async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
647 let cx = VimTestContext::new(cx, true).await;
648 let mut cx = cx.binding(["shift-^"]);
649 cx.assert("The q|uick", "|The quick");
650 cx.assert(" The q|uick", " |The quick");
651 cx.assert("|", "|");
652 cx.assert(
653 indoc! {"
654 The q|uick
655 brown fox"},
656 indoc! {"
657 |The quick
658 brown fox"},
659 );
660 cx.assert(
661 indoc! {"
662 |
663 The quick"},
664 indoc! {"
665 |
666 The quick"},
667 );
668 cx.assert(
669 indoc! {"
670 |
671 The quick"},
672 indoc! {"
673 |
674 The quick"},
675 );
676 }
677
678 #[gpui::test]
679 async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
680 let cx = VimTestContext::new(cx, true).await;
681 let mut cx = cx.binding(["shift-I"]).mode_after(Mode::Insert);
682 cx.assert("The q|uick", "|The quick");
683 cx.assert(" The q|uick", " |The quick");
684 cx.assert("|", "|");
685 cx.assert(
686 indoc! {"
687 The q|uick
688 brown fox"},
689 indoc! {"
690 |The quick
691 brown fox"},
692 );
693 cx.assert(
694 indoc! {"
695 |
696 The quick"},
697 indoc! {"
698 |
699 The quick"},
700 );
701 }
702
703 #[gpui::test]
704 async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
705 let cx = VimTestContext::new(cx, true).await;
706 let mut cx = cx.binding(["shift-D"]);
707 cx.assert(
708 indoc! {"
709 The q|uick
710 brown fox"},
711 indoc! {"
712 The |q
713 brown fox"},
714 );
715 cx.assert(
716 indoc! {"
717 The quick
718 |
719 brown fox"},
720 indoc! {"
721 The quick
722 |
723 brown fox"},
724 );
725 }
726
727 #[gpui::test]
728 async fn test_x(cx: &mut gpui::TestAppContext) {
729 let cx = VimTestContext::new(cx, true).await;
730 let mut cx = cx.binding(["x"]);
731 cx.assert("|Test", "|est");
732 cx.assert("Te|st", "Te|t");
733 cx.assert("Tes|t", "Te|s");
734 cx.assert(
735 indoc! {"
736 Tes|t
737 test"},
738 indoc! {"
739 Te|s
740 test"},
741 );
742 }
743
744 #[gpui::test]
745 async fn test_delete_left(cx: &mut gpui::TestAppContext) {
746 let cx = VimTestContext::new(cx, true).await;
747 let mut cx = cx.binding(["shift-X"]);
748 cx.assert("Te|st", "T|st");
749 cx.assert("T|est", "|est");
750 cx.assert("|Test", "|Test");
751 cx.assert(
752 indoc! {"
753 Test
754 |test"},
755 indoc! {"
756 Test
757 |test"},
758 );
759 }
760
761 #[gpui::test]
762 async fn test_o(cx: &mut gpui::TestAppContext) {
763 let cx = VimTestContext::new(cx, true).await;
764 let mut cx = cx.binding(["o"]).mode_after(Mode::Insert);
765
766 cx.assert(
767 "|",
768 indoc! {"
769
770 |"},
771 );
772 cx.assert(
773 "The |quick",
774 indoc! {"
775 The quick
776 |"},
777 );
778 cx.assert(
779 indoc! {"
780 The quick
781 brown |fox
782 jumps over"},
783 indoc! {"
784 The quick
785 brown fox
786 |
787 jumps over"},
788 );
789 cx.assert(
790 indoc! {"
791 The quick
792 brown fox
793 jumps |over"},
794 indoc! {"
795 The quick
796 brown fox
797 jumps over
798 |"},
799 );
800 cx.assert(
801 indoc! {"
802 The q|uick
803 brown fox
804 jumps over"},
805 indoc! {"
806 The quick
807 |
808 brown fox
809 jumps over"},
810 );
811 cx.assert(
812 indoc! {"
813 The quick
814 |
815 brown fox"},
816 indoc! {"
817 The quick
818
819 |
820 brown fox"},
821 );
822 cx.assert(
823 indoc! {"
824 fn test() {
825 println!(|);
826 }"},
827 indoc! {"
828 fn test() {
829 println!();
830 |
831 }"},
832 );
833 cx.assert(
834 indoc! {"
835 fn test(|) {
836 println!();
837 }"},
838 indoc! {"
839 fn test() {
840 |
841 println!();
842 }"},
843 );
844 }
845
846 #[gpui::test]
847 async fn test_insert_line_above(cx: &mut gpui::TestAppContext) {
848 let cx = VimTestContext::new(cx, true).await;
849 let mut cx = cx.binding(["shift-O"]).mode_after(Mode::Insert);
850
851 cx.assert(
852 "|",
853 indoc! {"
854 |
855 "},
856 );
857 cx.assert(
858 "The |quick",
859 indoc! {"
860 |
861 The quick"},
862 );
863 cx.assert(
864 indoc! {"
865 The quick
866 brown |fox
867 jumps over"},
868 indoc! {"
869 The quick
870 |
871 brown fox
872 jumps over"},
873 );
874 cx.assert(
875 indoc! {"
876 The quick
877 brown fox
878 jumps |over"},
879 indoc! {"
880 The quick
881 brown fox
882 |
883 jumps over"},
884 );
885 cx.assert(
886 indoc! {"
887 The q|uick
888 brown fox
889 jumps over"},
890 indoc! {"
891 |
892 The quick
893 brown fox
894 jumps over"},
895 );
896 cx.assert(
897 indoc! {"
898 The quick
899 |
900 brown fox"},
901 indoc! {"
902 The quick
903 |
904
905 brown fox"},
906 );
907 cx.assert(
908 indoc! {"
909 fn test() {
910 println!(|);
911 }"},
912 indoc! {"
913 fn test() {
914 |
915 println!();
916 }"},
917 );
918 cx.assert(
919 indoc! {"
920 fn test(|) {
921 println!();
922 }"},
923 indoc! {"
924 |
925 fn test() {
926 println!();
927 }"},
928 );
929 }
930
931 #[gpui::test]
932 async fn test_dd(cx: &mut gpui::TestAppContext) {
933 let cx = VimTestContext::new(cx, true).await;
934 let mut cx = cx.binding(["d", "d"]);
935
936 cx.assert("|", "|");
937 cx.assert("The |quick", "|");
938 cx.assert(
939 indoc! {"
940 The quick
941 brown |fox
942 jumps over"},
943 indoc! {"
944 The quick
945 jumps |over"},
946 );
947 cx.assert(
948 indoc! {"
949 The quick
950 brown fox
951 jumps |over"},
952 indoc! {"
953 The quick
954 brown |fox"},
955 );
956 cx.assert(
957 indoc! {"
958 The q|uick
959 brown fox
960 jumps over"},
961 indoc! {"
962 brown| fox
963 jumps over"},
964 );
965 cx.assert(
966 indoc! {"
967 The quick
968 |
969 brown fox"},
970 indoc! {"
971 The quick
972 |brown fox"},
973 );
974 }
975
976 #[gpui::test]
977 async fn test_cc(cx: &mut gpui::TestAppContext) {
978 let cx = VimTestContext::new(cx, true).await;
979 let mut cx = cx.binding(["c", "c"]).mode_after(Mode::Insert);
980
981 cx.assert("|", "|");
982 cx.assert("The |quick", "|");
983 cx.assert(
984 indoc! {"
985 The quick
986 brown |fox
987 jumps over"},
988 indoc! {"
989 The quick
990 |
991 jumps over"},
992 );
993 cx.assert(
994 indoc! {"
995 The quick
996 brown fox
997 jumps |over"},
998 indoc! {"
999 The quick
1000 brown fox
1001 |"},
1002 );
1003 cx.assert(
1004 indoc! {"
1005 The q|uick
1006 brown fox
1007 jumps over"},
1008 indoc! {"
1009 |
1010 brown fox
1011 jumps over"},
1012 );
1013 cx.assert(
1014 indoc! {"
1015 The quick
1016 |
1017 brown fox"},
1018 indoc! {"
1019 The quick
1020 |
1021 brown fox"},
1022 );
1023 }
1024}