mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-03 17:44:30 +00:00
Preserve serach index for multicaret selection editor events
This commit is contained in:
parent
c130dd6b47
commit
ccc78000bd
6 changed files with 130 additions and 16 deletions
|
@ -887,10 +887,20 @@ pub(crate) enum BufferSearchHighlights {}
|
||||||
impl SearchableItem for Editor {
|
impl SearchableItem for Editor {
|
||||||
type Match = Range<Anchor>;
|
type Match = Range<Anchor>;
|
||||||
|
|
||||||
fn to_search_event(event: &Self::Event) -> Option<SearchEvent> {
|
fn to_search_event(
|
||||||
|
&mut self,
|
||||||
|
event: &Self::Event,
|
||||||
|
_: &mut ViewContext<Self>,
|
||||||
|
) -> Option<SearchEvent> {
|
||||||
match event {
|
match event {
|
||||||
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
|
Event::BufferEdited => Some(SearchEvent::MatchesInvalidated),
|
||||||
Event::SelectionsChanged { .. } => Some(SearchEvent::ActiveMatchChanged),
|
Event::SelectionsChanged { .. } => {
|
||||||
|
if self.selections.disjoint_anchors().len() == 1 {
|
||||||
|
Some(SearchEvent::ActiveMatchChanged)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -954,8 +964,16 @@ impl SearchableItem for Editor {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let buffer = self.buffer().read(cx).snapshot(cx);
|
let buffer = self.buffer().read(cx).snapshot(cx);
|
||||||
let cursor = self.selections.newest_anchor().head();
|
let current_index_position = if self.selections.disjoint_anchors().len() == 1 {
|
||||||
if matches[current_index].start.cmp(&cursor, &buffer).is_gt() {
|
self.selections.newest_anchor().head()
|
||||||
|
} else {
|
||||||
|
matches[current_index].start
|
||||||
|
};
|
||||||
|
if matches[current_index]
|
||||||
|
.start
|
||||||
|
.cmp(¤t_index_position, &buffer)
|
||||||
|
.is_gt()
|
||||||
|
{
|
||||||
if direction == Direction::Prev {
|
if direction == Direction::Prev {
|
||||||
if current_index == 0 {
|
if current_index == 0 {
|
||||||
current_index = matches.len() - 1;
|
current_index = matches.len() - 1;
|
||||||
|
@ -963,7 +981,11 @@ impl SearchableItem for Editor {
|
||||||
current_index -= 1;
|
current_index -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if matches[current_index].end.cmp(&cursor, &buffer).is_lt() {
|
} else if matches[current_index]
|
||||||
|
.end
|
||||||
|
.cmp(¤t_index_position, &buffer)
|
||||||
|
.is_lt()
|
||||||
|
{
|
||||||
if direction == Direction::Next {
|
if direction == Direction::Next {
|
||||||
current_index = 0;
|
current_index = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -362,8 +362,13 @@ impl Item for FeedbackEditor {
|
||||||
impl SearchableItem for FeedbackEditor {
|
impl SearchableItem for FeedbackEditor {
|
||||||
type Match = Range<Anchor>;
|
type Match = Range<Anchor>;
|
||||||
|
|
||||||
fn to_search_event(event: &Self::Event) -> Option<workspace::searchable::SearchEvent> {
|
fn to_search_event(
|
||||||
Editor::to_search_event(event)
|
&mut self,
|
||||||
|
event: &Self::Event,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Option<workspace::searchable::SearchEvent> {
|
||||||
|
self.editor
|
||||||
|
.update(cx, |editor, cx| editor.to_search_event(event, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
|
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
|
|
@ -467,8 +467,13 @@ impl Item for LspLogView {
|
||||||
impl SearchableItem for LspLogView {
|
impl SearchableItem for LspLogView {
|
||||||
type Match = <Editor as SearchableItem>::Match;
|
type Match = <Editor as SearchableItem>::Match;
|
||||||
|
|
||||||
fn to_search_event(event: &Self::Event) -> Option<workspace::searchable::SearchEvent> {
|
fn to_search_event(
|
||||||
Editor::to_search_event(event)
|
&mut self,
|
||||||
|
event: &Self::Event,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Option<workspace::searchable::SearchEvent> {
|
||||||
|
self.editor
|
||||||
|
.update(cx, |editor, cx| editor.to_search_event(event, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
|
fn clear_matches(&mut self, cx: &mut ViewContext<Self>) {
|
||||||
|
|
|
@ -1029,12 +1029,16 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.next_notification(cx).await;
|
editor.next_notification(cx).await;
|
||||||
editor.update(cx, |editor, cx| {
|
let initial_selections = editor.update(cx, |editor, cx| {
|
||||||
let initial_selections = editor.selections.display_ranges(cx);
|
let initial_selections = editor.selections.display_ranges(cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
initial_selections.len(), 1,
|
initial_selections.len(), 1,
|
||||||
"Expected to have only one selection before adding carets to all matches, but got: {initial_selections:?}",
|
"Expected to have only one selection before adding carets to all matches, but got: {initial_selections:?}",
|
||||||
)
|
);
|
||||||
|
initial_selections
|
||||||
|
});
|
||||||
|
search_bar.update(cx, |search_bar, _| {
|
||||||
|
assert_eq!(search_bar.active_match_index, Some(0));
|
||||||
});
|
});
|
||||||
|
|
||||||
search_bar.update(cx, |search_bar, cx| {
|
search_bar.update(cx, |search_bar, cx| {
|
||||||
|
@ -1047,5 +1051,74 @@ mod tests {
|
||||||
"Should select all `a` characters in the buffer, but got: {all_selections:?}"
|
"Should select all `a` characters in the buffer, but got: {all_selections:?}"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
search_bar.update(cx, |search_bar, _| {
|
||||||
|
assert_eq!(
|
||||||
|
search_bar.active_match_index,
|
||||||
|
Some(0),
|
||||||
|
"Match index should not change after selecting all matches"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
search_bar.update(cx, |search_bar, cx| {
|
||||||
|
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||||
|
let all_selections =
|
||||||
|
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
|
||||||
|
assert_eq!(
|
||||||
|
all_selections.len(),
|
||||||
|
1,
|
||||||
|
"On next match, should deselect items and select the next match"
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
all_selections, initial_selections,
|
||||||
|
"Next match should be different from the first selection"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
search_bar.update(cx, |search_bar, _| {
|
||||||
|
assert_eq!(
|
||||||
|
search_bar.active_match_index,
|
||||||
|
Some(1),
|
||||||
|
"Match index should be updated to the next one"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
search_bar.update(cx, |search_bar, cx| {
|
||||||
|
search_bar.select_all_matches(&SelectAllMatches, cx);
|
||||||
|
let all_selections =
|
||||||
|
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
|
||||||
|
assert_eq!(
|
||||||
|
all_selections.len(),
|
||||||
|
expected_query_matches_count,
|
||||||
|
"Should select all `a` characters in the buffer, but got: {all_selections:?}"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
search_bar.update(cx, |search_bar, _| {
|
||||||
|
assert_eq!(
|
||||||
|
search_bar.active_match_index,
|
||||||
|
Some(1),
|
||||||
|
"Match index should not change after selecting all matches"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
search_bar.update(cx, |search_bar, cx| {
|
||||||
|
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||||
|
let all_selections =
|
||||||
|
editor.update(cx, |editor, cx| editor.selections.display_ranges(cx));
|
||||||
|
assert_eq!(
|
||||||
|
all_selections.len(),
|
||||||
|
1,
|
||||||
|
"On previous match, should deselect items and select the previous item"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
all_selections, initial_selections,
|
||||||
|
"Previous match should be the same as the first selection"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
search_bar.update(cx, |search_bar, _| {
|
||||||
|
assert_eq!(
|
||||||
|
search_bar.active_match_index,
|
||||||
|
Some(0),
|
||||||
|
"Match index should be updated to the previous one"
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -647,7 +647,11 @@ impl SearchableItem for TerminalView {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert events raised by this item into search-relevant events (if applicable)
|
/// Convert events raised by this item into search-relevant events (if applicable)
|
||||||
fn to_search_event(event: &Self::Event) -> Option<SearchEvent> {
|
fn to_search_event(
|
||||||
|
&mut self,
|
||||||
|
event: &Self::Event,
|
||||||
|
_: &mut ViewContext<Self>,
|
||||||
|
) -> Option<SearchEvent> {
|
||||||
match event {
|
match event {
|
||||||
Event::Wakeup => Some(SearchEvent::MatchesInvalidated),
|
Event::Wakeup => Some(SearchEvent::MatchesInvalidated),
|
||||||
Event::SelectionsChanged => Some(SearchEvent::ActiveMatchChanged),
|
Event::SelectionsChanged => Some(SearchEvent::ActiveMatchChanged),
|
||||||
|
|
|
@ -37,7 +37,11 @@ pub trait SearchableItem: Item {
|
||||||
regex: true,
|
regex: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn to_search_event(event: &Self::Event) -> Option<SearchEvent>;
|
fn to_search_event(
|
||||||
|
&mut self,
|
||||||
|
event: &Self::Event,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> Option<SearchEvent>;
|
||||||
fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
|
fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
|
||||||
fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
|
fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
|
||||||
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
|
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
|
||||||
|
@ -141,8 +145,9 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
handler: Box<dyn Fn(SearchEvent, &mut WindowContext)>,
|
handler: Box<dyn Fn(SearchEvent, &mut WindowContext)>,
|
||||||
) -> Subscription {
|
) -> Subscription {
|
||||||
cx.subscribe(self, move |_, event, cx| {
|
cx.subscribe(self, move |handle, event, cx| {
|
||||||
if let Some(search_event) = T::to_search_event(event) {
|
let search_event = handle.update(cx, |handle, cx| handle.to_search_event(event, cx));
|
||||||
|
if let Some(search_event) = search_event {
|
||||||
handler(search_event, cx)
|
handler(search_event, cx)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue