vim: Show 'j' from jk pre-emptively (#32007)

Fixes: #29812
Fixes: #22538

Co-Authored-By: <corentinhenry@gmail.com>

Release Notes:

- vim: Multi-key bindings in insert mode will now show the pending
keystroke in the buffer. For example if you have `jk` mapped to escape,
pressing `j` will immediately show a `j`.
This commit is contained in:
Conrad Irwin 2025-06-06 14:11:51 -06:00 committed by GitHub
parent 35a119d573
commit 5ad51ca48e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 151 additions and 4 deletions

View file

@ -7,14 +7,15 @@ use std::time::Duration;
use collections::HashMap;
use command_palette::CommandPalette;
use editor::{
DisplayPoint, Editor, EditorMode, MultiBuffer, actions::DeleteLine, display_map::DisplayRow,
test::editor_test_context::EditorTestContext,
AnchorRangeExt, DisplayPoint, Editor, EditorMode, MultiBuffer, actions::DeleteLine,
display_map::DisplayRow, test::editor_test_context::EditorTestContext,
};
use futures::StreamExt;
use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext};
use language::Point;
pub use neovim_backed_test_context::*;
use settings::SettingsStore;
use util::test::marked_text_ranges;
pub use vim_test_context::*;
use indoc::indoc;
@ -860,6 +861,49 @@ async fn test_jk(cx: &mut gpui::TestAppContext) {
cx.shared_state().await.assert_eq("jˇohello");
}
fn assert_pending_input(cx: &mut VimTestContext, expected: &str) {
cx.update_editor(|editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let highlights = editor
.text_highlights::<editor::PendingInput>(cx)
.unwrap()
.1;
let (_, ranges) = marked_text_ranges(expected, false);
assert_eq!(
highlights
.iter()
.map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
.collect::<Vec<_>>(),
ranges
)
});
}
#[gpui::test]
async fn test_jk_multi(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.update(|_, cx| {
cx.bind_keys([KeyBinding::new(
"j k l",
NormalBefore,
Some("vim_mode == insert"),
)])
});
cx.set_state("ˇone ˇone ˇone", Mode::Normal);
cx.simulate_keystrokes("i j");
cx.simulate_keystrokes("k");
cx.assert_state("ˇjkone ˇjkone ˇjkone", Mode::Insert);
assert_pending_input(&mut cx, "«jk»one «jk»one «jk»one");
cx.simulate_keystrokes("o j k");
cx.assert_state("jkoˇjkone jkoˇjkone jkoˇjkone", Mode::Insert);
assert_pending_input(&mut cx, "jko«jk»one jko«jk»one jko«jk»one");
cx.simulate_keystrokes("l");
cx.assert_state("jkˇoone jkˇoone jkˇoone", Mode::Normal);
}
#[gpui::test]
async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
@ -876,7 +920,22 @@ async fn test_jk_delay(cx: &mut gpui::TestAppContext) {
cx.simulate_keystrokes("i j");
cx.executor().advance_clock(Duration::from_millis(500));
cx.run_until_parked();
cx.assert_state("ˇhello", Mode::Insert);
cx.assert_state("ˇjhello", Mode::Insert);
cx.update_editor(|editor, window, cx| {
let snapshot = editor.snapshot(window, cx);
let highlights = editor
.text_highlights::<editor::PendingInput>(cx)
.unwrap()
.1;
assert_eq!(
highlights
.iter()
.map(|highlight| highlight.to_offset(&snapshot.buffer_snapshot))
.collect::<Vec<_>>(),
vec![0..1]
)
});
cx.executor().advance_clock(Duration::from_millis(500));
cx.run_until_parked();
cx.assert_state("jˇhello", Mode::Insert);