Fix VIM cw on last character of a word doesn't work as expected: (#10963)

At the moment, using the default expand_selection seems to do the job
well, without the need for some additional logic, which may also make
the code a little clearer, Fix #10945



Release Notes:


- N/A
This commit is contained in:
Hans 2024-04-26 11:09:06 +08:00 committed by GitHub
parent d9d509a2bb
commit 5c2f27a501
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 61 deletions

View File

@ -31,48 +31,42 @@ pub fn change_motion(vim: &mut Vim, motion: Motion, times: Option<usize>, cx: &m
editor.set_clip_at_line_ends(false, cx); editor.set_clip_at_line_ends(false, cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| { s.move_with(|map, selection| {
motion_succeeded |= if let Motion::NextWordStart { ignore_punctuation } = motion motion_succeeded |= match motion {
{ Motion::NextWordStart { ignore_punctuation }
expand_changed_word_selection( | Motion::NextSubwordStart { ignore_punctuation } => {
map, expand_changed_word_selection(
selection, map,
times, selection,
ignore_punctuation, times,
&text_layout_details, ignore_punctuation,
false, &text_layout_details,
) motion == Motion::NextSubwordStart { ignore_punctuation },
} else if let Motion::NextSubwordStart { ignore_punctuation } = motion { )
expand_changed_word_selection(
map,
selection,
times,
ignore_punctuation,
&text_layout_details,
true,
)
} else {
let result = motion.expand_selection(
map,
selection,
times,
false,
&text_layout_details,
);
if let Motion::CurrentLine = motion {
let mut start_offset = selection.start.to_offset(map, Bias::Left);
let scope = map
.buffer_snapshot
.language_scope_at(selection.start.to_point(&map));
for (ch, offset) in map.buffer_chars_at(start_offset) {
if ch == '\n' || char_kind(&scope, ch) != CharKind::Whitespace {
break;
}
start_offset = offset + ch.len_utf8();
}
selection.start = start_offset.to_display_point(map);
} }
result _ => {
}; let result = motion.expand_selection(
map,
selection,
times,
false,
&text_layout_details,
);
if let Motion::CurrentLine = motion {
let mut start_offset = selection.start.to_offset(map, Bias::Left);
let scope = map
.buffer_snapshot
.language_scope_at(selection.start.to_point(&map));
for (ch, offset) in map.buffer_chars_at(start_offset) {
if ch == '\n' || char_kind(&scope, ch) != CharKind::Whitespace {
break;
}
start_offset = offset + ch.len_utf8();
}
selection.start = start_offset.to_display_point(map);
}
result
}
}
}); });
}); });
copy_selections_content(vim, editor, motion.linewise(), cx); copy_selections_content(vim, editor, motion.linewise(), cx);
@ -116,8 +110,8 @@ pub fn change_object(vim: &mut Vim, object: Object, around: bool, cx: &mut Windo
// Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is // Special case: "cw" and "cW" are treated like "ce" and "cE" if the cursor is
// on a non-blank. This is because "cw" is interpreted as change-word, and a // on a non-blank. This is because "cw" is interpreted as change-word, and a
// word does not include the following white space. {Vi: "cw" when on a blank // word does not include the following white space. {Vi: "cw" when on a blank
// followed by other blanks changes only the first blank; this is probably a // followed by other blanks changes only the first blank; this is probably a
// bug, because "dw" deletes all the blanks} // bug, because "dw" deletes all the blanks}
fn expand_changed_word_selection( fn expand_changed_word_selection(
map: &DisplaySnapshot, map: &DisplaySnapshot,
selection: &mut Selection<DisplayPoint>, selection: &mut Selection<DisplayPoint>,
@ -126,7 +120,7 @@ fn expand_changed_word_selection(
text_layout_details: &TextLayoutDetails, text_layout_details: &TextLayoutDetails,
use_subword: bool, use_subword: bool,
) -> bool { ) -> bool {
if times.is_none() || times.unwrap() == 1 { let is_in_word = || {
let scope = map let scope = map
.buffer_snapshot .buffer_snapshot
.language_scope_at(selection.start.to_point(map)); .language_scope_at(selection.start.to_point(map));
@ -135,25 +129,28 @@ fn expand_changed_word_selection(
.next() .next()
.map(|(c, _)| char_kind(&scope, c) != CharKind::Whitespace) .map(|(c, _)| char_kind(&scope, c) != CharKind::Whitespace)
.unwrap_or_default(); .unwrap_or_default();
return in_word;
if in_word { };
if use_subword { if (times.is_none() || times.unwrap() == 1) && is_in_word() {
selection.end = let next_char = map
motion::next_subword_end(map, selection.end, ignore_punctuation, 1, false); .buffer_chars_at(
} else { motion::next_char(map, selection.end, false).to_offset(map, Bias::Left),
selection.end = )
motion::next_word_end(map, selection.end, ignore_punctuation, 1, false); .next();
match next_char {
Some((' ', _)) => selection.end = motion::next_char(map, selection.end, false),
_ => {
if use_subword {
selection.end =
motion::next_subword_end(map, selection.end, ignore_punctuation, 1, false);
} else {
selection.end =
motion::next_word_end(map, selection.end, ignore_punctuation, 1, false);
}
selection.end = motion::next_char(map, selection.end, false);
} }
selection.end = motion::next_char(map, selection.end, false);
true
} else {
let motion = if use_subword {
Motion::NextSubwordStart { ignore_punctuation }
} else {
Motion::NextWordStart { ignore_punctuation }
};
motion.expand_selection(map, selection, None, false, &text_layout_details)
} }
true
} else { } else {
let motion = if use_subword { let motion = if use_subword {
Motion::NextSubwordStart { ignore_punctuation } Motion::NextSubwordStart { ignore_punctuation }
@ -209,6 +206,7 @@ mod test {
cx.assert("Teˇst").await; cx.assert("Teˇst").await;
cx.assert("Tˇest test").await; cx.assert("Tˇest test").await;
cx.assert("Testˇ test").await; cx.assert("Testˇ test").await;
cx.assert("Tesˇt test").await;
cx.assert(indoc! {" cx.assert(indoc! {"
Test teˇst Test teˇst
test"}) test"})

View File

@ -10,6 +10,10 @@
{"Key":"c"} {"Key":"c"}
{"Key":"w"} {"Key":"w"}
{"Get":{"state":"Testˇtest","mode":"Insert"}} {"Get":{"state":"Testˇtest","mode":"Insert"}}
{"Put":{"state":"Tesˇt test"}}
{"Key":"c"}
{"Key":"w"}
{"Get":{"state":"Tesˇ test","mode":"Insert"}}
{"Put":{"state":"Test teˇst\ntest"}} {"Put":{"state":"Test teˇst\ntest"}}
{"Key":"c"} {"Key":"c"}
{"Key":"w"} {"Key":"w"}