substitute.rs

  1use editor::movement;
  2use gpui::{actions, AppContext, WindowContext};
  3use language::Point;
  4use workspace::Workspace;
  5
  6use crate::{motion::Motion, utils::copy_selections_content, Mode, Vim};
  7
  8actions!(Substitute, SubstituteLine);
  9
 10pub(crate) fn init(cx: &mut AppContext) {
 11    // todo!()
 12    // cx.add_action(|_: &mut Workspace, _: &Substitute, cx| {
 13    //     Vim::update(cx, |vim, cx| {
 14    //         vim.start_recording(cx);
 15    //         let count = vim.take_count(cx);
 16    //         substitute(vim, count, vim.state().mode == Mode::VisualLine, cx);
 17    //     })
 18    // });
 19
 20    // cx.add_action(|_: &mut Workspace, _: &SubstituteLine, cx| {
 21    //     Vim::update(cx, |vim, cx| {
 22    //         vim.start_recording(cx);
 23    //         if matches!(vim.state().mode, Mode::VisualBlock | Mode::Visual) {
 24    //             vim.switch_mode(Mode::VisualLine, false, cx)
 25    //         }
 26    //         let count = vim.take_count(cx);
 27    //         substitute(vim, count, true, cx)
 28    //     })
 29    // });
 30}
 31
 32pub fn substitute(vim: &mut Vim, count: Option<usize>, line_mode: bool, cx: &mut WindowContext) {
 33    vim.update_active_editor(cx, |editor, cx| {
 34        editor.set_clip_at_line_ends(false, cx);
 35        editor.transact(cx, |editor, cx| {
 36            let text_layout_details = editor.text_layout_details(cx);
 37            editor.change_selections(None, cx, |s| {
 38                s.move_with(|map, selection| {
 39                    if selection.start == selection.end {
 40                        Motion::Right.expand_selection(
 41                            map,
 42                            selection,
 43                            count,
 44                            true,
 45                            &text_layout_details,
 46                        );
 47                    }
 48                    if line_mode {
 49                        // in Visual mode when the selection contains the newline at the end
 50                        // of the line, we should exclude it.
 51                        if !selection.is_empty() && selection.end.column() == 0 {
 52                            selection.end = movement::left(map, selection.end);
 53                        }
 54                        Motion::CurrentLine.expand_selection(
 55                            map,
 56                            selection,
 57                            None,
 58                            false,
 59                            &text_layout_details,
 60                        );
 61                        if let Some((point, _)) = (Motion::FirstNonWhitespace {
 62                            display_lines: false,
 63                        })
 64                        .move_point(
 65                            map,
 66                            selection.start,
 67                            selection.goal,
 68                            None,
 69                            &text_layout_details,
 70                        ) {
 71                            selection.start = point;
 72                        }
 73                    }
 74                })
 75            });
 76            copy_selections_content(editor, line_mode, cx);
 77            let selections = editor.selections.all::<Point>(cx).into_iter();
 78            let edits = selections.map(|selection| (selection.start..selection.end, ""));
 79            editor.edit(edits, cx);
 80        });
 81    });
 82    vim.switch_mode(Mode::Insert, true, cx);
 83}
 84
 85// #[cfg(test)]
 86// mod test {
 87//     use crate::{
 88//         state::Mode,
 89//         test::{NeovimBackedTestContext, VimTestContext},
 90//     };
 91//     use indoc::indoc;
 92
 93//     #[gpui::test]
 94//     async fn test_substitute(cx: &mut gpui::TestAppContext) {
 95//         let mut cx = VimTestContext::new(cx, true).await;
 96
 97//         // supports a single cursor
 98//         cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
 99//         cx.simulate_keystrokes(["s", "x"]);
100//         cx.assert_editor_state("xˇbc\n");
101
102//         // supports a selection
103//         cx.set_state(indoc! {"a«bcˇ»\n"}, Mode::Visual);
104//         cx.assert_editor_state("a«bcˇ»\n");
105//         cx.simulate_keystrokes(["s", "x"]);
106//         cx.assert_editor_state("axˇ\n");
107
108//         // supports counts
109//         cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
110//         cx.simulate_keystrokes(["2", "s", "x"]);
111//         cx.assert_editor_state("xˇc\n");
112
113//         // supports multiple cursors
114//         cx.set_state(indoc! {"a«bcˇ»deˇffg\n"}, Mode::Normal);
115//         cx.simulate_keystrokes(["2", "s", "x"]);
116//         cx.assert_editor_state("axˇdexˇg\n");
117
118//         // does not read beyond end of line
119//         cx.set_state(indoc! {"ˇabc\n"}, Mode::Normal);
120//         cx.simulate_keystrokes(["5", "s", "x"]);
121//         cx.assert_editor_state("xˇ\n");
122
123//         // it handles multibyte characters
124//         cx.set_state(indoc! {"ˇcàfé\n"}, Mode::Normal);
125//         cx.simulate_keystrokes(["4", "s"]);
126//         cx.assert_editor_state("ˇ\n");
127
128//         // should transactionally undo selection changes
129//         cx.simulate_keystrokes(["escape", "u"]);
130//         cx.assert_editor_state("ˇcàfé\n");
131
132//         // it handles visual line mode
133//         cx.set_state(
134//             indoc! {"
135//             alpha
136//               beˇta
137//             gamma"},
138//             Mode::Normal,
139//         );
140//         cx.simulate_keystrokes(["shift-v", "s"]);
141//         cx.assert_editor_state(indoc! {"
142//             alpha
143//               ˇ
144//             gamma"});
145//     }
146
147//     #[gpui::test]
148//     async fn test_visual_change(cx: &mut gpui::TestAppContext) {
149//         let mut cx = NeovimBackedTestContext::new(cx).await;
150
151//         cx.set_shared_state("The quick ˇbrown").await;
152//         cx.simulate_shared_keystrokes(["v", "w", "c"]).await;
153//         cx.assert_shared_state("The quick ˇ").await;
154
155//         cx.set_shared_state(indoc! {"
156//             The ˇquick brown
157//             fox jumps over
158//             the lazy dog"})
159//             .await;
160//         cx.simulate_shared_keystrokes(["v", "w", "j", "c"]).await;
161//         cx.assert_shared_state(indoc! {"
162//             The ˇver
163//             the lazy dog"})
164//             .await;
165
166//         let cases = cx.each_marked_position(indoc! {"
167//             The ˇquick brown
168//             fox jumps ˇover
169//             the ˇlazy dog"});
170//         for initial_state in cases {
171//             cx.assert_neovim_compatible(&initial_state, ["v", "w", "j", "c"])
172//                 .await;
173//             cx.assert_neovim_compatible(&initial_state, ["v", "w", "k", "c"])
174//                 .await;
175//         }
176//     }
177
178//     #[gpui::test]
179//     async fn test_visual_line_change(cx: &mut gpui::TestAppContext) {
180//         let mut cx = NeovimBackedTestContext::new(cx)
181//             .await
182//             .binding(["shift-v", "c"]);
183//         cx.assert(indoc! {"
184//             The quˇick brown
185//             fox jumps over
186//             the lazy dog"})
187//             .await;
188//         // Test pasting code copied on change
189//         cx.simulate_shared_keystrokes(["escape", "j", "p"]).await;
190//         cx.assert_state_matches().await;
191
192//         cx.assert_all(indoc! {"
193//             The quick brown
194//             fox juˇmps over
195//             the laˇzy dog"})
196//             .await;
197//         let mut cx = cx.binding(["shift-v", "j", "c"]);
198//         cx.assert(indoc! {"
199//             The quˇick brown
200//             fox jumps over
201//             the lazy dog"})
202//             .await;
203//         // Test pasting code copied on delete
204//         cx.simulate_shared_keystrokes(["escape", "j", "p"]).await;
205//         cx.assert_state_matches().await;
206
207//         cx.assert_all(indoc! {"
208//             The quick brown
209//             fox juˇmps over
210//             the laˇzy dog"})
211//             .await;
212//     }
213
214//     #[gpui::test]
215//     async fn test_substitute_line(cx: &mut gpui::TestAppContext) {
216//         let mut cx = NeovimBackedTestContext::new(cx).await;
217
218//         let initial_state = indoc! {"
219//                     The quick brown
220//                     fox juˇmps over
221//                     the lazy dog
222//                     "};
223
224//         // normal mode
225//         cx.set_shared_state(initial_state).await;
226//         cx.simulate_shared_keystrokes(["shift-s", "o"]).await;
227//         cx.assert_shared_state(indoc! {"
228//             The quick brown
229//             oˇ
230//             the lazy dog
231//             "})
232//             .await;
233
234//         // visual mode
235//         cx.set_shared_state(initial_state).await;
236//         cx.simulate_shared_keystrokes(["v", "k", "shift-s", "o"])
237//             .await;
238//         cx.assert_shared_state(indoc! {"
239//             oˇ
240//             the lazy dog
241//             "})
242//             .await;
243
244//         // visual block mode
245//         cx.set_shared_state(initial_state).await;
246//         cx.simulate_shared_keystrokes(["ctrl-v", "j", "shift-s", "o"])
247//             .await;
248//         cx.assert_shared_state(indoc! {"
249//             The quick brown
250//             oˇ
251//             "})
252//             .await;
253
254//         // visual mode including newline
255//         cx.set_shared_state(initial_state).await;
256//         cx.simulate_shared_keystrokes(["v", "$", "shift-s", "o"])
257//             .await;
258//         cx.assert_shared_state(indoc! {"
259//             The quick brown
260//             oˇ
261//             the lazy dog
262//             "})
263//             .await;
264
265//         // indentation
266//         cx.set_neovim_option("shiftwidth=4").await;
267//         cx.set_shared_state(initial_state).await;
268//         cx.simulate_shared_keystrokes([">", ">", "shift-s", "o"])
269//             .await;
270//         cx.assert_shared_state(indoc! {"
271//             The quick brown
272//                 oˇ
273//             the lazy dog
274//             "})
275//             .await;
276//     }
277// }