substitute.rs

 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.update_active_editor(cx, |editor, cx| {
 8        editor.set_clip_at_line_ends(false, 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        editor.set_clip_at_line_ends(true, cx);
25    });
26    vim.switch_mode(Mode::Insert, true, cx)
27}
28
29#[cfg(test)]
30mod test {
31    use crate::{state::Mode, test::VimTestContext};
32    use indoc::indoc;
33
34    #[gpui::test]
35    async fn test_substitute(cx: &mut gpui::TestAppContext) {
36        let mut cx = VimTestContext::new(cx, true).await;
37
38        // supports a single cursor
39        cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
40        cx.simulate_keystrokes(["s", "x"]);
41        cx.assert_editor_state("xˇbc\n");
42
43        // supports a selection
44        cx.set_state(indoc! {"a«bcˇ»\n"}, Mode::Visual { line: false });
45        cx.assert_editor_state("a«bcˇ»\n");
46        cx.simulate_keystrokes(["s", "x"]);
47        cx.assert_editor_state("axˇ\n");
48
49        // supports counts
50        cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
51        cx.simulate_keystrokes(["2", "s", "x"]);
52        cx.assert_editor_state("xˇc\n");
53
54        // supports multiple cursors
55        cx.set_state(indoc! {"a«bcˇ»deˇffg\n"}, Mode::Normal);
56        cx.simulate_keystrokes(["2", "s", "x"]);
57        cx.assert_editor_state("axˇdexˇg\n");
58
59        // does not read beyond end of line
60        cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
61        cx.simulate_keystrokes(["5", "s", "x"]);
62        cx.assert_editor_state("\n");
63
64        // it handles multibyte characters
65        cx.set_state(indoc! {"ˇcàfé\n"}, Mode::Normal);
66        cx.simulate_keystrokes(["4", "s"]);
67        cx.assert_editor_state("ˇ\n");
68
69        // should transactionally undo selection changes
70        cx.simulate_keystrokes(["escape", "u"]);
71        cx.assert_editor_state("ˇcàfé\n");
72    }
73}