From b4d1ba7a0d51557f8728c333dfac13447ab8bb00 Mon Sep 17 00:00:00 2001 From: xcb3d <122720156+xcb3d@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:56:49 +0700 Subject: [PATCH] terminal: Fix vi mode cursor not updating on k/j navigation (#46762) The cursor in terminal vi mode was not visually updating when using vi motion navigation keys, despite internal position tracking working correctly. This was caused by missing `cx.notify()` calls to trigger re-rendering. Refactored keystroke handling by extracting common logic from `key_down` and `send_keystroke` into a shared `process_keystroke` helper method. Closes #46736 Release Notes: - terminal: Fixed vi mode cursor not visually updating when navigating with vi motion keys --------- Co-authored-by: dino --- crates/terminal_view/src/terminal_view.rs | 41 +++++++++++++---------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 21c5a23c7fec324ed273611a24df7a901d85a9f4..3c482286f89fbc281b6f5d37be67e601d0df465f 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -722,14 +722,7 @@ impl TerminalView { fn send_keystroke(&mut self, text: &SendKeystroke, _: &mut Window, cx: &mut Context) { if let Some(keystroke) = Keystroke::parse(&text.0).log_err() { self.clear_bell(cx); - self.terminal.update(cx, |term, cx| { - let processed = - term.try_keystroke(&keystroke, TerminalSettings::get_global(cx).option_as_meta); - if processed && term.vi_mode_enabled() { - cx.notify(); - } - processed - }); + self.process_keystroke(&keystroke, cx); } } @@ -1009,19 +1002,33 @@ impl ScrollbarVisibility for TerminalScrollbarSettingsWrapper { } impl TerminalView { + /// Attempts to process a keystroke in the terminal. Returns true if handled. + /// + /// In vi mode, explicitly triggers a re-render because vi navigation (like j/k) + /// updates the cursor locally without sending data to the shell, so there's no + /// shell output to automatically trigger a re-render. + fn process_keystroke(&mut self, keystroke: &Keystroke, cx: &mut Context) -> bool { + let (handled, vi_mode_enabled) = self.terminal.update(cx, |term, cx| { + ( + term.try_keystroke(keystroke, TerminalSettings::get_global(cx).option_as_meta), + term.vi_mode_enabled(), + ) + }); + + if handled && vi_mode_enabled { + cx.notify(); + } + + handled + } + fn key_down(&mut self, event: &KeyDownEvent, window: &mut Window, cx: &mut Context) { self.clear_bell(cx); self.pause_cursor_blinking(window, cx); - self.terminal.update(cx, |term, cx| { - let handled = term.try_keystroke( - &event.keystroke, - TerminalSettings::get_global(cx).option_as_meta, - ); - if handled { - cx.stop_propagation(); - } - }); + if self.process_keystroke(&event.keystroke, cx) { + cx.stop_propagation(); + } } fn focus_in(&mut self, window: &mut Window, cx: &mut Context) {