Fix vim cw at end of words (#35300)

Fixes #35269

Release Notes:

- N/A
This commit is contained in:
Julia Ryan 2025-07-31 05:48:36 -05:00 committed by GitHub
parent 47af878ebb
commit 4b9334b910
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 4 deletions

View file

@ -987,7 +987,7 @@ impl Motion {
SelectionGoal::None, SelectionGoal::None,
), ),
NextWordEnd { ignore_punctuation } => ( NextWordEnd { ignore_punctuation } => (
next_word_end(map, point, *ignore_punctuation, times, true), next_word_end(map, point, *ignore_punctuation, times, true, true),
SelectionGoal::None, SelectionGoal::None,
), ),
PreviousWordStart { ignore_punctuation } => ( PreviousWordStart { ignore_punctuation } => (
@ -1723,14 +1723,19 @@ pub(crate) fn next_word_end(
ignore_punctuation: bool, ignore_punctuation: bool,
times: usize, times: usize,
allow_cross_newline: bool, allow_cross_newline: bool,
always_advance: bool,
) -> DisplayPoint { ) -> DisplayPoint {
let classifier = map let classifier = map
.buffer_snapshot .buffer_snapshot
.char_classifier_at(point.to_point(map)) .char_classifier_at(point.to_point(map))
.ignore_punctuation(ignore_punctuation); .ignore_punctuation(ignore_punctuation);
for _ in 0..times { for _ in 0..times {
let new_point = next_char(map, point, allow_cross_newline);
let mut need_next_char = false; let mut need_next_char = false;
let new_point = if always_advance {
next_char(map, point, allow_cross_newline)
} else {
point
};
let new_point = movement::find_boundary_exclusive( let new_point = movement::find_boundary_exclusive(
map, map,
new_point, new_point,

View file

@ -51,6 +51,7 @@ impl Vim {
ignore_punctuation, ignore_punctuation,
&text_layout_details, &text_layout_details,
motion == Motion::NextSubwordStart { ignore_punctuation }, motion == Motion::NextSubwordStart { ignore_punctuation },
!matches!(motion, Motion::NextWordStart { .. }),
) )
} }
_ => { _ => {
@ -148,6 +149,7 @@ fn expand_changed_word_selection(
ignore_punctuation: bool, ignore_punctuation: bool,
text_layout_details: &TextLayoutDetails, text_layout_details: &TextLayoutDetails,
use_subword: bool, use_subword: bool,
always_advance: bool,
) -> Option<MotionKind> { ) -> Option<MotionKind> {
let is_in_word = || { let is_in_word = || {
let classifier = map let classifier = map
@ -173,8 +175,14 @@ fn expand_changed_word_selection(
selection.end = selection.end =
motion::next_subword_end(map, selection.end, ignore_punctuation, 1, false); motion::next_subword_end(map, selection.end, ignore_punctuation, 1, false);
} else { } else {
selection.end = selection.end = motion::next_word_end(
motion::next_word_end(map, selection.end, ignore_punctuation, 1, false); map,
selection.end,
ignore_punctuation,
1,
false,
always_advance,
);
} }
selection.end = motion::next_char(map, selection.end, false); selection.end = motion::next_char(map, selection.end, false);
} }
@ -271,6 +279,10 @@ mod test {
cx.simulate("c shift-w", "Test teˇst-test test") cx.simulate("c shift-w", "Test teˇst-test test")
.await .await
.assert_matches(); .assert_matches();
// on last character of word, `cw` doesn't eat subsequent punctuation
// see https://github.com/zed-industries/zed/issues/35269
cx.simulate("c w", "tesˇt-test").await.assert_matches();
} }
#[gpui::test] #[gpui::test]

View file

@ -30,3 +30,7 @@
{"Key":"c"} {"Key":"c"}
{"Key":"shift-w"} {"Key":"shift-w"}
{"Get":{"state":"Test teˇ test","mode":"Insert"}} {"Get":{"state":"Test teˇ test","mode":"Insert"}}
{"Put":{"state":"tesˇt-test"}}
{"Key":"c"}
{"Key":"w"}
{"Get":{"state":"tesˇ-test","mode":"Insert"}}