vim: Fix deletion with sentence-motion (#22289)

Fixes #22151.

Turns out Vim also has some weird behavior with sentence deletion in
case it's on the first character of a line.

Release Notes:

- vim: Fixed deleting sentence-wise (i.e. `d(` and `d)`), which would
previously delete the whole line instead of just a sentence.
This commit is contained in:
Thorsten Ball 2024-12-20 13:48:38 +01:00 committed by GitHub
parent 7c03e11cfc
commit b25d8ecb75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 98 additions and 14 deletions

View file

@ -585,8 +585,6 @@ impl Motion {
| NextLineStart | NextLineStart
| PreviousLineStart | PreviousLineStart
| StartOfLineDownward | StartOfLineDownward
| SentenceBackward
| SentenceForward
| StartOfParagraph | StartOfParagraph
| EndOfParagraph | EndOfParagraph
| WindowTop | WindowTop
@ -611,6 +609,8 @@ impl Motion {
| Left | Left
| Backspace | Backspace
| Right | Right
| SentenceBackward
| SentenceForward
| Space | Space
| StartOfLine { .. } | StartOfLine { .. }
| EndOfLineDownward | EndOfLineDownward

View file

@ -28,23 +28,27 @@ impl Vim {
original_columns.insert(selection.id, original_head.column()); original_columns.insert(selection.id, original_head.column());
motion.expand_selection(map, selection, times, true, &text_layout_details); motion.expand_selection(map, selection, times, true, &text_layout_details);
let start_point = selection.start.to_point(map);
let next_line = map
.buffer_snapshot
.clip_point(Point::new(start_point.row + 1, 0), Bias::Left)
.to_display_point(map);
match motion { match motion {
// Motion::NextWordStart on an empty line should delete it. // Motion::NextWordStart on an empty line should delete it.
Motion::NextWordStart { .. } => { Motion::NextWordStart { .. }
if selection.is_empty() if selection.is_empty()
&& map && map
.buffer_snapshot .buffer_snapshot
.line_len(MultiBufferRow(selection.start.to_point(map).row)) .line_len(MultiBufferRow(start_point.row))
== 0 == 0 =>
{ {
selection.end = map selection.end = next_line
.buffer_snapshot }
.clip_point( // Sentence motions, when done from start of line, include the newline
Point::new(selection.start.to_point(map).row + 1, 0), Motion::SentenceForward | Motion::SentenceBackward
Bias::Left, if selection.start.column() == 0 =>
) {
.to_display_point(map) selection.end = next_line
}
} }
Motion::EndOfDocument {} => { Motion::EndOfDocument {} => {
// Deleting until the end of the document includes the last line, including // Deleting until the end of the document includes the last line, including
@ -604,4 +608,62 @@ mod test {
cx.simulate("d t x", "ˇax").await.assert_matches(); cx.simulate("d t x", "ˇax").await.assert_matches();
cx.simulate("d t x", "aˇx").await.assert_matches(); cx.simulate("d t x", "aˇx").await.assert_matches();
} }
#[gpui::test]
async fn test_delete_sentence(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.simulate(
"d )",
indoc! {"
Fiˇrst. Second. Third.
Fourth.
"},
)
.await
.assert_matches();
cx.simulate(
"d )",
indoc! {"
First. Secˇond. Third.
Fourth.
"},
)
.await
.assert_matches();
// Two deletes
cx.simulate(
"d ) d )",
indoc! {"
First. Second. Thirˇd.
Fourth.
"},
)
.await
.assert_matches();
// Should delete whole line if done on first column
cx.simulate(
"d )",
indoc! {"
ˇFirst.
Fourth.
"},
)
.await
.assert_matches();
// Backwards it should also delete the whole first line
cx.simulate(
"d (",
indoc! {"
First.
ˇSecond.
Fourth.
"},
)
.await
.assert_matches();
}
} }

View file

@ -0,0 +1,22 @@
{"Put":{"state":"Fiˇrst. Second. Third.\nFourth.\n"}}
{"Key":"d"}
{"Key":")"}
{"Get":{"state":"FiˇSecond. Third.\nFourth.\n","mode":"Normal"}}
{"Put":{"state":"First. Secˇond. Third.\nFourth.\n"}}
{"Key":"d"}
{"Key":")"}
{"Get":{"state":"First. SecˇThird.\nFourth.\n","mode":"Normal"}}
{"Put":{"state":"First. Second. Thirˇd.\nFourth.\n"}}
{"Key":"d"}
{"Key":")"}
{"Key":"d"}
{"Key":")"}
{"Get":{"state":"First. Second. Thˇi\n","mode":"Normal"}}
{"Put":{"state":"ˇFirst.\nFourth.\n"}}
{"Key":"d"}
{"Key":")"}
{"Get":{"state":"ˇFourth.\n","mode":"Normal"}}
{"Put":{"state":"First.\nˇSecond.\nFourth.\n"}}
{"Key":"d"}
{"Key":"("}
{"Get":{"state":"ˇSecond.\nFourth.\n","mode":"Normal"}}