Reuse buffer search queries on tab switch (#18281)
Before this change, with a large chunk of text as a search query (N*10^5 in my experiments) and the buffer search bar visible, switching between editor tabs was very slow, even if the editors were N*10^2 lines long. The slow switch was caused by Zed always re-creating the Aho-Corasick queries, which is now reused. Release Notes: - Improved buffer search performance when switching tabs Co-authored-by: Piotr Osiewicz <piotr@zed.dev>
This commit is contained in:
parent
e87d6da2a6
commit
2470db4901
1 changed files with 55 additions and 43 deletions
|
@ -440,7 +440,7 @@ impl ToolbarItemView for BufferSearchBar {
|
||||||
));
|
));
|
||||||
|
|
||||||
self.active_searchable_item = Some(searchable_item_handle);
|
self.active_searchable_item = Some(searchable_item_handle);
|
||||||
drop(self.update_matches(cx));
|
drop(self.update_matches(true, cx));
|
||||||
if !self.dismissed {
|
if !self.dismissed {
|
||||||
return ToolbarItemLocation::Secondary;
|
return ToolbarItemLocation::Secondary;
|
||||||
}
|
}
|
||||||
|
@ -701,7 +701,8 @@ impl BufferSearchBar {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> oneshot::Receiver<()> {
|
) -> oneshot::Receiver<()> {
|
||||||
let options = options.unwrap_or(self.default_options);
|
let options = options.unwrap_or(self.default_options);
|
||||||
if query != self.query(cx) || self.search_options != options {
|
let updated = query != self.query(cx) || self.search_options != options;
|
||||||
|
if updated {
|
||||||
self.query_editor.update(cx, |query_editor, cx| {
|
self.query_editor.update(cx, |query_editor, cx| {
|
||||||
query_editor.buffer().update(cx, |query_buffer, cx| {
|
query_editor.buffer().update(cx, |query_buffer, cx| {
|
||||||
let len = query_buffer.len(cx);
|
let len = query_buffer.len(cx);
|
||||||
|
@ -712,7 +713,7 @@ impl BufferSearchBar {
|
||||||
self.clear_matches(cx);
|
self.clear_matches(cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
self.update_matches(cx)
|
self.update_matches(!updated, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_search_option_button(
|
fn render_search_option_button(
|
||||||
|
@ -738,7 +739,7 @@ impl BufferSearchBar {
|
||||||
) {
|
) {
|
||||||
self.search_options.toggle(search_option);
|
self.search_options.toggle(search_option);
|
||||||
self.default_options = self.search_options;
|
self.default_options = self.search_options;
|
||||||
drop(self.update_matches(cx));
|
drop(self.update_matches(false, cx));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,7 +842,7 @@ impl BufferSearchBar {
|
||||||
editor::EditorEvent::Edited { .. } => {
|
editor::EditorEvent::Edited { .. } => {
|
||||||
self.smartcase(cx);
|
self.smartcase(cx);
|
||||||
self.clear_matches(cx);
|
self.clear_matches(cx);
|
||||||
let search = self.update_matches(cx);
|
let search = self.update_matches(false, cx);
|
||||||
|
|
||||||
let width = editor.update(cx, |editor, cx| {
|
let width = editor.update(cx, |editor, cx| {
|
||||||
let text_layout_details = editor.text_layout_details(cx);
|
let text_layout_details = editor.text_layout_details(cx);
|
||||||
|
@ -879,7 +880,7 @@ impl BufferSearchBar {
|
||||||
fn on_active_searchable_item_event(&mut self, event: &SearchEvent, cx: &mut ViewContext<Self>) {
|
fn on_active_searchable_item_event(&mut self, event: &SearchEvent, cx: &mut ViewContext<Self>) {
|
||||||
match event {
|
match event {
|
||||||
SearchEvent::MatchesInvalidated => {
|
SearchEvent::MatchesInvalidated => {
|
||||||
drop(self.update_matches(cx));
|
drop(self.update_matches(false, cx));
|
||||||
}
|
}
|
||||||
SearchEvent::ActiveMatchChanged => self.update_match_index(cx),
|
SearchEvent::ActiveMatchChanged => self.update_match_index(cx),
|
||||||
}
|
}
|
||||||
|
@ -897,7 +898,7 @@ impl BufferSearchBar {
|
||||||
if let Some(active_item) = self.active_searchable_item.as_mut() {
|
if let Some(active_item) = self.active_searchable_item.as_mut() {
|
||||||
self.selection_search_enabled = !self.selection_search_enabled;
|
self.selection_search_enabled = !self.selection_search_enabled;
|
||||||
active_item.toggle_filtered_search_ranges(self.selection_search_enabled, cx);
|
active_item.toggle_filtered_search_ranges(self.selection_search_enabled, cx);
|
||||||
drop(self.update_matches(cx));
|
drop(self.update_matches(false, cx));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -937,7 +938,11 @@ impl BufferSearchBar {
|
||||||
.extend(active_item_matches);
|
.extend(active_item_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_matches(&mut self, cx: &mut ViewContext<Self>) -> oneshot::Receiver<()> {
|
fn update_matches(
|
||||||
|
&mut self,
|
||||||
|
reuse_existing_query: bool,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> oneshot::Receiver<()> {
|
||||||
let (done_tx, done_rx) = oneshot::channel();
|
let (done_tx, done_rx) = oneshot::channel();
|
||||||
let query = self.query(cx);
|
let query = self.query(cx);
|
||||||
self.pending_search.take();
|
self.pending_search.take();
|
||||||
|
@ -949,44 +954,51 @@ impl BufferSearchBar {
|
||||||
let _ = done_tx.send(());
|
let _ = done_tx.send(());
|
||||||
cx.notify();
|
cx.notify();
|
||||||
} else {
|
} else {
|
||||||
let query: Arc<_> = if self.search_options.contains(SearchOptions::REGEX) {
|
let query: Arc<_> = if let Some(search) =
|
||||||
match SearchQuery::regex(
|
self.active_search.take().filter(|_| reuse_existing_query)
|
||||||
query,
|
{
|
||||||
self.search_options.contains(SearchOptions::WHOLE_WORD),
|
search
|
||||||
self.search_options.contains(SearchOptions::CASE_SENSITIVE),
|
|
||||||
false,
|
|
||||||
Default::default(),
|
|
||||||
Default::default(),
|
|
||||||
None,
|
|
||||||
) {
|
|
||||||
Ok(query) => query.with_replacement(self.replacement(cx)),
|
|
||||||
Err(_) => {
|
|
||||||
self.query_contains_error = true;
|
|
||||||
self.clear_active_searchable_item_matches(cx);
|
|
||||||
cx.notify();
|
|
||||||
return done_rx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
match SearchQuery::text(
|
if self.search_options.contains(SearchOptions::REGEX) {
|
||||||
query,
|
match SearchQuery::regex(
|
||||||
self.search_options.contains(SearchOptions::WHOLE_WORD),
|
query,
|
||||||
self.search_options.contains(SearchOptions::CASE_SENSITIVE),
|
self.search_options.contains(SearchOptions::WHOLE_WORD),
|
||||||
false,
|
self.search_options.contains(SearchOptions::CASE_SENSITIVE),
|
||||||
Default::default(),
|
false,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
None,
|
Default::default(),
|
||||||
) {
|
None,
|
||||||
Ok(query) => query.with_replacement(self.replacement(cx)),
|
) {
|
||||||
Err(_) => {
|
Ok(query) => query.with_replacement(self.replacement(cx)),
|
||||||
self.query_contains_error = true;
|
Err(_) => {
|
||||||
self.clear_active_searchable_item_matches(cx);
|
self.query_contains_error = true;
|
||||||
cx.notify();
|
self.clear_active_searchable_item_matches(cx);
|
||||||
return done_rx;
|
cx.notify();
|
||||||
|
return done_rx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match SearchQuery::text(
|
||||||
|
query,
|
||||||
|
self.search_options.contains(SearchOptions::WHOLE_WORD),
|
||||||
|
self.search_options.contains(SearchOptions::CASE_SENSITIVE),
|
||||||
|
false,
|
||||||
|
Default::default(),
|
||||||
|
Default::default(),
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
Ok(query) => query.with_replacement(self.replacement(cx)),
|
||||||
|
Err(_) => {
|
||||||
|
self.query_contains_error = true;
|
||||||
|
self.clear_active_searchable_item_matches(cx);
|
||||||
|
cx.notify();
|
||||||
|
return done_rx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.into()
|
||||||
.into();
|
};
|
||||||
|
|
||||||
self.active_search = Some(query.clone());
|
self.active_search = Some(query.clone());
|
||||||
let query_text = query.as_str().to_string();
|
let query_text = query.as_str().to_string();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue