1mod case;
2mod change;
3mod delete;
4mod increment;
5mod paste;
6pub(crate) mod repeat;
7mod scroll;
8pub(crate) mod search;
9pub mod substitute;
10mod yank;
11
12use std::sync::Arc;
13
14use crate::{
15 motion::{self, first_non_whitespace, next_line_end, right, Motion},
16 object::Object,
17 state::{Mode, Operator},
18 Vim,
19};
20use collections::HashSet;
21use editor::scroll::autoscroll::Autoscroll;
22use editor::{Bias, DisplayPoint};
23use gpui::{actions, AppContext, ViewContext, WindowContext};
24use language::SelectionGoal;
25use log::error;
26use workspace::Workspace;
27
28use self::{
29 case::change_case,
30 change::{change_motion, change_object},
31 delete::{delete_motion, delete_object},
32 yank::{yank_motion, yank_object},
33};
34
35actions!(
36 InsertAfter,
37 InsertBefore,
38 InsertFirstNonWhitespace,
39 InsertEndOfLine,
40 InsertLineAbove,
41 InsertLineBelow,
42 DeleteLeft,
43 DeleteRight,
44 ChangeToEndOfLine,
45 DeleteToEndOfLine,
46 Yank,
47 YankLine,
48 ChangeCase,
49 JoinLines,
50);
51
52pub fn init(cx: &mut AppContext) {
53 paste::init(cx);
54 repeat::init(cx);
55 scroll::init(cx);
56 search::init(cx);
57 substitute::init(cx);
58 increment::init(cx);
59
60 // cx.add_action(insert_after);
61 // cx.add_action(insert_before);
62 // cx.add_action(insert_first_non_whitespace);
63 // cx.add_action(insert_end_of_line);
64 // cx.add_action(insert_line_above);
65 // cx.add_action(insert_line_below);
66 // cx.add_action(change_case);
67 // cx.add_action(yank_line);
68
69 // cx.add_action(|_: &mut Workspace, _: &DeleteLeft, cx| {
70 // Vim::update(cx, |vim, cx| {
71 // vim.record_current_action(cx);
72 // let times = vim.take_count(cx);
73 // delete_motion(vim, Motion::Left, times, cx);
74 // })
75 // });
76 // cx.add_action(|_: &mut Workspace, _: &DeleteRight, cx| {
77 // Vim::update(cx, |vim, cx| {
78 // vim.record_current_action(cx);
79 // let times = vim.take_count(cx);
80 // delete_motion(vim, Motion::Right, times, cx);
81 // })
82 // });
83 // cx.add_action(|_: &mut Workspace, _: &ChangeToEndOfLine, cx| {
84 // Vim::update(cx, |vim, cx| {
85 // vim.start_recording(cx);
86 // let times = vim.take_count(cx);
87 // change_motion(
88 // vim,
89 // Motion::EndOfLine {
90 // display_lines: false,
91 // },
92 // times,
93 // cx,
94 // );
95 // })
96 // });
97 // cx.add_action(|_: &mut Workspace, _: &DeleteToEndOfLine, cx| {
98 // Vim::update(cx, |vim, cx| {
99 // vim.record_current_action(cx);
100 // let times = vim.take_count(cx);
101 // delete_motion(
102 // vim,
103 // Motion::EndOfLine {
104 // display_lines: false,
105 // },
106 // times,
107 // cx,
108 // );
109 // })
110 // });
111 // cx.add_action(|_: &mut Workspace, _: &JoinLines, cx| {
112 // Vim::update(cx, |vim, cx| {
113 // vim.record_current_action(cx);
114 // let mut times = vim.take_count(cx).unwrap_or(1);
115 // if vim.state().mode.is_visual() {
116 // times = 1;
117 // } else if times > 1 {
118 // // 2J joins two lines together (same as J or 1J)
119 // times -= 1;
120 // }
121
122 // vim.update_active_editor(cx, |editor, cx| {
123 // editor.transact(cx, |editor, cx| {
124 // for _ in 0..times {
125 // editor.join_lines(&Default::default(), cx)
126 // }
127 // })
128 // })
129 // })
130 // })
131}
132
133pub fn normal_motion(
134 motion: Motion,
135 operator: Option<Operator>,
136 times: Option<usize>,
137 cx: &mut WindowContext,
138) {
139 Vim::update(cx, |vim, cx| {
140 match operator {
141 None => move_cursor(vim, motion, times, cx),
142 Some(Operator::Change) => change_motion(vim, motion, times, cx),
143 Some(Operator::Delete) => delete_motion(vim, motion, times, cx),
144 Some(Operator::Yank) => yank_motion(vim, motion, times, cx),
145 Some(operator) => {
146 // Can't do anything for text objects, Ignoring
147 error!("Unexpected normal mode motion operator: {:?}", operator)
148 }
149 }
150 });
151}
152
153pub fn normal_object(object: Object, cx: &mut WindowContext) {
154 Vim::update(cx, |vim, cx| {
155 match vim.maybe_pop_operator() {
156 Some(Operator::Object { around }) => match vim.maybe_pop_operator() {
157 Some(Operator::Change) => change_object(vim, object, around, cx),
158 Some(Operator::Delete) => delete_object(vim, object, around, cx),
159 Some(Operator::Yank) => yank_object(vim, object, around, cx),
160 _ => {
161 // Can't do anything for namespace operators. Ignoring
162 }
163 },
164 _ => {
165 // Can't do anything with change/delete/yank and text objects. Ignoring
166 }
167 }
168 vim.clear_operator(cx);
169 })
170}
171
172pub(crate) fn move_cursor(
173 vim: &mut Vim,
174 motion: Motion,
175 times: Option<usize>,
176 cx: &mut WindowContext,
177) {
178 vim.update_active_editor(cx, |editor, cx| {
179 let text_layout_details = editor.text_layout_details(cx);
180 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
181 s.move_cursors_with(|map, cursor, goal| {
182 motion
183 .move_point(map, cursor, goal, times, &text_layout_details)
184 .unwrap_or((cursor, goal))
185 })
186 })
187 });
188}
189
190fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspace>) {
191 Vim::update(cx, |vim, cx| {
192 vim.start_recording(cx);
193 vim.switch_mode(Mode::Insert, false, cx);
194 vim.update_active_editor(cx, |editor, cx| {
195 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
196 s.move_cursors_with(|map, cursor, _| (right(map, cursor, 1), SelectionGoal::None));
197 });
198 });
199 });
200}
201
202fn insert_before(_: &mut Workspace, _: &InsertBefore, cx: &mut ViewContext<Workspace>) {
203 Vim::update(cx, |vim, cx| {
204 vim.start_recording(cx);
205 vim.switch_mode(Mode::Insert, false, cx);
206 });
207}
208
209fn insert_first_non_whitespace(
210 _: &mut Workspace,
211 _: &InsertFirstNonWhitespace,
212 cx: &mut ViewContext<Workspace>,
213) {
214 Vim::update(cx, |vim, cx| {
215 vim.start_recording(cx);
216 vim.switch_mode(Mode::Insert, false, cx);
217 vim.update_active_editor(cx, |editor, cx| {
218 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
219 s.move_cursors_with(|map, cursor, _| {
220 (
221 first_non_whitespace(map, false, cursor),
222 SelectionGoal::None,
223 )
224 });
225 });
226 });
227 });
228}
229
230fn insert_end_of_line(_: &mut Workspace, _: &InsertEndOfLine, cx: &mut ViewContext<Workspace>) {
231 Vim::update(cx, |vim, cx| {
232 vim.start_recording(cx);
233 vim.switch_mode(Mode::Insert, false, cx);
234 vim.update_active_editor(cx, |editor, cx| {
235 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
236 s.move_cursors_with(|map, cursor, _| {
237 (next_line_end(map, cursor, 1), SelectionGoal::None)
238 });
239 });
240 });
241 });
242}
243
244fn insert_line_above(_: &mut Workspace, _: &InsertLineAbove, cx: &mut ViewContext<Workspace>) {
245 Vim::update(cx, |vim, cx| {
246 vim.start_recording(cx);
247 vim.switch_mode(Mode::Insert, false, cx);
248 vim.update_active_editor(cx, |editor, cx| {
249 editor.transact(cx, |editor, cx| {
250 let (map, old_selections) = editor.selections.all_display(cx);
251 let selection_start_rows: HashSet<u32> = old_selections
252 .into_iter()
253 .map(|selection| selection.start.row())
254 .collect();
255 let edits = selection_start_rows.into_iter().map(|row| {
256 let (indent, _) = map.line_indent(row);
257 let start_of_line =
258 motion::start_of_line(&map, false, DisplayPoint::new(row, 0))
259 .to_point(&map);
260 let mut new_text = " ".repeat(indent as usize);
261 new_text.push('\n');
262 (start_of_line..start_of_line, new_text)
263 });
264 editor.edit_with_autoindent(edits, cx);
265 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
266 s.move_cursors_with(|map, cursor, _| {
267 let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1);
268 let insert_point = motion::end_of_line(map, false, previous_line);
269 (insert_point, SelectionGoal::None)
270 });
271 });
272 });
273 });
274 });
275}
276
277fn insert_line_below(_: &mut Workspace, _: &InsertLineBelow, cx: &mut ViewContext<Workspace>) {
278 Vim::update(cx, |vim, cx| {
279 vim.start_recording(cx);
280 vim.switch_mode(Mode::Insert, false, cx);
281 vim.update_active_editor(cx, |editor, cx| {
282 let text_layout_details = editor.text_layout_details(cx);
283 editor.transact(cx, |editor, cx| {
284 let (map, old_selections) = editor.selections.all_display(cx);
285
286 let selection_end_rows: HashSet<u32> = old_selections
287 .into_iter()
288 .map(|selection| selection.end.row())
289 .collect();
290 let edits = selection_end_rows.into_iter().map(|row| {
291 let (indent, _) = map.line_indent(row);
292 let end_of_line =
293 motion::end_of_line(&map, false, DisplayPoint::new(row, 0)).to_point(&map);
294
295 let mut new_text = "\n".to_string();
296 new_text.push_str(&" ".repeat(indent as usize));
297 (end_of_line..end_of_line, new_text)
298 });
299 editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
300 s.maybe_move_cursors_with(|map, cursor, goal| {
301 Motion::CurrentLine.move_point(
302 map,
303 cursor,
304 goal,
305 None,
306 &text_layout_details,
307 )
308 });
309 });
310 editor.edit_with_autoindent(edits, cx);
311 });
312 });
313 });
314}
315
316fn yank_line(_: &mut Workspace, _: &YankLine, cx: &mut ViewContext<Workspace>) {
317 Vim::update(cx, |vim, cx| {
318 let count = vim.take_count(cx);
319 yank_motion(vim, motion::Motion::CurrentLine, count, cx)
320 })
321}
322
323pub(crate) fn normal_replace(text: Arc<str>, cx: &mut WindowContext) {
324 Vim::update(cx, |vim, cx| {
325 vim.stop_recording();
326 vim.update_active_editor(cx, |editor, cx| {
327 editor.transact(cx, |editor, cx| {
328 editor.set_clip_at_line_ends(false, cx);
329 let (map, display_selections) = editor.selections.all_display(cx);
330 // Selections are biased right at the start. So we need to store
331 // anchors that are biased left so that we can restore the selections
332 // after the change
333 let stable_anchors = editor
334 .selections
335 .disjoint_anchors()
336 .into_iter()
337 .map(|selection| {
338 let start = selection.start.bias_left(&map.buffer_snapshot);
339 start..start
340 })
341 .collect::<Vec<_>>();
342
343 let edits = display_selections
344 .into_iter()
345 .map(|selection| {
346 let mut range = selection.range();
347 *range.end.column_mut() += 1;
348 range.end = map.clip_point(range.end, Bias::Right);
349
350 (
351 range.start.to_offset(&map, Bias::Left)
352 ..range.end.to_offset(&map, Bias::Left),
353 text.clone(),
354 )
355 })
356 .collect::<Vec<_>>();
357
358 editor.buffer().update(cx, |buffer, cx| {
359 buffer.edit(edits, None, cx);
360 });
361 editor.set_clip_at_line_ends(true, cx);
362 editor.change_selections(None, cx, |s| {
363 s.select_anchor_ranges(stable_anchors);
364 });
365 });
366 });
367 vim.pop_operator(cx)
368 });
369}
370
371// #[cfg(test)]
372// mod test {
373// use gpui::TestAppContext;
374// use indoc::indoc;
375
376// use crate::{
377// state::Mode::{self},
378// test::NeovimBackedTestContext,
379// };
380
381// #[gpui::test]
382// async fn test_h(cx: &mut gpui::TestAppContext) {
383// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]);
384// cx.assert_all(indoc! {"
385// ˇThe qˇuick
386// ˇbrown"
387// })
388// .await;
389// }
390
391// #[gpui::test]
392// async fn test_backspace(cx: &mut gpui::TestAppContext) {
393// let mut cx = NeovimBackedTestContext::new(cx)
394// .await
395// .binding(["backspace"]);
396// cx.assert_all(indoc! {"
397// ˇThe qˇuick
398// ˇbrown"
399// })
400// .await;
401// }
402
403// #[gpui::test]
404// async fn test_j(cx: &mut gpui::TestAppContext) {
405// let mut cx = NeovimBackedTestContext::new(cx).await;
406
407// cx.set_shared_state(indoc! {"
408// aaˇaa
409// 😃😃"
410// })
411// .await;
412// cx.simulate_shared_keystrokes(["j"]).await;
413// cx.assert_shared_state(indoc! {"
414// aaaa
415// 😃ˇ😃"
416// })
417// .await;
418
419// for marked_position in cx.each_marked_position(indoc! {"
420// ˇThe qˇuick broˇwn
421// ˇfox jumps"
422// }) {
423// cx.assert_neovim_compatible(&marked_position, ["j"]).await;
424// }
425// }
426
427// #[gpui::test]
428// async fn test_enter(cx: &mut gpui::TestAppContext) {
429// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["enter"]);
430// cx.assert_all(indoc! {"
431// ˇThe qˇuick broˇwn
432// ˇfox jumps"
433// })
434// .await;
435// }
436
437// #[gpui::test]
438// async fn test_k(cx: &mut gpui::TestAppContext) {
439// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["k"]);
440// cx.assert_all(indoc! {"
441// ˇThe qˇuick
442// ˇbrown fˇox jumˇps"
443// })
444// .await;
445// }
446
447// #[gpui::test]
448// async fn test_l(cx: &mut gpui::TestAppContext) {
449// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["l"]);
450// cx.assert_all(indoc! {"
451// ˇThe qˇuicˇk
452// ˇbrowˇn"})
453// .await;
454// }
455
456// #[gpui::test]
457// async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
458// let mut cx = NeovimBackedTestContext::new(cx).await;
459// cx.assert_binding_matches_all(
460// ["$"],
461// indoc! {"
462// ˇThe qˇuicˇk
463// ˇbrowˇn"},
464// )
465// .await;
466// cx.assert_binding_matches_all(
467// ["0"],
468// indoc! {"
469// ˇThe qˇuicˇk
470// ˇbrowˇn"},
471// )
472// .await;
473// }
474
475// #[gpui::test]
476// async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
477// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-g"]);
478
479// cx.assert_all(indoc! {"
480// The ˇquick
481
482// brown fox jumps
483// overˇ the lazy doˇg"})
484// .await;
485// cx.assert(indoc! {"
486// The quiˇck
487
488// brown"})
489// .await;
490// cx.assert(indoc! {"
491// The quiˇck
492
493// "})
494// .await;
495// }
496
497// #[gpui::test]
498// async fn test_w(cx: &mut gpui::TestAppContext) {
499// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["w"]);
500// cx.assert_all(indoc! {"
501// The ˇquickˇ-ˇbrown
502// ˇ
503// ˇ
504// ˇfox_jumps ˇover
505// ˇthˇe"})
506// .await;
507// let mut cx = cx.binding(["shift-w"]);
508// cx.assert_all(indoc! {"
509// The ˇquickˇ-ˇbrown
510// ˇ
511// ˇ
512// ˇfox_jumps ˇover
513// ˇthˇe"})
514// .await;
515// }
516
517// #[gpui::test]
518// async fn test_end_of_word(cx: &mut gpui::TestAppContext) {
519// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["e"]);
520// cx.assert_all(indoc! {"
521// Thˇe quicˇkˇ-browˇn
522
523// fox_jumpˇs oveˇr
524// thˇe"})
525// .await;
526// let mut cx = cx.binding(["shift-e"]);
527// cx.assert_all(indoc! {"
528// Thˇe quicˇkˇ-browˇn
529
530// fox_jumpˇs oveˇr
531// thˇe"})
532// .await;
533// }
534
535// #[gpui::test]
536// async fn test_b(cx: &mut gpui::TestAppContext) {
537// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["b"]);
538// cx.assert_all(indoc! {"
539// ˇThe ˇquickˇ-ˇbrown
540// ˇ
541// ˇ
542// ˇfox_jumps ˇover
543// ˇthe"})
544// .await;
545// let mut cx = cx.binding(["shift-b"]);
546// cx.assert_all(indoc! {"
547// ˇThe ˇquickˇ-ˇbrown
548// ˇ
549// ˇ
550// ˇfox_jumps ˇover
551// ˇthe"})
552// .await;
553// }
554
555// #[gpui::test]
556// async fn test_gg(cx: &mut gpui::TestAppContext) {
557// let mut cx = NeovimBackedTestContext::new(cx).await;
558// cx.assert_binding_matches_all(
559// ["g", "g"],
560// indoc! {"
561// The qˇuick
562
563// brown fox jumps
564// over ˇthe laˇzy dog"},
565// )
566// .await;
567// cx.assert_binding_matches(
568// ["g", "g"],
569// indoc! {"
570
571// brown fox jumps
572// over the laˇzy dog"},
573// )
574// .await;
575// cx.assert_binding_matches(
576// ["2", "g", "g"],
577// indoc! {"
578// ˇ
579
580// brown fox jumps
581// over the lazydog"},
582// )
583// .await;
584// }
585
586// #[gpui::test]
587// async fn test_end_of_document(cx: &mut gpui::TestAppContext) {
588// let mut cx = NeovimBackedTestContext::new(cx).await;
589// cx.assert_binding_matches_all(
590// ["shift-g"],
591// indoc! {"
592// The qˇuick
593
594// brown fox jumps
595// over ˇthe laˇzy dog"},
596// )
597// .await;
598// cx.assert_binding_matches(
599// ["shift-g"],
600// indoc! {"
601
602// brown fox jumps
603// over the laˇzy dog"},
604// )
605// .await;
606// cx.assert_binding_matches(
607// ["2", "shift-g"],
608// indoc! {"
609// ˇ
610
611// brown fox jumps
612// over the lazydog"},
613// )
614// .await;
615// }
616
617// #[gpui::test]
618// async fn test_a(cx: &mut gpui::TestAppContext) {
619// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["a"]);
620// cx.assert_all("The qˇuicˇk").await;
621// }
622
623// #[gpui::test]
624// async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
625// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-a"]);
626// cx.assert_all(indoc! {"
627// ˇ
628// The qˇuick
629// brown ˇfox "})
630// .await;
631// }
632
633// #[gpui::test]
634// async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
635// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["^"]);
636// cx.assert("The qˇuick").await;
637// cx.assert(" The qˇuick").await;
638// cx.assert("ˇ").await;
639// cx.assert(indoc! {"
640// The qˇuick
641// brown fox"})
642// .await;
643// cx.assert(indoc! {"
644// ˇ
645// The quick"})
646// .await;
647// // Indoc disallows trailing whitespace.
648// cx.assert(" ˇ \nThe quick").await;
649// }
650
651// #[gpui::test]
652// async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
653// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-i"]);
654// cx.assert("The qˇuick").await;
655// cx.assert(" The qˇuick").await;
656// cx.assert("ˇ").await;
657// cx.assert(indoc! {"
658// The qˇuick
659// brown fox"})
660// .await;
661// cx.assert(indoc! {"
662// ˇ
663// The quick"})
664// .await;
665// }
666
667// #[gpui::test]
668// async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
669// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-d"]);
670// cx.assert(indoc! {"
671// The qˇuick
672// brown fox"})
673// .await;
674// cx.assert(indoc! {"
675// The quick
676// ˇ
677// brown fox"})
678// .await;
679// }
680
681// #[gpui::test]
682// async fn test_x(cx: &mut gpui::TestAppContext) {
683// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["x"]);
684// cx.assert_all("ˇTeˇsˇt").await;
685// cx.assert(indoc! {"
686// Tesˇt
687// test"})
688// .await;
689// }
690
691// #[gpui::test]
692// async fn test_delete_left(cx: &mut gpui::TestAppContext) {
693// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-x"]);
694// cx.assert_all("ˇTˇeˇsˇt").await;
695// cx.assert(indoc! {"
696// Test
697// ˇtest"})
698// .await;
699// }
700
701// #[gpui::test]
702// async fn test_o(cx: &mut gpui::TestAppContext) {
703// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["o"]);
704// cx.assert("ˇ").await;
705// cx.assert("The ˇquick").await;
706// cx.assert_all(indoc! {"
707// The qˇuick
708// brown ˇfox
709// jumps ˇover"})
710// .await;
711// cx.assert(indoc! {"
712// The quick
713// ˇ
714// brown fox"})
715// .await;
716
717// cx.assert_manual(
718// indoc! {"
719// fn test() {
720// println!(ˇ);
721// }"},
722// Mode::Normal,
723// indoc! {"
724// fn test() {
725// println!();
726// ˇ
727// }"},
728// Mode::Insert,
729// );
730
731// cx.assert_manual(
732// indoc! {"
733// fn test(ˇ) {
734// println!();
735// }"},
736// Mode::Normal,
737// indoc! {"
738// fn test() {
739// ˇ
740// println!();
741// }"},
742// Mode::Insert,
743// );
744// }
745
746// #[gpui::test]
747// async fn test_insert_line_above(cx: &mut gpui::TestAppContext) {
748// let cx = NeovimBackedTestContext::new(cx).await;
749// let mut cx = cx.binding(["shift-o"]);
750// cx.assert("ˇ").await;
751// cx.assert("The ˇquick").await;
752// cx.assert_all(indoc! {"
753// The qˇuick
754// brown ˇfox
755// jumps ˇover"})
756// .await;
757// cx.assert(indoc! {"
758// The quick
759// ˇ
760// brown fox"})
761// .await;
762
763// // Our indentation is smarter than vims. So we don't match here
764// cx.assert_manual(
765// indoc! {"
766// fn test() {
767// println!(ˇ);
768// }"},
769// Mode::Normal,
770// indoc! {"
771// fn test() {
772// ˇ
773// println!();
774// }"},
775// Mode::Insert,
776// );
777// cx.assert_manual(
778// indoc! {"
779// fn test(ˇ) {
780// println!();
781// }"},
782// Mode::Normal,
783// indoc! {"
784// ˇ
785// fn test() {
786// println!();
787// }"},
788// Mode::Insert,
789// );
790// }
791
792// #[gpui::test]
793// async fn test_dd(cx: &mut gpui::TestAppContext) {
794// let mut cx = NeovimBackedTestContext::new(cx).await;
795// cx.assert_neovim_compatible("ˇ", ["d", "d"]).await;
796// cx.assert_neovim_compatible("The ˇquick", ["d", "d"]).await;
797// for marked_text in cx.each_marked_position(indoc! {"
798// The qˇuick
799// brown ˇfox
800// jumps ˇover"})
801// {
802// cx.assert_neovim_compatible(&marked_text, ["d", "d"]).await;
803// }
804// cx.assert_neovim_compatible(
805// indoc! {"
806// The quick
807// ˇ
808// brown fox"},
809// ["d", "d"],
810// )
811// .await;
812// }
813
814// #[gpui::test]
815// async fn test_cc(cx: &mut gpui::TestAppContext) {
816// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "c"]);
817// cx.assert("ˇ").await;
818// cx.assert("The ˇquick").await;
819// cx.assert_all(indoc! {"
820// The quˇick
821// brown ˇfox
822// jumps ˇover"})
823// .await;
824// cx.assert(indoc! {"
825// The quick
826// ˇ
827// brown fox"})
828// .await;
829// }
830
831// #[gpui::test]
832// async fn test_repeated_word(cx: &mut gpui::TestAppContext) {
833// let mut cx = NeovimBackedTestContext::new(cx).await;
834
835// for count in 1..=5 {
836// cx.assert_binding_matches_all(
837// [&count.to_string(), "w"],
838// indoc! {"
839// ˇThe quˇickˇ browˇn
840// ˇ
841// ˇfox ˇjumpsˇ-ˇoˇver
842// ˇthe lazy dog
843// "},
844// )
845// .await;
846// }
847// }
848
849// #[gpui::test]
850// async fn test_h_through_unicode(cx: &mut gpui::TestAppContext) {
851// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]);
852// cx.assert_all("Testˇ├ˇ──ˇ┐ˇTest").await;
853// }
854
855// #[gpui::test]
856// async fn test_f_and_t(cx: &mut gpui::TestAppContext) {
857// let mut cx = NeovimBackedTestContext::new(cx).await;
858
859// for count in 1..=3 {
860// let test_case = indoc! {"
861// ˇaaaˇbˇ ˇbˇ ˇbˇbˇ aˇaaˇbaaa
862// ˇ ˇbˇaaˇa ˇbˇbˇb
863// ˇ
864// ˇb
865// "};
866
867// cx.assert_binding_matches_all([&count.to_string(), "f", "b"], test_case)
868// .await;
869
870// cx.assert_binding_matches_all([&count.to_string(), "t", "b"], test_case)
871// .await;
872// }
873// }
874
875// #[gpui::test]
876// async fn test_capital_f_and_capital_t(cx: &mut gpui::TestAppContext) {
877// let mut cx = NeovimBackedTestContext::new(cx).await;
878// let test_case = indoc! {"
879// ˇaaaˇbˇ ˇbˇ ˇbˇbˇ aˇaaˇbaaa
880// ˇ ˇbˇaaˇa ˇbˇbˇb
881// ˇ•••
882// ˇb
883// "
884// };
885
886// for count in 1..=3 {
887// cx.assert_binding_matches_all([&count.to_string(), "shift-f", "b"], test_case)
888// .await;
889
890// cx.assert_binding_matches_all([&count.to_string(), "shift-t", "b"], test_case)
891// .await;
892// }
893// }
894
895// #[gpui::test]
896// async fn test_percent(cx: &mut TestAppContext) {
897// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["%"]);
898// cx.assert_all("ˇconsole.logˇ(ˇvaˇrˇ)ˇ;").await;
899// cx.assert_all("ˇconsole.logˇ(ˇ'var', ˇ[ˇ1, ˇ2, 3ˇ]ˇ)ˇ;")
900// .await;
901// cx.assert_all("let result = curried_funˇ(ˇ)ˇ(ˇ)ˇ;").await;
902// }
903// }