Make buffer search aware of search direction (#24974)

This solves a couple of issues with Vim search by making the search
buffer and `SearchableItem` aware of the direction of the search. If
`SearchOptions::BACKWARDS` is set, all operations will be reversed. By
making `SearchableItem` aware of the direction, the correct active match
can be selected when searching backward.

Fixes #22506. This PR does not fix the last problem in that issue, but
that one is also tracked in #8049.

Release Notes:

- Fixes incorrect behavior of backward search in Vim mode
This commit is contained in:
Nico Lehmann 2025-03-04 18:27:37 -08:00 committed by GitHub
parent ed13e05855
commit 229e853874
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 80 additions and 25 deletions

View file

@ -41,7 +41,7 @@ use workspace::{
BreadcrumbText, Item, ItemEvent, SerializableItem, TabContentParams, TabTooltipContent,
},
register_serializable_item,
searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle},
searchable::{Direction, SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle},
CloseActiveItem, NewCenterTerminal, NewTerminal, OpenVisible, ToolbarItemLocation, Workspace,
WorkspaceId,
};
@ -1583,6 +1583,7 @@ impl SearchableItem for TerminalView {
/// Reports back to the search toolbar what the active match should be (the selection)
fn active_match_index(
&mut self,
direction: Direction,
matches: &[Self::Match],
_: &mut Window,
cx: &mut Context<Self>,
@ -1593,19 +1594,36 @@ impl SearchableItem for TerminalView {
let res = if !matches.is_empty() {
if let Some(selection_head) = self.terminal().read(cx).selection_head {
// If selection head is contained in a match. Return that match
if let Some(ix) = matches
.iter()
.enumerate()
.find(|(_, search_match)| {
search_match.contains(&selection_head)
|| search_match.start() > &selection_head
})
.map(|(ix, _)| ix)
{
Some(ix)
} else {
// If no selection after selection head, return the last match
Some(matches.len().saturating_sub(1))
match direction {
Direction::Prev => {
// If no selection before selection head, return the first match
Some(
matches
.iter()
.enumerate()
.rev()
.find(|(_, search_match)| {
search_match.contains(&selection_head)
|| search_match.start() < &selection_head
})
.map(|(ix, _)| ix)
.unwrap_or(0),
)
}
Direction::Next => {
// If no selection after selection head, return the last match
Some(
matches
.iter()
.enumerate()
.find(|(_, search_match)| {
search_match.contains(&selection_head)
|| search_match.start() > &selection_head
})
.map(|(ix, _)| ix)
.unwrap_or(matches.len().saturating_sub(1)),
)
}
}
} else {
// Matches found but no active selection, return the first last one (closest to cursor)