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