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