vim: Prevent around word operations from selecting indentation (#24635)

Closes https://github.com/zed-industries/zed/issues/15323

Changes:
Added check for first word on line

Tested `v/c/d/y aw`. Matches standard neovim.

|initial|old|new|
|---|---|---|

|![image](https://github.com/user-attachments/assets/725b74e6-3aa0-40dc-9fd2-4d2b593e9926)|![image](https://github.com/user-attachments/assets/eeebd267-b4c6-4ea6-bb9a-fb913614754c)|![image](https://github.com/user-attachments/assets/fb695e54-b4c2-44a6-a588-909c1fd415e0)



Release Notes:

- vim: Prevent around word operations from selecting indentation
This commit is contained in:
5brian 2025-02-11 13:35:59 -05:00 committed by GitHub
parent 7378ab9ba5
commit 0a146793ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 74 additions and 2 deletions

View file

@ -727,8 +727,25 @@ fn around_containing_word(
relative_to: DisplayPoint,
ignore_punctuation: bool,
) -> Option<Range<DisplayPoint>> {
in_word(map, relative_to, ignore_punctuation)
.map(|range| expand_to_include_whitespace(map, range, true))
in_word(map, relative_to, ignore_punctuation).map(|range| {
let line_start = DisplayPoint::new(range.start.row(), 0);
let is_first_word = map
.buffer_chars_at(line_start.to_offset(map, Bias::Left))
.take_while(|(ch, offset)| {
offset < &range.start.to_offset(map, Bias::Left) && ch.is_whitespace()
})
.count()
> 0;
if is_first_word {
// For first word on line, trim indentation
let mut expanded = expand_to_include_whitespace(map, range.clone(), true);
expanded.start = range.start;
expanded
} else {
expand_to_include_whitespace(map, range, true)
}
})
}
fn around_next_word(
@ -2455,4 +2472,36 @@ mod test {
Mode::Visual,
);
}
#[gpui::test]
async fn test_around_containing_word_indent(cx: &mut gpui::TestAppContext) {
let mut cx = NeovimBackedTestContext::new(cx).await;
cx.set_shared_state(" ˇconst f = (x: unknown) => {")
.await;
cx.simulate_shared_keystrokes("v a w").await;
cx.shared_state()
.await
.assert_eq(" «const ˇ»f = (x: unknown) => {");
cx.set_shared_state(" ˇconst f = (x: unknown) => {")
.await;
cx.simulate_shared_keystrokes("y a w").await;
cx.shared_clipboard().await.assert_eq("const ");
cx.set_shared_state(" ˇconst f = (x: unknown) => {")
.await;
cx.simulate_shared_keystrokes("d a w").await;
cx.shared_state()
.await
.assert_eq(" ˇf = (x: unknown) => {");
cx.shared_clipboard().await.assert_eq("const ");
cx.set_shared_state(" ˇconst f = (x: unknown) => {")
.await;
cx.simulate_shared_keystrokes("c a w").await;
cx.shared_state()
.await
.assert_eq(" ˇf = (x: unknown) => {");
cx.shared_clipboard().await.assert_eq("const ");
}
}

View file

@ -0,0 +1,23 @@
{"Put":{"state":" ˇconst f = (x: unknown) => {"}}
{"Key":"v"}
{"Key":"a"}
{"Key":"w"}
{"Get":{"state":" «const ˇ»f = (x: unknown) => {","mode":"Visual"}}
{"Put":{"state":" ˇconst f = (x: unknown) => {"}}
{"Key":"y"}
{"Key":"a"}
{"Key":"w"}
{"Get":{"state":" ˇconst f = (x: unknown) => {","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"const "}}
{"Put":{"state":" ˇconst f = (x: unknown) => {"}}
{"Key":"d"}
{"Key":"a"}
{"Key":"w"}
{"Get":{"state":" ˇf = (x: unknown) => {","mode":"Normal"}}
{"ReadRegister":{"name":"\"","value":"const "}}
{"Put":{"state":" ˇconst f = (x: unknown) => {"}}
{"Key":"c"}
{"Key":"a"}
{"Key":"w"}
{"Get":{"state":" ˇf = (x: unknown) => {","mode":"Insert"}}
{"ReadRegister":{"name":"\"","value":"const "}}