From deb8b47ba5e844831712950758dccd50a579f0bd Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Mon, 29 Jan 2024 20:43:20 +0100 Subject: [PATCH 1/2] vim: fix `t` not being repeatable with `,` This fixes `t` not being repeatable with `,` and `;` in normal mode. Co-authored-by: Conrad --- assets/keymaps/vim.json | 7 +- crates/vim/src/motion.rs | 139 ++++++++++++------ .../vim/test_data/test_comma_semicolon.json | 17 +++ 3 files changed, 113 insertions(+), 50 deletions(-) diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 0bdf1aae32ad0f..da819c57f7a6b9 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -330,12 +330,7 @@ "*": "vim::MoveToNext", "#": "vim::MoveToPrev", ";": "vim::RepeatFind", - ",": [ - "vim::RepeatFind", - { - "backwards": true - } - ], + ",": "vim::RepeatFindReversed", "r": ["vim::PushOperator", "Replace"], "s": "vim::Substitute", "shift-s": "vim::SubstituteLine", diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index d7a46888cc9e95..f1417f555f4563 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -37,6 +37,8 @@ pub enum Motion { Matching, FindForward { before: bool, char: char }, FindBackward { after: bool, char: char }, + RepeatFind { last_find: Box }, + RepeatFindReversed { last_find: Box }, NextLineStart, StartOfLineDownward, EndOfLineDownward, @@ -99,16 +101,9 @@ pub struct StartOfLine { pub(crate) display_lines: bool, } -#[derive(Clone, Deserialize, PartialEq)] -struct RepeatFind { - #[serde(default)] - backwards: bool, -} - impl_actions!( vim, [ - RepeatFind, StartOfLine, EndOfLine, FirstNonWhitespace, @@ -136,6 +131,8 @@ actions!( StartOfLineDownward, EndOfLineDownward, GoToColumn, + RepeatFind, + RepeatFindReversed, ] ); @@ -228,8 +225,27 @@ pub fn register(workspace: &mut Workspace, _: &mut ViewContext) { }); workspace .register_action(|_: &mut Workspace, &GoToColumn, cx: _| motion(Motion::GoToColumn, cx)); - workspace.register_action(|_: &mut Workspace, action: &RepeatFind, cx: _| { - repeat_motion(action.backwards, cx) + + workspace.register_action(|_: &mut Workspace, _: &RepeatFind, cx: _| { + if let Some(last_find) = Vim::read(cx) + .workspace_state + .last_find + .clone() + .map(|f| Box::new(f)) + { + motion(Motion::RepeatFind { last_find }, cx); + } + }); + + workspace.register_action(|_: &mut Workspace, _: &RepeatFindReversed, cx: _| { + if let Some(last_find) = Vim::read(cx) + .workspace_state + .last_find + .clone() + .map(|f| Box::new(f)) + { + motion(Motion::RepeatFindReversed { last_find }, cx); + } }); } @@ -252,35 +268,6 @@ pub(crate) fn motion(motion: Motion, cx: &mut WindowContext) { Vim::update(cx, |vim, cx| vim.clear_operator(cx)); } -fn repeat_motion(backwards: bool, cx: &mut WindowContext) { - let find = match Vim::read(cx).workspace_state.last_find.clone() { - Some(Motion::FindForward { before, char }) => { - if backwards { - Motion::FindBackward { - after: before, - char, - } - } else { - Motion::FindForward { before, char } - } - } - - Some(Motion::FindBackward { after, char }) => { - if backwards { - Motion::FindForward { - before: after, - char, - } - } else { - Motion::FindBackward { after, char } - } - } - _ => return, - }; - - motion(find, cx) -} - // Motion handling is specified here: // https://github.com/vim/vim/blob/master/runtime/doc/motion.txt impl Motion { @@ -309,7 +296,9 @@ impl Motion { | NextWordStart { .. } | PreviousWordStart { .. } | FirstNonWhitespace { .. } - | FindBackward { .. } => false, + | FindBackward { .. } + | RepeatFind { .. } + | RepeatFindReversed { .. } => false, } } @@ -323,6 +312,7 @@ impl Motion { | NextWordEnd { .. } | Matching | FindForward { .. } + | RepeatFind { .. } | Left | Backspace | Right @@ -336,6 +326,7 @@ impl Motion { | PreviousWordStart { .. } | FirstNonWhitespace { .. } | FindBackward { .. } + | RepeatFindReversed { .. } | NextLineStart => false, } } @@ -366,6 +357,9 @@ impl Motion { | PreviousWordStart { .. } | FirstNonWhitespace { .. } | FindBackward { .. } => false, + RepeatFind { last_find: motion } | RepeatFindReversed { last_find: motion } => { + motion.inclusive() + } } } @@ -434,17 +428,58 @@ impl Motion { SelectionGoal::None, ), Matching => (matching(map, point), SelectionGoal::None), + // t f FindForward { before, char } => { - if let Some(new_point) = find_forward(map, point, *before, *char, times) { - return Some((new_point, SelectionGoal::None)); - } else { - return None; - } + return find_forward(map, point, *before, *char, times) + .map(|new_point| (new_point, SelectionGoal::None)) } + // T F FindBackward { after, char } => ( find_backward(map, point, *after, *char, times), SelectionGoal::None, ), + // ; -- repeat the last find done with t, f, T, F + RepeatFind { last_find } => match **last_find { + Motion::FindForward { before, char } => { + let mut new_point = find_forward(map, point, before, char, times); + if new_point == Some(point) { + new_point = find_forward(map, point, before, char, times + 1); + } + + return new_point.map(|new_point| (new_point, SelectionGoal::None)); + } + + Motion::FindBackward { after, char } => { + let mut new_point = find_backward(map, point, after, char, times); + if new_point == point { + new_point = find_backward(map, point, after, char, times + 1); + } + + (new_point, SelectionGoal::None) + } + _ => return None, + }, + // , -- repeat the last find done with t, f, T, F, in opposite direction + RepeatFindReversed { last_find } => match **last_find { + Motion::FindForward { before, char } => { + let mut new_point = find_backward(map, point, before, char, times); + if new_point == point { + new_point = find_backward(map, point, before, char, times + 1); + } + + (new_point, SelectionGoal::None) + } + + Motion::FindBackward { after, char } => { + let mut new_point = find_forward(map, point, after, char, times); + if new_point == Some(point) { + new_point = find_forward(map, point, after, char, times + 1); + } + + return new_point.map(|new_point| (new_point, SelectionGoal::None)); + } + _ => return None, + }, 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), @@ -1085,6 +1120,7 @@ mod test { async fn test_comma_semicolon(cx: &mut gpui::TestAppContext) { let mut cx = NeovimBackedTestContext::new(cx).await; + // f and F cx.set_shared_state("ˇone two three four").await; cx.simulate_shared_keystrokes(["f", "o"]).await; cx.assert_shared_state("one twˇo three four").await; @@ -1092,6 +1128,21 @@ mod test { cx.assert_shared_state("ˇone two three four").await; cx.simulate_shared_keystrokes(["2", ";"]).await; cx.assert_shared_state("one two three fˇour").await; + cx.simulate_shared_keystrokes(["shift-f", "e"]).await; + cx.assert_shared_state("one two threˇe four").await; + cx.simulate_shared_keystrokes(["2", ";"]).await; + cx.assert_shared_state("onˇe two three four").await; + cx.simulate_shared_keystrokes([","]).await; + cx.assert_shared_state("one two thrˇee four").await; + + // t and T + cx.set_shared_state("ˇone two three four").await; + cx.simulate_shared_keystrokes(["t", "o"]).await; + cx.assert_shared_state("one tˇwo three four").await; + cx.simulate_shared_keystrokes([","]).await; + cx.assert_shared_state("oˇne two three four").await; + cx.simulate_shared_keystrokes(["2", ";"]).await; + cx.assert_shared_state("one two three ˇfour").await; cx.simulate_shared_keystrokes(["shift-t", "e"]).await; cx.assert_shared_state("one two threeˇ four").await; cx.simulate_shared_keystrokes(["3", ";"]).await; diff --git a/crates/vim/test_data/test_comma_semicolon.json b/crates/vim/test_data/test_comma_semicolon.json index 8cde887ed1262b..e08b6963fa76a2 100644 --- a/crates/vim/test_data/test_comma_semicolon.json +++ b/crates/vim/test_data/test_comma_semicolon.json @@ -7,6 +7,23 @@ {"Key":"2"} {"Key":";"} {"Get":{"state":"one two three fˇour","mode":"Normal"}} +{"Key":"shift-f"} +{"Key":"e"} +{"Get":{"state":"one two threˇe four","mode":"Normal"}} +{"Key":"2"} +{"Key":";"} +{"Get":{"state":"onˇe two three four","mode":"Normal"}} +{"Key":","} +{"Get":{"state":"one two thrˇee four","mode":"Normal"}} +{"Put":{"state":"ˇone two three four"}} +{"Key":"t"} +{"Key":"o"} +{"Get":{"state":"one tˇwo three four","mode":"Normal"}} +{"Key":","} +{"Get":{"state":"oˇne two three four","mode":"Normal"}} +{"Key":"2"} +{"Key":";"} +{"Get":{"state":"one two three ˇfour","mode":"Normal"}} {"Key":"shift-t"} {"Key":"e"} {"Get":{"state":"one two threeˇ four","mode":"Normal"}} From a7da54715fc22cfdc218d0f8a7f91228c791060b Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Mon, 29 Jan 2024 20:56:57 +0100 Subject: [PATCH 2/2] Remove useless closure --- crates/vim/src/motion.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index f1417f555f4563..895b00888e5489 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -231,7 +231,7 @@ pub fn register(workspace: &mut Workspace, _: &mut ViewContext) { .workspace_state .last_find .clone() - .map(|f| Box::new(f)) + .map(Box::new) { motion(Motion::RepeatFind { last_find }, cx); } @@ -242,7 +242,7 @@ pub fn register(workspace: &mut Workspace, _: &mut ViewContext) { .workspace_state .last_find .clone() - .map(|f| Box::new(f)) + .map(Box::new) { motion(Motion::RepeatFindReversed { last_find }, cx); }