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
| PreviousLineStart
| StartOfLineDownward
| SentenceBackward
| SentenceForward
| StartOfParagraph
| EndOfParagraph
| WindowTop
@ -611,6 +609,8 @@ impl Motion {
| Left
| Backspace
| Right
| SentenceBackward
| SentenceForward
| Space
| StartOfLine { .. }
| EndOfLineDownward

View file

@ -28,23 +28,27 @@ impl Vim {
original_columns.insert(selection.id, original_head.column());
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 {
// Motion::NextWordStart on an empty line should delete it.
Motion::NextWordStart { .. } => {
Motion::NextWordStart { .. }
if selection.is_empty()
&& map
.buffer_snapshot
.line_len(MultiBufferRow(selection.start.to_point(map).row))
== 0
{
selection.end = map
.buffer_snapshot
.clip_point(
Point::new(selection.start.to_point(map).row + 1, 0),
Bias::Left,
)
.to_display_point(map)
}
.line_len(MultiBufferRow(start_point.row))
== 0 =>
{
selection.end = next_line
}
// Sentence motions, when done from start of line, include the newline
Motion::SentenceForward | Motion::SentenceBackward
if selection.start.column() == 0 =>
{
selection.end = next_line
}
Motion::EndOfDocument {} => {
// 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", "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"}}