diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index b99b64c02d..59b21ae345 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -210,7 +210,8 @@ "ctrl-w space": "editor::OpenExcerptsSplit", "ctrl-w g space": "editor::OpenExcerptsSplit", "ctrl-6": "pane::AlternateFile", - "ctrl-^": "pane::AlternateFile" + "ctrl-^": "pane::AlternateFile", + ".": "vim::Repeat" } }, { @@ -219,7 +220,6 @@ "ctrl-[": "editor::Cancel", "escape": "editor::Cancel", ":": "command_palette::Toggle", - ".": "vim::Repeat", "c": "vim::PushChange", "shift-c": "vim::ChangeToEndOfLine", "d": "vim::PushDelete", diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index 49f07954ff..8799a8b635 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -245,61 +245,63 @@ impl Vim { }) else { return; }; - if let Some(mode) = mode { - self.switch_mode(mode, false, window, cx) - } + if mode != Some(self.mode) { + if let Some(mode) = mode { + self.switch_mode(mode, false, window, cx) + } - match selection { - RecordedSelection::SingleLine { cols } => { - if cols > 1 { - self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx) + match selection { + RecordedSelection::SingleLine { cols } => { + if cols > 1 { + self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx) + } } - } - RecordedSelection::Visual { rows, cols } => { - self.visual_motion( - Motion::Down { - display_lines: false, - }, - Some(rows as usize), - window, - cx, - ); - self.visual_motion( - Motion::StartOfLine { - display_lines: false, - }, - None, - window, - cx, - ); - if cols > 1 { - self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx) + RecordedSelection::Visual { rows, cols } => { + self.visual_motion( + Motion::Down { + display_lines: false, + }, + Some(rows as usize), + window, + cx, + ); + self.visual_motion( + Motion::StartOfLine { + display_lines: false, + }, + None, + window, + cx, + ); + if cols > 1 { + self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx) + } } - } - RecordedSelection::VisualBlock { rows, cols } => { - self.visual_motion( - Motion::Down { - display_lines: false, - }, - Some(rows as usize), - window, - cx, - ); - if cols > 1 { - self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx); + RecordedSelection::VisualBlock { rows, cols } => { + self.visual_motion( + Motion::Down { + display_lines: false, + }, + Some(rows as usize), + window, + cx, + ); + if cols > 1 { + self.visual_motion(Motion::Right, Some(cols as usize - 1), window, cx); + } } + RecordedSelection::VisualLine { rows } => { + self.visual_motion( + Motion::Down { + display_lines: false, + }, + Some(rows as usize), + window, + cx, + ); + } + RecordedSelection::None => {} } - RecordedSelection::VisualLine { rows } => { - self.visual_motion( - Motion::Down { - display_lines: false, - }, - Some(rows as usize), - window, - cx, - ); - } - RecordedSelection::None => {} } // insert internally uses repeat to handle counts diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index e62d8c58ef..2db1d4a20c 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -2071,3 +2071,42 @@ async fn test_paragraph_multi_delete(cx: &mut gpui::TestAppContext) { cx.simulate_shared_keystrokes("4 d a p").await; cx.shared_state().await.assert_eq(indoc! {"ˇ"}); } + +#[gpui::test] +async fn test_multi_cursor_replay(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + cx.set_state( + indoc! { + " + oˇne one one + + two two two + " + }, + Mode::Normal, + ); + + cx.simulate_keystrokes("3 g l s wow escape escape"); + cx.assert_state( + indoc! { + " + woˇw wow wow + + two two two + " + }, + Mode::Normal, + ); + + cx.simulate_keystrokes("2 j 3 g l ."); + cx.assert_state( + indoc! { + " + wow wow wow + + woˇw woˇw woˇw + " + }, + Mode::Normal, + ); +}