From 665006c4144c35f8673bd342c56e8d39df8b9e17 Mon Sep 17 00:00:00 2001 From: Aleksei Gusev Date: Thu, 14 Aug 2025 00:45:50 +0300 Subject: [PATCH] Move the cursor on search in Terminal if ViMode is active (#33305) Currently, the terminal search function doesn't work well with ViMode. It matches the search terms, scrolls the active match in the view, but it doesn't move the cursor to the match, which makes it useless for navigating the scrollback in vimode. With this improvement, if a user activates ViMode before the search Zed moves the cursor to the active search terms. So, when the search dialog is dismissed the cursor is places on the latest active search term and it's possible to navigate the scrollback via ViMode using this place as the starting point. https://github.com/user-attachments/assets/63325405-ed93-4bf8-a00f-28ded5511f31 Release Notes: - Improved the search function in the terminal when ViMode is activated --- crates/terminal/src/terminal.rs | 20 +++++++++++++++++--- crates/terminal_view/src/terminal_view.rs | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index c3c6de9e53..86728cc11c 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -167,6 +167,7 @@ enum InternalEvent { // Vi mode events ToggleViMode, ViMotion(ViMotion), + MoveViCursorToAlacPoint(AlacPoint), } ///A translation struct for Alacritty to communicate with us from their event loop @@ -972,6 +973,10 @@ impl Terminal { term.scroll_to_point(*point); self.refresh_hovered_word(window); } + InternalEvent::MoveViCursorToAlacPoint(point) => { + term.vi_goto_point(*point); + self.refresh_hovered_word(window); + } InternalEvent::ToggleViMode => { self.vi_mode_enabled = !self.vi_mode_enabled; term.toggle_vi_mode(); @@ -1100,12 +1105,21 @@ impl Terminal { pub fn activate_match(&mut self, index: usize) { if let Some(search_match) = self.matches.get(index).cloned() { self.set_selection(Some((make_selection(&search_match), *search_match.end()))); - - self.events - .push_back(InternalEvent::ScrollToAlacPoint(*search_match.start())); + if self.vi_mode_enabled { + self.events + .push_back(InternalEvent::MoveViCursorToAlacPoint(*search_match.end())); + } else { + self.events + .push_back(InternalEvent::ScrollToAlacPoint(*search_match.start())); + } } } + pub fn clear_matches(&mut self) { + self.matches.clear(); + self.set_selection(None); + } + pub fn select_matches(&mut self, matches: &[RangeInclusive]) { let matches_to_select = self .matches diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 0ec5f816d5..219238496c 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -1869,7 +1869,7 @@ impl SearchableItem for TerminalView { /// Clear stored matches fn clear_matches(&mut self, _window: &mut Window, cx: &mut Context) { - self.terminal().update(cx, |term, _| term.matches.clear()) + self.terminal().update(cx, |term, _| term.clear_matches()) } /// Store matches returned from find_matches somewhere for rendering