1use gpui::WindowContext;
2use language::Point;
3
4use crate::{motion::Motion, Mode, Vim};
5
6pub fn substitute(vim: &mut Vim, count: Option<usize>, cx: &mut WindowContext) {
7 vim.switch_mode(Mode::Insert, true, cx);
8 vim.update_active_editor(cx, |editor, cx| {
9 editor.transact(cx, |editor, cx| {
10 editor.change_selections(None, cx, |s| {
11 s.move_with(|map, selection| {
12 if selection.start == selection.end {
13 Motion::Right.expand_selection(map, selection, count, true);
14 }
15 })
16 });
17 let selections = editor.selections.all::<Point>(cx);
18 for selection in selections.into_iter().rev() {
19 editor.buffer().update(cx, |buffer, cx| {
20 buffer.edit([(selection.start..selection.end, "")], None, cx)
21 })
22 }
23 });
24 });
25}
26
27#[cfg(test)]
28mod test {
29 use crate::{state::Mode, test::VimTestContext};
30 use indoc::indoc;
31
32 #[gpui::test]
33 async fn test_substitute(cx: &mut gpui::TestAppContext) {
34 let mut cx = VimTestContext::new(cx, true).await;
35
36 // supports a single cursor
37 cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
38 cx.simulate_keystrokes(["s", "x"]);
39 cx.assert_editor_state("xˇbc\n");
40
41 // supports a selection
42 cx.set_state(indoc! {"a«bcˇ»\n"}, Mode::Visual { line: false });
43 cx.assert_editor_state("a«bcˇ»\n");
44 cx.simulate_keystrokes(["s", "x"]);
45 cx.assert_editor_state("axˇ\n");
46
47 // supports counts
48 cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
49 cx.simulate_keystrokes(["2", "s", "x"]);
50 cx.assert_editor_state("xˇc\n");
51
52 // supports multiple cursors
53 cx.set_state(indoc! {"a«bcˇ»deˇffg\n"}, Mode::Normal);
54 cx.simulate_keystrokes(["2", "s", "x"]);
55 cx.assert_editor_state("axˇdexˇg\n");
56
57 // does not read beyond end of line
58 cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
59 cx.simulate_keystrokes(["5", "s", "x"]);
60 cx.assert_editor_state("xˇ\n");
61
62 // it handles multibyte characters
63 cx.set_state(indoc! {"ˇcàfé\n"}, Mode::Normal);
64 cx.simulate_keystrokes(["4", "s"]);
65 cx.assert_editor_state("ˇ\n");
66
67 // should transactionally undo selection changes
68 cx.simulate_keystrokes(["escape", "u"]);
69 cx.assert_editor_state("ˇcàfé\n");
70 }
71}