From 236f51cddda3a0715878416eb2f4b2f4adb21623 Mon Sep 17 00:00:00 2001 From: 5brian Date: Tue, 11 Feb 2025 00:26:26 -0500 Subject: [PATCH] vim: Update `vi{` (#24601) Small fix: Following up on https://github.com/zed-industries/zed/pull/24518 where i missed `vi{`. Matching neovim(tree-sitter), `vi{` should not have the newline selected (Now `vi{d`/`vi{c` can match `di{`/`ci{`). Also moved the cursor to the start. |prev|new|neovim| |---|---|---| |![image](https://github.com/user-attachments/assets/0311fbe5-df2e-4feb-977d-de33a3af7fdc)|![image](https://github.com/user-attachments/assets/a940c6ba-268b-4401-8c43-38ca17848542)|![image](https://github.com/user-attachments/assets/dab2c47d-660c-4ae3-bf79-635265222cc1)| Release Notes: - N/A --- crates/vim/src/object.rs | 60 ++++++++++++++++++- crates/vim/src/visual.rs | 5 +- ...ltiline_surrounding_character_objects.json | 13 ++-- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/crates/vim/src/object.rs b/crates/vim/src/object.rs index ed5e3c21bf..7b920c252f 100644 --- a/crates/vim/src/object.rs +++ b/crates/vim/src/object.rs @@ -422,7 +422,7 @@ impl Object { /// If the selection spans multiple lines and is preceded by an opening brace (`{`), /// this function will trim the selection to exclude the final newline /// in order to preserve a properly indented line. -fn preserve_indented_newline(map: &DisplaySnapshot, selection: &mut Selection) { +pub fn preserve_indented_newline(map: &DisplaySnapshot, selection: &mut Selection) { let (start_point, end_point) = (selection.start.to_point(map), selection.end.to_point(map)); if start_point.row == end_point.row { @@ -446,6 +446,7 @@ fn preserve_indented_newline(map: &DisplaySnapshot, selection: &mut Selection { selection.end = offset.to_display_point(map); + selection.reversed = true; break; } ch if !ch.is_whitespace() => break, @@ -1759,6 +1760,17 @@ mod test { Mode::Normal, ); cx.simulate_keystrokes("v i {"); + cx.assert_state( + indoc! { + "func empty(a string) bool { + «ˇif a == \"\" { + return true + } + return false» + }" + }, + Mode::Visual, + ); cx.set_state( indoc! { @@ -1772,6 +1784,17 @@ mod test { Mode::Normal, ); cx.simulate_keystrokes("v i {"); + cx.assert_state( + indoc! { + "func empty(a string) bool { + if a == \"\" { + «ˇreturn true» + } + return false + }" + }, + Mode::Visual, + ); cx.set_state( indoc! { @@ -1785,6 +1808,41 @@ mod test { Mode::Normal, ); cx.simulate_keystrokes("v i {"); + cx.assert_state( + indoc! { + "func empty(a string) bool { + if a == \"\" { + «ˇreturn true» + } + return false + }" + }, + Mode::Visual, + ); + + cx.set_state( + indoc! { + "func empty(a string) bool { + if a == \"\" { + return true + } + return false + ˇ}" + }, + Mode::Normal, + ); + cx.simulate_keystrokes("v i {"); + cx.assert_state( + indoc! { + "func empty(a string) bool { + «ˇif a == \"\" { + return true + } + return false» + }" + }, + Mode::Visual, + ); } #[gpui::test] diff --git a/crates/vim/src/visual.rs b/crates/vim/src/visual.rs index a9e4dd9767..1a4cda5e22 100644 --- a/crates/vim/src/visual.rs +++ b/crates/vim/src/visual.rs @@ -16,7 +16,7 @@ use workspace::searchable::Direction; use crate::{ motion::{first_non_whitespace, next_line_end, start_of_line, Motion}, - object::Object, + object::{self, Object}, state::{Mode, Operator}, Vim, }; @@ -375,6 +375,9 @@ impl Vim { } else { selection.end = range.end; } + if !around && object.is_multiline() { + object::preserve_indented_newline(map, selection); + } } // In the visual selection result of a paragraph object, the cursor is diff --git a/crates/vim/test_data/test_multiline_surrounding_character_objects.json b/crates/vim/test_data/test_multiline_surrounding_character_objects.json index 973df647a2..c61b7b9145 100644 --- a/crates/vim/test_data/test_multiline_surrounding_character_objects.json +++ b/crates/vim/test_data/test_multiline_surrounding_character_objects.json @@ -1,15 +1,20 @@ -{"Put":{"state":"func empty(a string) bool {\n if a == \"\" {\n return true\n }\n ˇreturn false\n}"}} +{"Put":{"state":"func empty(a string) bool {\n if a == \"\" {\n return true\n }\n ˇreturn false\n}"}} {"Key":"v"} {"Key":"i"} {"Key":"{"} -{"Get":{"state":"func empty(a string) bool {\n« if a == \"\" {\n return true\n }\n return false\nˇ»}","mode":"Visual"}} +{"Get":{"state":"func empty(a string) bool {\n «ˇif a == \"\" {\n return true\n }\n return false»\n}","mode":"Visual"}} {"Put":{"state":"func empty(a string) bool {\n if a == \"\" {\n ˇreturn true\n }\n return false\n}"}} {"Key":"v"} {"Key":"i"} {"Key":"{"} -{"Get":{"state":"func empty(a string) bool {\n if a == \"\" {\n« return true\nˇ» }\n return false\n}","mode":"Visual"}} +{"Get":{"state":"func empty(a string) bool {\n if a == \"\" {\n «ˇreturn true»\n }\n return false\n}","mode":"Visual"}} {"Put":{"state":"func empty(a string) bool {\n if a == \"\" ˇ{\n return true\n }\n return false\n}"}} {"Key":"v"} {"Key":"i"} {"Key":"{"} -{"Get":{"state":"func empty(a string) bool {\n if a == \"\" {\n« return true\nˇ» }\n return false\n}","mode":"Visual"}} +{"Get":{"state":"func empty(a string) bool {\n if a == \"\" {\n «ˇreturn true»\n }\n return false\n}","mode":"Visual"}} +{"Put":{"state":"func empty(a string) bool {\n if a == \"\" {\n return true\n }\n return false\nˇ}"}} +{"Key":"v"} +{"Key":"i"} +{"Key":"{"} +{"Get":{"state":"func empty(a string) bool {\n «ˇif a == \"\" {\n return true\n }\n return false»\n}","mode":"Visual"}}