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