delete.rs

  1use crate::{motion::Motion, utils::copy_selections_content, Vim};
  2use collections::HashMap;
  3use editor::{Autoscroll, Bias};
  4use gpui::MutableAppContext;
  5
  6pub fn delete_over(vim: &mut Vim, motion: Motion, cx: &mut MutableAppContext) {
  7    vim.update_active_editor(cx, |editor, cx| {
  8        editor.transact(cx, |editor, cx| {
  9            editor.set_clip_at_line_ends(false, cx);
 10            let mut original_columns: HashMap<_, _> = Default::default();
 11            editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
 12                s.move_with(|map, selection| {
 13                    let original_head = selection.head();
 14                    motion.expand_selection(map, selection, true);
 15                    original_columns.insert(selection.id, original_head.column());
 16                });
 17            });
 18            copy_selections_content(editor, motion.linewise(), cx);
 19            editor.insert("", cx);
 20
 21            // Fixup cursor position after the deletion
 22            editor.set_clip_at_line_ends(true, cx);
 23            editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
 24                s.move_with(|map, selection| {
 25                    let mut cursor = selection.head();
 26                    if motion.linewise() {
 27                        if let Some(column) = original_columns.get(&selection.id) {
 28                            *cursor.column_mut() = *column
 29                        }
 30                    }
 31                    cursor = map.clip_point(cursor, Bias::Left);
 32                    selection.collapse_to(cursor, selection.goal)
 33                });
 34            });
 35        });
 36    });
 37}
 38
 39#[cfg(test)]
 40mod test {
 41    use indoc::indoc;
 42
 43    use crate::{state::Mode, vim_test_context::VimTestContext};
 44
 45    #[gpui::test]
 46    async fn test_delete_h(cx: &mut gpui::TestAppContext) {
 47        let cx = VimTestContext::new(cx, true).await;
 48        let mut cx = cx.binding(["d", "h"]);
 49        cx.assert("Teˇst", "Tˇst");
 50        cx.assert("Tˇest", "ˇest");
 51        cx.assert("ˇTest", "ˇTest");
 52        cx.assert(
 53            indoc! {"
 54                Test
 55                ˇtest"},
 56            indoc! {"
 57                Test
 58                ˇtest"},
 59        );
 60    }
 61
 62    #[gpui::test]
 63    async fn test_delete_l(cx: &mut gpui::TestAppContext) {
 64        let cx = VimTestContext::new(cx, true).await;
 65        let mut cx = cx.binding(["d", "l"]);
 66        cx.assert("ˇTest", "ˇest");
 67        cx.assert("Teˇst", "Teˇt");
 68        cx.assert("Tesˇt", "Teˇs");
 69        cx.assert(
 70            indoc! {"
 71                Tesˇt
 72                test"},
 73            indoc! {"
 74                Teˇs
 75                test"},
 76        );
 77    }
 78
 79    #[gpui::test]
 80    async fn test_delete_w(cx: &mut gpui::TestAppContext) {
 81        let cx = VimTestContext::new(cx, true).await;
 82        let mut cx = cx.binding(["d", "w"]);
 83        cx.assert("Teˇst", "Tˇe");
 84        cx.assert("Tˇest test", "Tˇtest");
 85        cx.assert(
 86            indoc! {"
 87                Test teˇst
 88                test"},
 89            indoc! {"
 90                Test tˇe
 91                test"},
 92        );
 93        cx.assert(
 94            indoc! {"
 95                Test tesˇt
 96                test"},
 97            indoc! {"
 98                Test teˇs
 99                test"},
100        );
101        cx.assert(
102            indoc! {"
103                Test test
104                ˇ
105                test"},
106            indoc! {"
107                Test test
108                ˇ
109                test"},
110        );
111
112        let mut cx = cx.binding(["d", "shift-w"]);
113        cx.assert("Test teˇst-test test", "Test teˇtest");
114    }
115
116    #[gpui::test]
117    async fn test_delete_e(cx: &mut gpui::TestAppContext) {
118        let cx = VimTestContext::new(cx, true).await;
119        let mut cx = cx.binding(["d", "e"]);
120        cx.assert("Teˇst Test", "Teˇ Test");
121        cx.assert("Tˇest test", "Tˇ test");
122        cx.assert(
123            indoc! {"
124                Test teˇst
125                test"},
126            indoc! {"
127                Test tˇe
128                test"},
129        );
130        cx.assert(
131            indoc! {"
132                Test tesˇt
133                test"},
134            "Test teˇs",
135        );
136        cx.assert(
137            indoc! {"
138                Test test
139                ˇ
140                test"},
141            indoc! {"
142                Test test
143                ˇ
144                test"},
145        );
146
147        let mut cx = cx.binding(["d", "shift-e"]);
148        cx.assert("Test teˇst-test test", "Test teˇ test");
149    }
150
151    #[gpui::test]
152    async fn test_delete_b(cx: &mut gpui::TestAppContext) {
153        let cx = VimTestContext::new(cx, true).await;
154        let mut cx = cx.binding(["d", "b"]);
155        cx.assert("Teˇst Test", "ˇst Test");
156        cx.assert("Test ˇtest", "ˇtest");
157        cx.assert("Test1 test2 ˇtest3", "Test1 ˇtest3");
158        cx.assert(
159            indoc! {"
160                Test test
161                ˇtest"},
162            // Trailing whitespace after cursor
163            indoc! {"
164                Testˇ 
165                test"},
166        );
167        cx.assert(
168            indoc! {"
169                Test test
170                ˇ
171                test"},
172            // Trailing whitespace after cursor
173            indoc! {"
174                Testˇ 
175                
176                test"},
177        );
178
179        let mut cx = cx.binding(["d", "shift-b"]);
180        cx.assert("Test test-test ˇtest", "Test ˇtest");
181    }
182
183    #[gpui::test]
184    async fn test_delete_end_of_line(cx: &mut gpui::TestAppContext) {
185        let cx = VimTestContext::new(cx, true).await;
186        let mut cx = cx.binding(["d", "$"]);
187        cx.assert(
188            indoc! {"
189                The qˇuick
190                brown fox"},
191            indoc! {"
192                The ˇq
193                brown fox"},
194        );
195        cx.assert(
196            indoc! {"
197                The quick
198                ˇ
199                brown fox"},
200            indoc! {"
201                The quick
202                ˇ
203                brown fox"},
204        );
205    }
206
207    #[gpui::test]
208    async fn test_delete_0(cx: &mut gpui::TestAppContext) {
209        let cx = VimTestContext::new(cx, true).await;
210        let mut cx = cx.binding(["d", "0"]);
211        cx.assert(
212            indoc! {"
213                The qˇuick
214                brown fox"},
215            indoc! {"
216                ˇuick
217                brown fox"},
218        );
219        cx.assert(
220            indoc! {"
221                The quick
222                ˇ
223                brown fox"},
224            indoc! {"
225                The quick
226                ˇ
227                brown fox"},
228        );
229    }
230
231    #[gpui::test]
232    async fn test_delete_k(cx: &mut gpui::TestAppContext) {
233        let cx = VimTestContext::new(cx, true).await;
234        let mut cx = cx.binding(["d", "k"]);
235        cx.assert(
236            indoc! {"
237                The quick
238                brown ˇfox
239                jumps over"},
240            "jumps ˇover",
241        );
242        cx.assert(
243            indoc! {"
244                The quick
245                brown fox
246                jumps ˇover"},
247            "The quˇick",
248        );
249        cx.assert(
250            indoc! {"
251                The qˇuick
252                brown fox
253                jumps over"},
254            indoc! {"
255                brownˇ fox
256                jumps over"},
257        );
258        cx.assert(
259            indoc! {"
260                ˇbrown fox
261                jumps over"},
262            "ˇjumps over",
263        );
264    }
265
266    #[gpui::test]
267    async fn test_delete_j(cx: &mut gpui::TestAppContext) {
268        let cx = VimTestContext::new(cx, true).await;
269        let mut cx = cx.binding(["d", "j"]);
270        cx.assert(
271            indoc! {"
272                The quick
273                brown ˇfox
274                jumps over"},
275            "The quˇick",
276        );
277        cx.assert(
278            indoc! {"
279                The quick
280                brown fox
281                jumps ˇover"},
282            indoc! {"
283                The quick
284                brown ˇfox"},
285        );
286        cx.assert(
287            indoc! {"
288                The qˇuick
289                brown fox
290                jumps over"},
291            "jumpsˇ over",
292        );
293        cx.assert(
294            indoc! {"
295                The quick
296                brown fox
297                ˇ"},
298            indoc! {"
299                The quick
300                ˇbrown fox"},
301        );
302    }
303
304    #[gpui::test]
305    async fn test_delete_end_of_document(cx: &mut gpui::TestAppContext) {
306        let cx = VimTestContext::new(cx, true).await;
307        let mut cx = cx.binding(["d", "shift-g"]);
308        cx.assert(
309            indoc! {"
310                The quick
311                brownˇ fox
312                jumps over
313                the lazy"},
314            "The qˇuick",
315        );
316        cx.assert(
317            indoc! {"
318                The quick
319                brownˇ fox
320                jumps over
321                the lazy"},
322            "The qˇuick",
323        );
324        cx.assert(
325            indoc! {"
326                The quick
327                brown fox
328                jumps over
329                the lˇazy"},
330            indoc! {"
331                The quick
332                brown fox
333                jumpsˇ over"},
334        );
335        cx.assert(
336            indoc! {"
337                The quick
338                brown fox
339                jumps over
340                ˇ"},
341            indoc! {"
342                The quick
343                brown fox
344                ˇjumps over"},
345        );
346    }
347
348    #[gpui::test]
349    async fn test_delete_gg(cx: &mut gpui::TestAppContext) {
350        let cx = VimTestContext::new(cx, true).await;
351        let mut cx = cx.binding(["d", "g", "g"]);
352        cx.assert(
353            indoc! {"
354                The quick
355                brownˇ fox
356                jumps over
357                the lazy"},
358            indoc! {"
359                jumpsˇ over
360                the lazy"},
361        );
362        cx.assert(
363            indoc! {"
364                The quick
365                brown fox
366                jumps over
367                the lˇazy"},
368            "ˇ",
369        );
370        cx.assert(
371            indoc! {"
372                The qˇuick
373                brown fox
374                jumps over
375                the lazy"},
376            indoc! {"
377                brownˇ fox
378                jumps over
379                the lazy"},
380        );
381        cx.assert(
382            indoc! {"
383                ˇ
384                brown fox
385                jumps over
386                the lazy"},
387            indoc! {"
388                ˇbrown fox
389                jumps over
390                the lazy"},
391        );
392    }
393
394    #[gpui::test]
395    async fn test_cancel_delete_operator(cx: &mut gpui::TestAppContext) {
396        let mut cx = VimTestContext::new(cx, true).await;
397        cx.set_state(
398            indoc! {"
399                The quick brown
400                fox juˇmps over
401                the lazy dog"},
402            Mode::Normal,
403        );
404
405        // Canceling operator twice reverts to normal mode with no active operator
406        cx.simulate_keystrokes(["d", "escape", "k"]);
407        assert_eq!(cx.active_operator(), None);
408        assert_eq!(cx.mode(), Mode::Normal);
409        cx.assert_editor_state(indoc! {"
410            The quˇick brown
411            fox jumps over
412            the lazy dog"});
413    }
414
415    #[gpui::test]
416    async fn test_unbound_command_cancels_pending_operator(cx: &mut gpui::TestAppContext) {
417        let mut cx = VimTestContext::new(cx, true).await;
418        cx.set_state(
419            indoc! {"
420                The quick brown
421                fox juˇmps over
422                the lazy dog"},
423            Mode::Normal,
424        );
425
426        // Canceling operator twice reverts to normal mode with no active operator
427        cx.simulate_keystrokes(["d", "y"]);
428        assert_eq!(cx.active_operator(), None);
429        assert_eq!(cx.mode(), Mode::Normal);
430    }
431}