From 1a62396b1ee9cd7c8beed803539728ed8a8c784f Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 18 Sep 2024 08:19:06 -0600 Subject: [PATCH] vim: Fix gv after indent/toggle comments (#17986) Release Notes: - vim: Fixed `gv` after > and < in visual mode --- crates/vim/src/{normal => }/indent.rs | 62 +++++++++++++- crates/vim/src/normal.rs | 104 ++++++++--------------- crates/vim/src/normal/mark.rs | 2 +- crates/vim/src/vim.rs | 2 + crates/vim/test_data/test_indent_gv.json | 8 ++ 5 files changed, 107 insertions(+), 71 deletions(-) rename crates/vim/src/{normal => }/indent.rs (58%) create mode 100644 crates/vim/test_data/test_indent_gv.json diff --git a/crates/vim/src/normal/indent.rs b/crates/vim/src/indent.rs similarity index 58% rename from crates/vim/src/normal/indent.rs rename to crates/vim/src/indent.rs index 4b4d5e7e80..676713c816 100644 --- a/crates/vim/src/normal/indent.rs +++ b/crates/vim/src/indent.rs @@ -1,6 +1,7 @@ -use crate::{motion::Motion, object::Object, Vim}; +use crate::{motion::Motion, object::Object, state::Mode, Vim}; use collections::HashMap; -use editor::{display_map::ToDisplayPoint, Bias}; +use editor::{display_map::ToDisplayPoint, Bias, Editor}; +use gpui::actions; use language::SelectionGoal; use ui::ViewContext; @@ -10,6 +11,46 @@ pub(crate) enum IndentDirection { Out, } +actions!(vim, [Indent, Outdent,]); + +pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext) { + Vim::action(editor, cx, |vim, _: &Indent, cx| { + vim.record_current_action(cx); + let count = vim.take_count(cx).unwrap_or(1); + vim.store_visual_marks(cx); + vim.update_editor(cx, |vim, editor, cx| { + editor.transact(cx, |editor, cx| { + let mut original_positions = vim.save_selection_starts(editor, cx); + for _ in 0..count { + editor.indent(&Default::default(), cx); + } + vim.restore_selection_cursors(editor, cx, &mut original_positions); + }); + }); + if vim.mode.is_visual() { + vim.switch_mode(Mode::Normal, true, cx) + } + }); + + Vim::action(editor, cx, |vim, _: &Outdent, cx| { + vim.record_current_action(cx); + let count = vim.take_count(cx).unwrap_or(1); + vim.store_visual_marks(cx); + vim.update_editor(cx, |vim, editor, cx| { + editor.transact(cx, |editor, cx| { + let mut original_positions = vim.save_selection_starts(editor, cx); + for _ in 0..count { + editor.outdent(&Default::default(), cx); + } + vim.restore_selection_cursors(editor, cx, &mut original_positions); + }); + }); + if vim.mode.is_visual() { + vim.switch_mode(Mode::Normal, true, cx) + } + }); +} + impl Vim { pub(crate) fn indent_motion( &mut self, @@ -78,3 +119,20 @@ impl Vim { }); } } + +#[cfg(test)] +mod test { + use crate::test::NeovimBackedTestContext; + + #[gpui::test] + async fn test_indent_gv(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + cx.set_neovim_option("shiftwidth=4").await; + + cx.set_shared_state("ˇhello\nworld\n").await; + cx.simulate_shared_keystrokes("v j > g v").await; + cx.shared_state() + .await + .assert_eq("« hello\n ˇ» world\n"); + } +} diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index 8198c0da53..741e09f178 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -2,7 +2,6 @@ mod case; mod change; mod delete; mod increment; -mod indent; pub(crate) mod mark; mod paste; pub(crate) mod repeat; @@ -16,6 +15,7 @@ use std::collections::HashMap; use std::sync::Arc; use crate::{ + indent::IndentDirection, motion::{self, first_non_whitespace, next_line_end, right, Motion}, object::Object, state::{Mode, Operator}, @@ -34,8 +34,6 @@ use language::{Point, SelectionGoal}; use log::error; use multi_buffer::MultiBufferRow; -use self::indent::IndentDirection; - actions!( vim, [ @@ -56,8 +54,6 @@ actions!( ConvertToUpperCase, ConvertToLowerCase, JoinLines, - Indent, - Outdent, ToggleComments, Undo, Redo, @@ -129,41 +125,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext) { }) }); if vim.mode.is_visual() { - vim.switch_mode(Mode::Normal, false, cx) - } - }); - - Vim::action(editor, cx, |vim, _: &Indent, cx| { - vim.record_current_action(cx); - let count = vim.take_count(cx).unwrap_or(1); - vim.update_editor(cx, |_, editor, cx| { - editor.transact(cx, |editor, cx| { - let mut original_positions = save_selection_starts(editor, cx); - for _ in 0..count { - editor.indent(&Default::default(), cx); - } - restore_selection_cursors(editor, cx, &mut original_positions); - }); - }); - if vim.mode.is_visual() { - vim.switch_mode(Mode::Normal, false, cx) - } - }); - - Vim::action(editor, cx, |vim, _: &Outdent, cx| { - vim.record_current_action(cx); - let count = vim.take_count(cx).unwrap_or(1); - vim.update_editor(cx, |_, editor, cx| { - editor.transact(cx, |editor, cx| { - let mut original_positions = save_selection_starts(editor, cx); - for _ in 0..count { - editor.outdent(&Default::default(), cx); - } - restore_selection_cursors(editor, cx, &mut original_positions); - }); - }); - if vim.mode.is_visual() { - vim.switch_mode(Mode::Normal, false, cx) + vim.switch_mode(Mode::Normal, true, cx) } }); @@ -428,15 +390,16 @@ impl Vim { fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext) { self.record_current_action(cx); - self.update_editor(cx, |_, editor, cx| { + self.store_visual_marks(cx); + self.update_editor(cx, |vim, editor, cx| { editor.transact(cx, |editor, cx| { - let mut original_positions = save_selection_starts(editor, cx); + let mut original_positions = vim.save_selection_starts(editor, cx); editor.toggle_comments(&Default::default(), cx); - restore_selection_cursors(editor, cx, &mut original_positions); + vim.restore_selection_cursors(editor, cx, &mut original_positions); }); }); if self.mode.is_visual() { - self.switch_mode(Mode::Normal, false, cx) + self.switch_mode(Mode::Normal, true, cx) } } @@ -480,33 +443,38 @@ impl Vim { }); self.pop_operator(cx); } -} -fn save_selection_starts(editor: &Editor, cx: &mut ViewContext) -> HashMap { - let (map, selections) = editor.selections.all_display(cx); - selections - .iter() - .map(|selection| { - ( - selection.id, - map.display_point_to_anchor(selection.start, Bias::Right), - ) - }) - .collect::>() -} + pub fn save_selection_starts( + &self, + editor: &Editor, + cx: &mut ViewContext, + ) -> HashMap { + let (map, selections) = editor.selections.all_display(cx); + selections + .iter() + .map(|selection| { + ( + selection.id, + map.display_point_to_anchor(selection.start, Bias::Right), + ) + }) + .collect::>() + } -fn restore_selection_cursors( - editor: &mut Editor, - cx: &mut ViewContext, - positions: &mut HashMap, -) { - editor.change_selections(Some(Autoscroll::fit()), cx, |s| { - s.move_with(|map, selection| { - if let Some(anchor) = positions.remove(&selection.id) { - selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None); - } + pub fn restore_selection_cursors( + &self, + editor: &mut Editor, + cx: &mut ViewContext, + positions: &mut HashMap, + ) { + editor.change_selections(Some(Autoscroll::fit()), cx, |s| { + s.move_with(|map, selection| { + if let Some(anchor) = positions.remove(&selection.id) { + selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None); + } + }); }); - }); + } } #[cfg(test)] mod test { diff --git a/crates/vim/src/normal/mark.rs b/crates/vim/src/normal/mark.rs index ae6dd3eed7..787430e747 100644 --- a/crates/vim/src/normal/mark.rs +++ b/crates/vim/src/normal/mark.rs @@ -54,7 +54,7 @@ impl Vim { ); starts.push( map.buffer_snapshot - .anchor_after(selection.start.to_offset(&map, Bias::Right)), + .anchor_before(selection.start.to_offset(&map, Bias::Left)), ); reversed.push(selection.reversed) } diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index fc5097d845..a4b77b1a7a 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -6,6 +6,7 @@ mod test; mod change_list; mod command; mod digraph; +mod indent; mod insert; mod mode_indicator; mod motion; @@ -289,6 +290,7 @@ impl Vim { motion::register(editor, cx); command::register(editor, cx); replace::register(editor, cx); + indent::register(editor, cx); object::register(editor, cx); visual::register(editor, cx); change_list::register(editor, cx); diff --git a/crates/vim/test_data/test_indent_gv.json b/crates/vim/test_data/test_indent_gv.json new file mode 100644 index 0000000000..2c24406aee --- /dev/null +++ b/crates/vim/test_data/test_indent_gv.json @@ -0,0 +1,8 @@ +{"SetOption":{"value":"shiftwidth=4"}} +{"Put":{"state":"ˇhello\nworld\n"}} +{"Key":"v"} +{"Key":"j"} +{"Key":">"} +{"Key":"g"} +{"Key":"v"} +{"Get":{"state":"« hello\n ˇ» world\n","mode":"Visual"}}