From f39a24255c4c0f2ab151d0669c28cf791900428e Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 1 May 2024 11:50:04 +0800 Subject: [PATCH] Adjust the yss surrounds operator (#11212) For #11084 In the case of an indentation in front of the current line, it may also be necessary to deal with the start point of the selected range Release Notes: - N/A --------- Co-authored-by: Conrad Irwin --- crates/vim/src/motion.rs | 33 ++++++++++- crates/vim/src/surrounds.rs | 58 +++++++++++++++---- crates/vim/src/vim.rs | 2 +- .../test_data/test_end_of_line_downward.json | 8 +++ 4 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 crates/vim/test_data/test_end_of_line_downward.json diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 12b9b70a42..2c50f52f5a 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -447,6 +447,7 @@ pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) { vim.clear_operator(cx); if let Some(operator) = waiting_operator { vim.push_operator(operator, cx); + vim.update_state(|state| state.pre_count = count) } }); } @@ -755,7 +756,7 @@ impl Motion { }, NextLineStart => (next_line_start(map, point, times), SelectionGoal::None), StartOfLineDownward => (next_line_start(map, point, times - 1), SelectionGoal::None), - EndOfLineDownward => (next_line_end(map, point, times), SelectionGoal::None), + EndOfLineDownward => (last_non_whitespace(map, point, times), SelectionGoal::None), GoToColumn => (go_to_column(map, point, times), SelectionGoal::None), WindowTop => window_top(map, point, &text_layout_details, times - 1), WindowMiddle => window_middle(map, point, &text_layout_details), @@ -1422,6 +1423,26 @@ pub(crate) fn first_non_whitespace( start_offset.to_display_point(map) } +pub(crate) fn last_non_whitespace( + map: &DisplaySnapshot, + from: DisplayPoint, + count: usize, +) -> DisplayPoint { + let mut end_of_line = end_of_line(map, false, from, count).to_offset(map, Bias::Left); + let scope = map.buffer_snapshot.language_scope_at(from.to_point(map)); + for (ch, offset) in map.reverse_buffer_chars_at(end_of_line) { + if ch == '\n' { + break; + } + end_of_line = offset; + if char_kind(&scope, ch) != CharKind::Whitespace || ch == '\n' { + break; + } + } + + end_of_line.to_display_point(map) +} + pub(crate) fn start_of_line( map: &DisplaySnapshot, display_lines: bool, @@ -1899,6 +1920,16 @@ mod test { cx.assert_shared_state("one\n ˇtwo\nthree").await; } + #[gpui::test] + async fn test_end_of_line_downward(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.set_shared_state("ˇ one \n two \nthree").await; + cx.simulate_shared_keystrokes(["g", "_"]).await; + cx.assert_shared_state(" onˇe \n two \nthree").await; + cx.simulate_shared_keystrokes(["2", "g", "_"]).await; + cx.assert_shared_state(" one \n twˇo \nthree").await; + } + #[gpui::test] async fn test_window_top(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; diff --git a/crates/vim/src/surrounds.rs b/crates/vim/src/surrounds.rs index f86e2dc21f..6bc8787efc 100644 --- a/crates/vim/src/surrounds.rs +++ b/crates/vim/src/surrounds.rs @@ -1,4 +1,9 @@ -use crate::{motion::Motion, object::Object, state::Mode, Vim}; +use crate::{ + motion::{self, Motion}, + object::Object, + state::Mode, + Vim, +}; use editor::{movement, scroll::Autoscroll, Bias}; use gpui::WindowContext; use language::BracketPair; @@ -23,6 +28,7 @@ impl<'de> Deserialize<'de> for SurroundsType { pub fn add_surrounds(text: Arc, target: SurroundsType, cx: &mut WindowContext) { Vim::update(cx, |vim, cx| { vim.stop_recording(); + let count = vim.take_count(cx); vim.update_active_editor(cx, |_, editor, cx| { let text_layout_details = editor.text_layout_details(cx); editor.transact(cx, |editor, cx| { @@ -52,22 +58,26 @@ pub fn add_surrounds(text: Arc, target: SurroundsType, cx: &mut WindowConte .range( &display_map, selection.clone(), - Some(1), + count, true, &text_layout_details, ) .map(|mut range| { - // The Motion::CurrentLine operation will contain the newline of the current line, - // so we need to deal with this edge case + // The Motion::CurrentLine operation will contain the newline of the current line and leading/trailing whitespace if let Motion::CurrentLine = motion { - let offset = range.end.to_offset(&display_map, Bias::Left); - if let Some((last_ch, _)) = - display_map.reverse_buffer_chars_at(offset).next() - { - if last_ch == '\n' { - range.end = movement::left(&display_map, range.end); - } - } + range.start = motion::first_non_whitespace( + &display_map, + false, + range.start, + ); + range.end = movement::saturating_right( + &display_map, + motion::last_non_whitespace( + &display_map, + movement::left(&display_map, range.end), + 1, + ), + ); } range }); @@ -627,6 +637,30 @@ mod test { the lazy dog."}, Mode::Normal, ); + + cx.set_state( + indoc! {" + The quˇick brown• + fox jumps over + the lazy dog."}, + Mode::Normal, + ); + cx.simulate_keystrokes(["y", "s", "s", "{"]); + cx.assert_state( + indoc! {" + ˇ{ The quick brown }• + fox jumps over + the lazy dog."}, + Mode::Normal, + ); + cx.simulate_keystrokes(["2", "y", "s", "s", ")"]); + cx.assert_state( + indoc! {" + ˇ({ The quick brown }• + fox jumps over) + the lazy dog."}, + Mode::Normal, + ); } #[gpui::test] diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 565e34f9a2..4d0837247d 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -526,7 +526,7 @@ impl Vim { | Operator::ChangeSurrounds { .. } | Operator::DeleteSurrounds ) { - self.clear_operator(cx); + self.update_state(|state| state.operator_stack.clear()); }; self.update_state(|state| state.operator_stack.push(operator)); self.sync_vim_settings(cx); diff --git a/crates/vim/test_data/test_end_of_line_downward.json b/crates/vim/test_data/test_end_of_line_downward.json new file mode 100644 index 0000000000..be73805cdc --- /dev/null +++ b/crates/vim/test_data/test_end_of_line_downward.json @@ -0,0 +1,8 @@ +{"Put":{"state":"ˇ one \n two \nthree"}} +{"Key":"g"} +{"Key":"_"} +{"Get":{"state":" onˇe \n two \nthree","mode":"Normal"}} +{"Key":"2"} +{"Key":"g"} +{"Key":"_"} +{"Get":{"state":" one \n twˇo \nthree","mode":"Normal"}}