From 1fc0642de1f8f8f53ee5b74e7e5be44d932ad315 Mon Sep 17 00:00:00 2001 From: AidanV <84053180+AidanV@users.noreply.github.com> Date: Wed, 12 Nov 2025 22:46:14 -0800 Subject: [PATCH] vim: Make each vim repeat its own transaction (#41735) Release Notes: - Pressing `u` after multiple `.` in rapid succession will now only undo the latest repeat instead of all repeats. --------- Co-authored-by: Conrad Irwin --- crates/vim/src/normal/repeat.rs | 19 ++++++++++++++++++- crates/vim/src/test.rs | 16 ++++++++++++++++ .../test_data/test_repeat_grouping_41735.json | 10 ++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 crates/vim/test_data/test_repeat_grouping_41735.json diff --git a/crates/vim/src/normal/repeat.rs b/crates/vim/src/normal/repeat.rs index 2d7927480869f7a14cff7e2051ec421268df1d97..e0b515595db013b23730c535df64123aa4dd6707 100644 --- a/crates/vim/src/normal/repeat.rs +++ b/crates/vim/src/normal/repeat.rs @@ -110,7 +110,24 @@ impl Replayer { } lock.running = true; let this = self.clone(); - window.defer(cx, move |window, cx| this.next(window, cx)) + window.defer(cx, move |window, cx| { + this.next(window, cx); + let Some(Some(workspace)) = window.root::() else { + return; + }; + let Some(editor) = workspace + .read(cx) + .active_item(cx) + .and_then(|item| item.act_as::(cx)) + else { + return; + }; + editor.update(cx, |editor, cx| { + editor + .buffer() + .update(cx, |multi, cx| multi.finalize_last_transaction(cx)) + }); + }) } pub fn stop(self) { diff --git a/crates/vim/src/test.rs b/crates/vim/src/test.rs index cb02a3ab0fafdeec254e8b3722bdd877fbeda0e2..d6aa116e8ddb12c0f3aff15fbe971b701fe90ab7 100644 --- a/crates/vim/src/test.rs +++ b/crates/vim/src/test.rs @@ -2365,3 +2365,19 @@ async fn test_wrap_selections_in_tag_line_mode(cx: &mut gpui::TestAppContext) { Mode::VisualLine, ); } + +#[gpui::test] +async fn test_repeat_grouping_41735(cx: &mut gpui::TestAppContext) { + let mut cx = NeovimBackedTestContext::new(cx).await; + + // typically transaction gropuing is disabled in tests, but here we need to test it. + cx.update_buffer(|buffer, _cx| buffer.set_group_interval(Duration::from_millis(300))); + + cx.set_shared_state("ˇ").await; + + cx.simulate_shared_keystrokes("i a escape").await; + cx.simulate_shared_keystrokes(". . .").await; + cx.shared_state().await.assert_eq("ˇaaaa"); + cx.simulate_shared_keystrokes("u").await; + cx.shared_state().await.assert_eq("ˇaaa"); +} diff --git a/crates/vim/test_data/test_repeat_grouping_41735.json b/crates/vim/test_data/test_repeat_grouping_41735.json new file mode 100644 index 0000000000000000000000000000000000000000..6523be6e4bebad7162a15da1af3455394abdfe12 --- /dev/null +++ b/crates/vim/test_data/test_repeat_grouping_41735.json @@ -0,0 +1,10 @@ +{"Put":{"state":"ˇ"}} +{"Key":"i"} +{"Key":"a"} +{"Key":"escape"} +{"Key":"."} +{"Key":"."} +{"Key":"."} +{"Get":{"state":"ˇaaaa","mode":"Normal"}} +{"Key":"u"} +{"Get":{"state":"ˇaaa","mode":"Normal"}}