mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
git: Fix searching in the split diff (#48894)
- Fix panics caused by reusing cached matches for the wrong side - Highlight matches on the side that was searched only - Clear matches in non-searched editor when initiating a new search Release Notes: - N/A --------- Co-authored-by: Eric <eric@zed.dev> Co-authored-by: Jakub <jakub@zed.dev>
This commit is contained in:
parent
d2c922b815
commit
c8054cacbd
11 changed files with 336 additions and 100 deletions
|
|
@ -61,7 +61,7 @@ use ui::{
|
|||
use util::{ResultExt, maybe};
|
||||
use workspace::{
|
||||
CollaboratorId,
|
||||
searchable::{Direction, SearchableItemHandle},
|
||||
searchable::{Direction, SearchToken, SearchableItemHandle},
|
||||
};
|
||||
|
||||
use workspace::{
|
||||
|
|
@ -2799,11 +2799,12 @@ impl SearchableItem for TextThreadEditor {
|
|||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
active_match_index: Option<usize>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.update_matches(matches, active_match_index, window, cx)
|
||||
editor.update_matches(matches, active_match_index, token, window, cx)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2816,33 +2817,37 @@ impl SearchableItem for TextThreadEditor {
|
|||
&mut self,
|
||||
index: usize,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.activate_match(index, matches, window, cx);
|
||||
editor.activate_match(index, matches, token, window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn select_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.select_matches(matches, window, cx));
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.select_matches(matches, token, window, cx)
|
||||
});
|
||||
}
|
||||
|
||||
fn replace(
|
||||
&mut self,
|
||||
identifier: &Self::Match,
|
||||
query: &project::search::SearchQuery,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.replace(identifier, query, window, cx)
|
||||
editor.replace(identifier, query, token, window, cx)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2860,11 +2865,12 @@ impl SearchableItem for TextThreadEditor {
|
|||
&mut self,
|
||||
direction: Direction,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize> {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
editor.active_match_index(direction, matches, window, cx)
|
||||
editor.active_match_index(direction, matches, token, window, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use util::maybe;
|
|||
use workspace::{
|
||||
ToolbarItemEvent, ToolbarItemView, Workspace,
|
||||
item::Item,
|
||||
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
||||
searchable::{Direction, SearchEvent, SearchToken, SearchableItem, SearchableItemHandle},
|
||||
ui::{Button, Clickable, ContextMenu, Label, LabelCommon, PopoverMenu, h_flex},
|
||||
};
|
||||
|
||||
|
|
@ -1018,11 +1018,12 @@ impl SearchableItem for DapLogView {
|
|||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
active_match_index: Option<usize>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor.update(cx, |e, cx| {
|
||||
e.update_matches(matches, active_match_index, window, cx)
|
||||
e.update_matches(matches, active_match_index, token, window, cx)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1035,21 +1036,24 @@ impl SearchableItem for DapLogView {
|
|||
&mut self,
|
||||
index: usize,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor
|
||||
.update(cx, |e, cx| e.activate_match(index, matches, window, cx))
|
||||
self.editor.update(cx, |e, cx| {
|
||||
e.activate_match(index, matches, token, window, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn select_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor
|
||||
.update(cx, |e, cx| e.select_matches(matches, window, cx))
|
||||
.update(cx, |e, cx| e.select_matches(matches, token, window, cx))
|
||||
}
|
||||
|
||||
fn find_matches(
|
||||
|
|
@ -1066,6 +1070,7 @@ impl SearchableItem for DapLogView {
|
|||
&mut self,
|
||||
_: &Self::Match,
|
||||
_: &SearchQuery,
|
||||
_token: SearchToken,
|
||||
_window: &mut Window,
|
||||
_: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -1087,11 +1092,12 @@ impl SearchableItem for DapLogView {
|
|||
&mut self,
|
||||
direction: Direction,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize> {
|
||||
self.editor.update(cx, |e, cx| {
|
||||
e.active_match_index(direction, matches, window, cx)
|
||||
e.active_match_index(direction, matches, token, window, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ use workspace::{
|
|||
invalid_item_view::InvalidItemView,
|
||||
item::{FollowableItem, Item, ItemBufferKind, ItemEvent, ProjectItem, SaveOptions},
|
||||
searchable::{
|
||||
Direction, FilteredSearchRange, SearchEvent, SearchableItem, SearchableItemHandle,
|
||||
Direction, FilteredSearchRange, SearchEvent, SearchToken, SearchableItem,
|
||||
SearchableItemHandle,
|
||||
},
|
||||
};
|
||||
use workspace::{
|
||||
|
|
@ -1496,12 +1497,15 @@ impl Editor {
|
|||
impl SearchableItem for Editor {
|
||||
type Match = Range<Anchor>;
|
||||
|
||||
fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec<Range<Anchor>> {
|
||||
self.background_highlights
|
||||
.get(&HighlightKey::BufferSearchHighlights)
|
||||
.map_or(Vec::new(), |(_color, ranges)| {
|
||||
ranges.iter().cloned().collect()
|
||||
})
|
||||
fn get_matches(&self, _window: &mut Window, _: &mut App) -> (Vec<Range<Anchor>>, SearchToken) {
|
||||
(
|
||||
self.background_highlights
|
||||
.get(&HighlightKey::BufferSearchHighlights)
|
||||
.map_or(Vec::new(), |(_color, ranges)| {
|
||||
ranges.iter().cloned().collect()
|
||||
}),
|
||||
SearchToken::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn clear_matches(&mut self, _: &mut Window, cx: &mut Context<Self>) {
|
||||
|
|
@ -1517,6 +1521,7 @@ impl SearchableItem for Editor {
|
|||
&mut self,
|
||||
matches: &[Range<Anchor>],
|
||||
active_match_index: Option<usize>,
|
||||
_token: SearchToken,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -1630,6 +1635,7 @@ impl SearchableItem for Editor {
|
|||
&mut self,
|
||||
index: usize,
|
||||
matches: &[Range<Anchor>],
|
||||
_token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -1648,6 +1654,7 @@ impl SearchableItem for Editor {
|
|||
fn select_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
_token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -1660,6 +1667,7 @@ impl SearchableItem for Editor {
|
|||
&mut self,
|
||||
identifier: &Self::Match,
|
||||
query: &SearchQuery,
|
||||
_token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -1683,6 +1691,7 @@ impl SearchableItem for Editor {
|
|||
&mut self,
|
||||
matches: &mut dyn Iterator<Item = &Self::Match>,
|
||||
query: &SearchQuery,
|
||||
_token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -1725,6 +1734,7 @@ impl SearchableItem for Editor {
|
|||
current_index: usize,
|
||||
direction: Direction,
|
||||
count: usize,
|
||||
_token: SearchToken,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> usize {
|
||||
|
|
@ -1832,6 +1842,7 @@ impl SearchableItem for Editor {
|
|||
&mut self,
|
||||
direction: Direction,
|
||||
matches: &[Range<Anchor>],
|
||||
_token: SearchToken,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize> {
|
||||
|
|
|
|||
|
|
@ -24,12 +24,13 @@ use ui::{
|
|||
|
||||
use crate::{
|
||||
display_map::CompanionExcerptPatch,
|
||||
element::SplitSide,
|
||||
split_editor_view::{SplitEditorState, SplitEditorView},
|
||||
};
|
||||
use workspace::{
|
||||
ActivatePaneLeft, ActivatePaneRight, Item, ToolbarItemLocation, Workspace,
|
||||
item::{BreadcrumbText, ItemBufferKind, ItemEvent, SaveOptions, TabContentParams},
|
||||
searchable::{SearchEvent, SearchableItem, SearchableItemHandle},
|
||||
searchable::{SearchEvent, SearchToken, SearchableItem, SearchableItemHandle},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -381,6 +382,7 @@ pub struct SplittableEditor {
|
|||
lhs: Option<LhsEditor>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
split_state: Entity<SplitEditorState>,
|
||||
searched_side: Option<SplitSide>,
|
||||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
|
|
@ -420,7 +422,17 @@ impl SplittableEditor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn last_selected_editor(&self) -> &Entity<Editor> {
|
||||
fn focused_side(&self) -> SplitSide {
|
||||
if let Some(lhs) = &self.lhs
|
||||
&& lhs.was_last_focused
|
||||
{
|
||||
SplitSide::Left
|
||||
} else {
|
||||
SplitSide::Right
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focused_editor(&self) -> &Entity<Editor> {
|
||||
if let Some(lhs) = &self.lhs
|
||||
&& lhs.was_last_focused
|
||||
{
|
||||
|
|
@ -459,8 +471,10 @@ impl SplittableEditor {
|
|||
_ => cx.emit(event.clone()),
|
||||
},
|
||||
),
|
||||
cx.subscribe(&rhs_editor, |_, _, event: &SearchEvent, cx| {
|
||||
cx.emit(event.clone());
|
||||
cx.subscribe(&rhs_editor, |this, _, event: &SearchEvent, cx| {
|
||||
if this.searched_side.is_none() || this.searched_side == Some(SplitSide::Right) {
|
||||
cx.emit(event.clone());
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
|
|
@ -493,6 +507,7 @@ impl SplittableEditor {
|
|||
lhs: None,
|
||||
workspace: workspace.downgrade(),
|
||||
split_state,
|
||||
searched_side: None,
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
}
|
||||
|
|
@ -596,13 +611,20 @@ impl SplittableEditor {
|
|||
},
|
||||
)];
|
||||
|
||||
subscriptions.push(
|
||||
cx.subscribe(&lhs_editor, |this, _, event: &SearchEvent, cx| {
|
||||
if this.searched_side == Some(SplitSide::Left) {
|
||||
cx.emit(event.clone());
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let lhs_focus_handle = lhs_editor.read(cx).focus_handle(cx);
|
||||
subscriptions.push(
|
||||
cx.on_focus_in(&lhs_focus_handle, window, |this, _window, cx| {
|
||||
if let Some(lhs) = &mut this.lhs {
|
||||
if !lhs.was_last_focused {
|
||||
lhs.was_last_focused = true;
|
||||
cx.emit(SearchEvent::MatchesInvalidated);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
|
@ -615,7 +637,6 @@ impl SplittableEditor {
|
|||
if let Some(lhs) = &mut this.lhs {
|
||||
if lhs.was_last_focused {
|
||||
lhs.was_last_focused = false;
|
||||
cx.emit(SearchEvent::MatchesInvalidated);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
|
@ -1085,6 +1106,19 @@ impl SplittableEditor {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn search_token(&self) -> SearchToken {
|
||||
SearchToken::new(self.focused_side() as u64)
|
||||
}
|
||||
|
||||
fn editor_for_token(&self, token: SearchToken) -> &Entity<Editor> {
|
||||
if token.value() == SplitSide::Left as u64 {
|
||||
if let Some(lhs) = &self.lhs {
|
||||
return &lhs.editor;
|
||||
}
|
||||
}
|
||||
&self.rhs_editor
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -1665,12 +1699,12 @@ impl Item for SplittableEditor {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
self.last_selected_editor()
|
||||
self.focused_editor()
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.last_selected_editor().update(cx, |editor, cx| {
|
||||
self.focused_editor().update(cx, |editor, cx| {
|
||||
editor.deactivated(window, cx);
|
||||
});
|
||||
}
|
||||
|
|
@ -1709,9 +1743,7 @@ impl Item for SplittableEditor {
|
|||
}
|
||||
|
||||
fn pixel_position_of_cursor(&self, cx: &App) -> Option<gpui::Point<gpui::Pixels>> {
|
||||
self.last_selected_editor()
|
||||
.read(cx)
|
||||
.pixel_position_of_cursor(cx)
|
||||
self.focused_editor().read(cx).pixel_position_of_cursor(cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1719,25 +1751,59 @@ impl SearchableItem for SplittableEditor {
|
|||
type Match = Range<Anchor>;
|
||||
|
||||
fn clear_matches(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.last_selected_editor().update(cx, |editor, cx| {
|
||||
self.rhs_editor.update(cx, |editor, cx| {
|
||||
editor.clear_matches(window, cx);
|
||||
});
|
||||
if let Some(lhs_editor) = self.lhs_editor() {
|
||||
lhs_editor.update(cx, |editor, cx| {
|
||||
editor.clear_matches(window, cx);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn update_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
active_match_index: Option<usize>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.last_selected_editor().update(cx, |editor, cx| {
|
||||
editor.update_matches(matches, active_match_index, window, cx);
|
||||
self.editor_for_token(token).update(cx, |editor, cx| {
|
||||
editor.update_matches(matches, active_match_index, token, window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn search_bar_visibility_changed(
|
||||
&mut self,
|
||||
visible: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if visible {
|
||||
let side = self.focused_side();
|
||||
self.searched_side = Some(side);
|
||||
match side {
|
||||
SplitSide::Left => {
|
||||
self.rhs_editor.update(cx, |editor, cx| {
|
||||
editor.clear_matches(window, cx);
|
||||
});
|
||||
}
|
||||
SplitSide::Right => {
|
||||
if let Some(lhs) = &self.lhs {
|
||||
lhs.editor.update(cx, |editor, cx| {
|
||||
editor.clear_matches(window, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.searched_side = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn query_suggestion(&mut self, window: &mut Window, cx: &mut Context<Self>) -> String {
|
||||
self.last_selected_editor()
|
||||
self.focused_editor()
|
||||
.update(cx, |editor, cx| editor.query_suggestion(window, cx))
|
||||
}
|
||||
|
||||
|
|
@ -1745,22 +1811,24 @@ impl SearchableItem for SplittableEditor {
|
|||
&mut self,
|
||||
index: usize,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.last_selected_editor().update(cx, |editor, cx| {
|
||||
editor.activate_match(index, matches, window, cx);
|
||||
self.editor_for_token(token).update(cx, |editor, cx| {
|
||||
editor.activate_match(index, matches, token, window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn select_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.last_selected_editor().update(cx, |editor, cx| {
|
||||
editor.select_matches(matches, window, cx);
|
||||
self.editor_for_token(token).update(cx, |editor, cx| {
|
||||
editor.select_matches(matches, token, window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1768,11 +1836,12 @@ impl SearchableItem for SplittableEditor {
|
|||
&mut self,
|
||||
identifier: &Self::Match,
|
||||
query: &project::search::SearchQuery,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.last_selected_editor().update(cx, |editor, cx| {
|
||||
editor.replace(identifier, query, window, cx);
|
||||
self.editor_for_token(token).update(cx, |editor, cx| {
|
||||
editor.replace(identifier, query, token, window, cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1782,19 +1851,41 @@ impl SearchableItem for SplittableEditor {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> gpui::Task<Vec<Self::Match>> {
|
||||
self.last_selected_editor()
|
||||
self.focused_editor()
|
||||
.update(cx, |editor, cx| editor.find_matches(query, window, cx))
|
||||
}
|
||||
|
||||
fn find_matches_with_token(
|
||||
&mut self,
|
||||
query: Arc<project::search::SearchQuery>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> gpui::Task<(Vec<Self::Match>, SearchToken)> {
|
||||
let token = self.search_token();
|
||||
let editor = self.focused_editor().downgrade();
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
let Some(matches) = editor
|
||||
.update_in(cx, |editor, window, cx| {
|
||||
editor.find_matches(query, window, cx)
|
||||
})
|
||||
.ok()
|
||||
else {
|
||||
return (Vec::new(), token);
|
||||
};
|
||||
(matches.await, token)
|
||||
})
|
||||
}
|
||||
|
||||
fn active_match_index(
|
||||
&mut self,
|
||||
direction: workspace::searchable::Direction,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize> {
|
||||
self.last_selected_editor().update(cx, |editor, cx| {
|
||||
editor.active_match_index(direction, matches, window, cx)
|
||||
self.editor_for_token(token).update(cx, |editor, cx| {
|
||||
editor.active_match_index(direction, matches, token, window, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -1803,7 +1894,7 @@ impl EventEmitter<EditorEvent> for SplittableEditor {}
|
|||
impl EventEmitter<SearchEvent> for SplittableEditor {}
|
||||
impl Focusable for SplittableEditor {
|
||||
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
|
||||
self.last_selected_editor().read(cx).focus_handle(cx)
|
||||
self.focused_editor().read(cx).focus_handle(cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ impl ProjectDiff {
|
|||
}
|
||||
|
||||
pub fn active_path(&self, cx: &App) -> Option<ProjectPath> {
|
||||
let editor = self.editor.read(cx).last_selected_editor().read(cx);
|
||||
let editor = self.editor.read(cx).focused_editor().read(cx);
|
||||
let position = editor.selections.newest_anchor().head();
|
||||
let multi_buffer = editor.buffer().read(cx);
|
||||
let (_, buffer, _) = multi_buffer.excerpt_containing(position, cx)?;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use util::ResultExt as _;
|
|||
use workspace::{
|
||||
SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, WorkspaceId,
|
||||
item::{Item, ItemHandle},
|
||||
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
||||
searchable::{Direction, SearchEvent, SearchToken, SearchableItem, SearchableItemHandle},
|
||||
};
|
||||
|
||||
use crate::get_or_create_tool;
|
||||
|
|
@ -813,11 +813,12 @@ impl SearchableItem for LspLogView {
|
|||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
active_match_index: Option<usize>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor.update(cx, |e, cx| {
|
||||
e.update_matches(matches, active_match_index, window, cx)
|
||||
e.update_matches(matches, active_match_index, token, window, cx)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -830,21 +831,24 @@ impl SearchableItem for LspLogView {
|
|||
&mut self,
|
||||
index: usize,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor
|
||||
.update(cx, |e, cx| e.activate_match(index, matches, window, cx))
|
||||
self.editor.update(cx, |e, cx| {
|
||||
e.activate_match(index, matches, token, window, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn select_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.editor
|
||||
.update(cx, |e, cx| e.select_matches(matches, window, cx))
|
||||
.update(cx, |e, cx| e.select_matches(matches, token, window, cx))
|
||||
}
|
||||
|
||||
fn find_matches(
|
||||
|
|
@ -861,6 +865,7 @@ impl SearchableItem for LspLogView {
|
|||
&mut self,
|
||||
_: &Self::Match,
|
||||
_: &SearchQuery,
|
||||
_token: SearchToken,
|
||||
_window: &mut Window,
|
||||
_: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -881,11 +886,12 @@ impl SearchableItem for LspLogView {
|
|||
&mut self,
|
||||
direction: Direction,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize> {
|
||||
self.editor.update(cx, |e, cx| {
|
||||
e.active_match_index(direction, matches, window, cx)
|
||||
e.active_match_index(direction, matches, token, window, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4245,7 +4245,7 @@ impl OutlinePanel {
|
|||
let buffer_search_matches = self
|
||||
.active_editor()
|
||||
.map(|active_editor| {
|
||||
active_editor.update(cx, |editor, cx| editor.get_matches(window, cx))
|
||||
active_editor.update(cx, |editor, cx| editor.get_matches(window, cx).0)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ use workspace::{
|
|||
ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||
item::{ItemBufferKind, ItemHandle},
|
||||
searchable::{
|
||||
CollapseDirection, Direction, FilteredSearchRange, SearchEvent, SearchableItemHandle,
|
||||
WeakSearchableItemHandle,
|
||||
CollapseDirection, Direction, FilteredSearchRange, SearchEvent, SearchToken,
|
||||
SearchableItemHandle, WeakSearchableItemHandle,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -76,7 +76,8 @@ pub struct BufferSearchBar {
|
|||
#[cfg(target_os = "macos")]
|
||||
pending_external_query: Option<(String, SearchOptions)>,
|
||||
active_search: Option<Arc<SearchQuery>>,
|
||||
searchable_items_with_matches: HashMap<Box<dyn WeakSearchableItemHandle>, AnyVec<dyn Send>>,
|
||||
searchable_items_with_matches:
|
||||
HashMap<Box<dyn WeakSearchableItemHandle>, (AnyVec<dyn Send>, SearchToken)>,
|
||||
pending_search: Option<Task<()>>,
|
||||
search_options: SearchOptions,
|
||||
default_options: SearchOptions,
|
||||
|
|
@ -233,7 +234,7 @@ impl Render for BufferSearchBar {
|
|||
let matches_count = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())
|
||||
.map(AnyVec::len)
|
||||
.map(|(matches, _)| matches.len())
|
||||
.unwrap_or(0);
|
||||
if let Some(match_ix) = self.active_match_index {
|
||||
Some(format!("{}/{}", match_ix + 1, matches_count))
|
||||
|
|
@ -1041,11 +1042,11 @@ impl BufferSearchBar {
|
|||
pub fn activate_current_match(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(match_ix) = self.active_match_index
|
||||
&& let Some(active_searchable_item) = self.active_searchable_item.as_ref()
|
||||
&& let Some(matches) = self
|
||||
&& let Some((matches, token)) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&active_searchable_item.downgrade())
|
||||
{
|
||||
active_searchable_item.activate_match(match_ix, matches, window, cx)
|
||||
active_searchable_item.activate_match(match_ix, matches, *token, window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1227,11 +1228,11 @@ impl BufferSearchBar {
|
|||
if !self.dismissed
|
||||
&& self.active_match_index.is_some()
|
||||
&& let Some(searchable_item) = self.active_searchable_item.as_ref()
|
||||
&& let Some(matches) = self
|
||||
&& let Some((matches, token)) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())
|
||||
{
|
||||
searchable_item.select_matches(matches, window, cx);
|
||||
searchable_item.select_matches(matches, *token, window, cx);
|
||||
self.focus_editor(&FocusEditor, window, cx);
|
||||
}
|
||||
}
|
||||
|
|
@ -1261,10 +1262,10 @@ impl BufferSearchBar {
|
|||
|
||||
if let Some(index) = self.active_match_index
|
||||
&& let Some(searchable_item) = self.active_searchable_item.as_ref()
|
||||
&& let Some(matches) = self
|
||||
&& let Some((matches, token)) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())
|
||||
.filter(|matches| !matches.is_empty())
|
||||
.filter(|(matches, _)| !matches.is_empty())
|
||||
{
|
||||
// If 'wrapscan' is disabled, searches do not wrap around the end of the file.
|
||||
if !EditorSettings::get_global(cx).search_wrap
|
||||
|
|
@ -1275,30 +1276,30 @@ impl BufferSearchBar {
|
|||
return;
|
||||
}
|
||||
let new_match_index = searchable_item
|
||||
.match_index_for_direction(matches, index, direction, count, window, cx);
|
||||
.match_index_for_direction(matches, index, direction, count, *token, window, cx);
|
||||
|
||||
searchable_item.update_matches(matches, Some(new_match_index), window, cx);
|
||||
searchable_item.activate_match(new_match_index, matches, window, cx);
|
||||
searchable_item.update_matches(matches, Some(new_match_index), *token, window, cx);
|
||||
searchable_item.activate_match(new_match_index, matches, *token, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_first_match(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(searchable_item) = self.active_searchable_item.as_ref()
|
||||
&& let Some(matches) = self
|
||||
&& let Some((matches, token)) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())
|
||||
{
|
||||
if matches.is_empty() {
|
||||
return;
|
||||
}
|
||||
searchable_item.update_matches(matches, Some(0), window, cx);
|
||||
searchable_item.activate_match(0, matches, window, cx);
|
||||
searchable_item.update_matches(matches, Some(0), *token, window, cx);
|
||||
searchable_item.activate_match(0, matches, *token, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_last_match(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(searchable_item) = self.active_searchable_item.as_ref()
|
||||
&& let Some(matches) = self
|
||||
&& let Some((matches, token)) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())
|
||||
{
|
||||
|
|
@ -1306,8 +1307,8 @@ impl BufferSearchBar {
|
|||
return;
|
||||
}
|
||||
let new_match_index = matches.len() - 1;
|
||||
searchable_item.update_matches(matches, Some(new_match_index), window, cx);
|
||||
searchable_item.activate_match(new_match_index, matches, window, cx);
|
||||
searchable_item.update_matches(matches, Some(new_match_index), *token, window, cx);
|
||||
searchable_item.activate_match(new_match_index, matches, *token, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1532,18 +1533,19 @@ impl BufferSearchBar {
|
|||
self.active_search = Some(query.clone());
|
||||
let query_text = query.as_str().to_string();
|
||||
|
||||
let matches = active_searchable_item.find_matches(query, window, cx);
|
||||
let matches_with_token =
|
||||
active_searchable_item.find_matches_with_token(query, window, cx);
|
||||
|
||||
let active_searchable_item = active_searchable_item.downgrade();
|
||||
self.pending_search = Some(cx.spawn_in(window, async move |this, cx| {
|
||||
let matches = matches.await;
|
||||
let (matches, token) = matches_with_token.await;
|
||||
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
if let Some(active_searchable_item) =
|
||||
WeakSearchableItemHandle::upgrade(active_searchable_item.as_ref(), cx)
|
||||
{
|
||||
this.searchable_items_with_matches
|
||||
.insert(active_searchable_item.downgrade(), matches);
|
||||
.insert(active_searchable_item.downgrade(), (matches, token));
|
||||
|
||||
this.update_match_index(window, cx);
|
||||
|
||||
|
|
@ -1552,7 +1554,7 @@ impl BufferSearchBar {
|
|||
.add(&mut this.search_history_cursor, query_text);
|
||||
}
|
||||
if !this.dismissed {
|
||||
let matches = this
|
||||
let (matches, token) = this
|
||||
.searchable_items_with_matches
|
||||
.get(&active_searchable_item.downgrade())
|
||||
.unwrap();
|
||||
|
|
@ -1562,6 +1564,7 @@ impl BufferSearchBar {
|
|||
active_searchable_item.update_matches(
|
||||
matches,
|
||||
this.active_match_index,
|
||||
*token,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
|
@ -1592,21 +1595,21 @@ impl BufferSearchBar {
|
|||
.active_searchable_item
|
||||
.as_ref()
|
||||
.and_then(|searchable_item| {
|
||||
let matches = self
|
||||
let (matches, token) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())?;
|
||||
searchable_item.active_match_index(direction, matches, window, cx)
|
||||
searchable_item.active_match_index(direction, matches, *token, window, cx)
|
||||
});
|
||||
if new_index != self.active_match_index {
|
||||
self.active_match_index = new_index;
|
||||
if !self.dismissed {
|
||||
if let Some(searchable_item) = self.active_searchable_item.as_ref() {
|
||||
if let Some(matches) = self
|
||||
if let Some((matches, token)) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())
|
||||
{
|
||||
if !matches.is_empty() {
|
||||
searchable_item.update_matches(matches, new_index, window, cx);
|
||||
searchable_item.update_matches(matches, new_index, *token, window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1712,7 +1715,7 @@ impl BufferSearchBar {
|
|||
&& self.active_search.is_some()
|
||||
&& let Some(searchable_item) = self.active_searchable_item.as_ref()
|
||||
&& let Some(query) = self.active_search.as_ref()
|
||||
&& let Some(matches) = self
|
||||
&& let Some((matches, token)) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())
|
||||
{
|
||||
|
|
@ -1721,7 +1724,7 @@ impl BufferSearchBar {
|
|||
.as_ref()
|
||||
.clone()
|
||||
.with_replacement(self.replacement(cx));
|
||||
searchable_item.replace(matches.at(active_index), &query, window, cx);
|
||||
searchable_item.replace(matches.at(active_index), &query, *token, window, cx);
|
||||
self.select_next_match(&SelectNextMatch, window, cx);
|
||||
}
|
||||
should_propagate = false;
|
||||
|
|
@ -1736,7 +1739,7 @@ impl BufferSearchBar {
|
|||
&& self.active_search.is_some()
|
||||
&& let Some(searchable_item) = self.active_searchable_item.as_ref()
|
||||
&& let Some(query) = self.active_search.as_ref()
|
||||
&& let Some(matches) = self
|
||||
&& let Some((matches, token)) = self
|
||||
.searchable_items_with_matches
|
||||
.get(&searchable_item.downgrade())
|
||||
{
|
||||
|
|
@ -1744,7 +1747,7 @@ impl BufferSearchBar {
|
|||
.as_ref()
|
||||
.clone()
|
||||
.with_replacement(self.replacement(cx));
|
||||
searchable_item.replace_all(&mut matches.iter(), &query, window, cx);
|
||||
searchable_item.replace_all(&mut matches.iter(), &query, *token, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,10 @@ use workspace::{
|
|||
DeploySearch, ItemNavHistory, NewSearch, ToolbarItemEvent, ToolbarItemLocation,
|
||||
ToolbarItemView, Workspace, WorkspaceId,
|
||||
item::{Item, ItemEvent, ItemHandle, SaveOptions},
|
||||
searchable::{CollapseDirection, Direction, SearchEvent, SearchableItem, SearchableItemHandle},
|
||||
searchable::{
|
||||
CollapseDirection, Direction, SearchEvent, SearchToken, SearchableItem,
|
||||
SearchableItemHandle,
|
||||
},
|
||||
};
|
||||
|
||||
actions!(
|
||||
|
|
@ -731,7 +734,7 @@ impl ProjectSearchView {
|
|||
let mat = self.entity.read(cx).match_ranges.get(active_index).cloned();
|
||||
self.results_editor.update(cx, |editor, cx| {
|
||||
if let Some(mat) = mat.as_ref() {
|
||||
editor.replace(mat, &query, window, cx);
|
||||
editor.replace(mat, &query, SearchToken::default(), window, cx);
|
||||
}
|
||||
});
|
||||
self.select_match(Direction::Next, window, cx)
|
||||
|
|
@ -761,7 +764,13 @@ impl ProjectSearchView {
|
|||
}
|
||||
|
||||
self.results_editor.update(cx, |editor, cx| {
|
||||
editor.replace_all(&mut match_ranges.iter(), &query, window, cx);
|
||||
editor.replace_all(
|
||||
&mut match_ranges.iter(),
|
||||
&query,
|
||||
SearchToken::default(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
self.entity.update(cx, |model, _cx| {
|
||||
|
|
@ -1394,7 +1403,15 @@ impl ProjectSearchView {
|
|||
}
|
||||
|
||||
let new_index = self.results_editor.update(cx, |editor, cx| {
|
||||
editor.match_index_for_direction(&match_ranges, index, direction, 1, window, cx)
|
||||
editor.match_index_for_direction(
|
||||
&match_ranges,
|
||||
index,
|
||||
direction,
|
||||
1,
|
||||
SearchToken::default(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let range_to_select = match_ranges[new_index].clone();
|
||||
|
|
|
|||
|
|
@ -56,7 +56,9 @@ use workspace::{
|
|||
BreadcrumbText, Item, ItemEvent, SerializableItem, TabContentParams, TabTooltipContent,
|
||||
},
|
||||
register_serializable_item,
|
||||
searchable::{Direction, SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle},
|
||||
searchable::{
|
||||
Direction, SearchEvent, SearchOptions, SearchToken, SearchableItem, SearchableItemHandle,
|
||||
},
|
||||
};
|
||||
use zed_actions::{agent::AddSelectionToThread, assistant::InlineAssist};
|
||||
|
||||
|
|
@ -1664,6 +1666,7 @@ impl SearchableItem for TerminalView {
|
|||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
_active_match_index: Option<usize>,
|
||||
_token: SearchToken,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -1686,6 +1689,7 @@ impl SearchableItem for TerminalView {
|
|||
&mut self,
|
||||
index: usize,
|
||||
_: &[Self::Match],
|
||||
_token: SearchToken,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
|
|
@ -1695,7 +1699,13 @@ impl SearchableItem for TerminalView {
|
|||
}
|
||||
|
||||
/// Add selections for all matches given.
|
||||
fn select_matches(&mut self, matches: &[Self::Match], _: &mut Window, cx: &mut Context<Self>) {
|
||||
fn select_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
_token: SearchToken,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.terminal()
|
||||
.update(cx, |term, _| term.select_matches(matches));
|
||||
cx.notify();
|
||||
|
|
@ -1721,6 +1731,7 @@ impl SearchableItem for TerminalView {
|
|||
&mut self,
|
||||
direction: Direction,
|
||||
matches: &[Self::Match],
|
||||
_token: SearchToken,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize> {
|
||||
|
|
@ -1774,6 +1785,7 @@ impl SearchableItem for TerminalView {
|
|||
&mut self,
|
||||
_: &Self::Match,
|
||||
_: &SearchQuery,
|
||||
_token: SearchToken,
|
||||
_window: &mut Window,
|
||||
_: &mut Context<Self>,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,19 @@ use crate::{
|
|||
item::{Item, WeakItemHandle},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct SearchToken(u64);
|
||||
|
||||
impl SearchToken {
|
||||
pub fn new(value: u64) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
pub fn value(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CollapseDirection {
|
||||
Collapsed,
|
||||
|
|
@ -96,14 +109,15 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
|||
) {
|
||||
}
|
||||
|
||||
fn get_matches(&self, _window: &mut Window, _: &mut App) -> Vec<Self::Match> {
|
||||
Vec::new()
|
||||
fn get_matches(&self, _window: &mut Window, _: &mut App) -> (Vec<Self::Match>, SearchToken) {
|
||||
(Vec::new(), SearchToken::default())
|
||||
}
|
||||
fn clear_matches(&mut self, window: &mut Window, cx: &mut Context<Self>);
|
||||
fn update_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
active_match_index: Option<usize>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
);
|
||||
|
|
@ -112,12 +126,14 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
|||
&mut self,
|
||||
index: usize,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
);
|
||||
fn select_matches(
|
||||
&mut self,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
);
|
||||
|
|
@ -125,6 +141,7 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
|||
&mut self,
|
||||
_: &Self::Match,
|
||||
_: &SearchQuery,
|
||||
_token: SearchToken,
|
||||
_window: &mut Window,
|
||||
_: &mut Context<Self>,
|
||||
);
|
||||
|
|
@ -132,11 +149,12 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
|||
&mut self,
|
||||
matches: &mut dyn Iterator<Item = &Self::Match>,
|
||||
query: &SearchQuery,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
for item in matches {
|
||||
self.replace(item, query, window, cx);
|
||||
self.replace(item, query, token, window, cx);
|
||||
}
|
||||
}
|
||||
fn match_index_for_direction(
|
||||
|
|
@ -145,6 +163,7 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
|||
current_index: usize,
|
||||
direction: Direction,
|
||||
count: usize,
|
||||
_token: SearchToken,
|
||||
_window: &mut Window,
|
||||
_: &mut Context<Self>,
|
||||
) -> usize {
|
||||
|
|
@ -166,10 +185,22 @@ pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Vec<Self::Match>>;
|
||||
|
||||
fn find_matches_with_token(
|
||||
&mut self,
|
||||
query: Arc<SearchQuery>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<(Vec<Self::Match>, SearchToken)> {
|
||||
let matches = self.find_matches(query, window, cx);
|
||||
cx.spawn(async move |_, _| (matches.await, SearchToken::default()))
|
||||
}
|
||||
|
||||
fn active_match_index(
|
||||
&mut self,
|
||||
direction: Direction,
|
||||
matches: &[Self::Match],
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<usize>;
|
||||
|
|
@ -191,6 +222,7 @@ pub trait SearchableItemHandle: ItemHandle {
|
|||
&self,
|
||||
matches: &AnyVec<dyn Send>,
|
||||
active_match_index: Option<usize>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
);
|
||||
|
|
@ -199,14 +231,22 @@ pub trait SearchableItemHandle: ItemHandle {
|
|||
&self,
|
||||
index: usize,
|
||||
matches: &AnyVec<dyn Send>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
);
|
||||
fn select_matches(
|
||||
&self,
|
||||
matches: &AnyVec<dyn Send>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
);
|
||||
fn select_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App);
|
||||
fn replace(
|
||||
&self,
|
||||
_: any_vec::element::ElementRef<'_, dyn Send>,
|
||||
_: &SearchQuery,
|
||||
token: SearchToken,
|
||||
_window: &mut Window,
|
||||
_: &mut App,
|
||||
);
|
||||
|
|
@ -214,6 +254,7 @@ pub trait SearchableItemHandle: ItemHandle {
|
|||
&self,
|
||||
matches: &mut dyn Iterator<Item = any_vec::element::ElementRef<'_, dyn Send>>,
|
||||
query: &SearchQuery,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
);
|
||||
|
|
@ -223,6 +264,7 @@ pub trait SearchableItemHandle: ItemHandle {
|
|||
current_index: usize,
|
||||
direction: Direction,
|
||||
count: usize,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> usize;
|
||||
|
|
@ -232,10 +274,17 @@ pub trait SearchableItemHandle: ItemHandle {
|
|||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Task<AnyVec<dyn Send>>;
|
||||
fn find_matches_with_token(
|
||||
&self,
|
||||
query: Arc<SearchQuery>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Task<(AnyVec<dyn Send>, SearchToken)>;
|
||||
fn active_match_index(
|
||||
&self,
|
||||
direction: Direction,
|
||||
matches: &AnyVec<dyn Send>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Option<usize>;
|
||||
|
|
@ -282,12 +331,13 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
|||
&self,
|
||||
matches: &AnyVec<dyn Send>,
|
||||
active_match_index: Option<usize>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let matches = matches.downcast_ref().unwrap();
|
||||
self.update(cx, |this, cx| {
|
||||
this.update_matches(matches.as_slice(), active_match_index, window, cx)
|
||||
this.update_matches(matches.as_slice(), active_match_index, token, window, cx)
|
||||
});
|
||||
}
|
||||
fn query_suggestion(&self, window: &mut Window, cx: &mut App) -> String {
|
||||
|
|
@ -297,19 +347,26 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
|||
&self,
|
||||
index: usize,
|
||||
matches: &AnyVec<dyn Send>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let matches = matches.downcast_ref().unwrap();
|
||||
self.update(cx, |this, cx| {
|
||||
this.activate_match(index, matches.as_slice(), window, cx)
|
||||
this.activate_match(index, matches.as_slice(), token, window, cx)
|
||||
});
|
||||
}
|
||||
|
||||
fn select_matches(&self, matches: &AnyVec<dyn Send>, window: &mut Window, cx: &mut App) {
|
||||
fn select_matches(
|
||||
&self,
|
||||
matches: &AnyVec<dyn Send>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let matches = matches.downcast_ref().unwrap();
|
||||
self.update(cx, |this, cx| {
|
||||
this.select_matches(matches.as_slice(), window, cx)
|
||||
this.select_matches(matches.as_slice(), token, window, cx)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -319,6 +376,7 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
|||
current_index: usize,
|
||||
direction: Direction,
|
||||
count: usize,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> usize {
|
||||
|
|
@ -329,6 +387,7 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
|||
current_index,
|
||||
direction,
|
||||
count,
|
||||
token,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
|
|
@ -353,16 +412,38 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
|||
any_matches
|
||||
})
|
||||
}
|
||||
fn find_matches_with_token(
|
||||
&self,
|
||||
query: Arc<SearchQuery>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Task<(AnyVec<dyn Send>, SearchToken)> {
|
||||
let matches_with_token = self.update(cx, |this, cx| {
|
||||
this.find_matches_with_token(query, window, cx)
|
||||
});
|
||||
window.spawn(cx, async |_| {
|
||||
let (matches, token) = matches_with_token.await;
|
||||
let mut any_matches = AnyVec::with_capacity::<T::Match>(matches.len());
|
||||
{
|
||||
let mut any_matches = any_matches.downcast_mut::<T::Match>().unwrap();
|
||||
for mat in matches {
|
||||
any_matches.push(mat);
|
||||
}
|
||||
}
|
||||
(any_matches, token)
|
||||
})
|
||||
}
|
||||
fn active_match_index(
|
||||
&self,
|
||||
direction: Direction,
|
||||
matches: &AnyVec<dyn Send>,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Option<usize> {
|
||||
let matches = matches.downcast_ref()?;
|
||||
self.update(cx, |this, cx| {
|
||||
this.active_match_index(direction, matches.as_slice(), window, cx)
|
||||
this.active_match_index(direction, matches.as_slice(), token, window, cx)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -370,17 +451,19 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
|||
&self,
|
||||
mat: any_vec::element::ElementRef<'_, dyn Send>,
|
||||
query: &SearchQuery,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let mat = mat.downcast_ref().unwrap();
|
||||
self.update(cx, |this, cx| this.replace(mat, query, window, cx))
|
||||
self.update(cx, |this, cx| this.replace(mat, query, token, window, cx))
|
||||
}
|
||||
|
||||
fn replace_all(
|
||||
&self,
|
||||
matches: &mut dyn Iterator<Item = any_vec::element::ElementRef<'_, dyn Send>>,
|
||||
query: &SearchQuery,
|
||||
token: SearchToken,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
|
|
@ -388,6 +471,7 @@ impl<T: SearchableItem> SearchableItemHandle for Entity<T> {
|
|||
this.replace_all(
|
||||
&mut matches.map(|m| m.downcast_ref().unwrap()),
|
||||
query,
|
||||
token,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue