Don't starve the main thread adding too many search excerpts at once

This commit is contained in:
Antonio Scandurra 2023-01-18 14:22:23 +01:00
parent 8ca0f9ac99
commit a8f466b422
3 changed files with 35 additions and 12 deletions

1
Cargo.lock generated
View file

@ -5520,6 +5520,7 @@ dependencies = [
"serde_json", "serde_json",
"settings", "settings",
"smallvec", "smallvec",
"smol",
"theme", "theme",
"unindent", "unindent",
"util", "util",

View file

@ -23,6 +23,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] }
postage = { version = "0.4.1", features = ["futures-traits"] } postage = { version = "0.4.1", features = ["futures-traits"] }
serde = { version = "1.0", features = ["derive", "rc"] } serde = { version = "1.0", features = ["derive", "rc"] }
smallvec = { version = "1.6", features = ["union"] } smallvec = { version = "1.6", features = ["union"] }
smol = "1.2"
[dev-dependencies] [dev-dependencies]
editor = { path = "../editor", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] }

View file

@ -18,6 +18,7 @@ use settings::Settings;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
mem,
ops::Range, ops::Range,
path::PathBuf, path::PathBuf,
sync::Arc, sync::Arc,
@ -67,6 +68,7 @@ struct ProjectSearch {
pending_search: Option<Task<Option<()>>>, pending_search: Option<Task<Option<()>>>,
match_ranges: Vec<Range<Anchor>>, match_ranges: Vec<Range<Anchor>>,
active_query: Option<SearchQuery>, active_query: Option<SearchQuery>,
search_id: usize,
} }
pub struct ProjectSearchView { pub struct ProjectSearchView {
@ -78,6 +80,7 @@ pub struct ProjectSearchView {
regex: bool, regex: bool,
query_contains_error: bool, query_contains_error: bool,
active_match_index: Option<usize>, active_match_index: Option<usize>,
search_id: usize,
} }
pub struct ProjectSearchBar { pub struct ProjectSearchBar {
@ -98,6 +101,7 @@ impl ProjectSearch {
pending_search: Default::default(), pending_search: Default::default(),
match_ranges: Default::default(), match_ranges: Default::default(),
active_query: None, active_query: None,
search_id: 0,
} }
} }
@ -110,6 +114,7 @@ impl ProjectSearch {
pending_search: Default::default(), pending_search: Default::default(),
match_ranges: self.match_ranges.clone(), match_ranges: self.match_ranges.clone(),
active_query: self.active_query.clone(), active_query: self.active_query.clone(),
search_id: self.search_id,
}) })
} }
@ -117,21 +122,25 @@ impl ProjectSearch {
let search = self let search = self
.project .project
.update(cx, |project, cx| project.search(query.clone(), cx)); .update(cx, |project, cx| project.search(query.clone(), cx));
self.search_id += 1;
self.active_query = Some(query); self.active_query = Some(query);
self.match_ranges.clear(); self.match_ranges.clear();
self.pending_search = Some(cx.spawn_weak(|this, mut cx| async move { self.pending_search = Some(cx.spawn_weak(|this, mut cx| async move {
let matches = search.await.log_err()?; let matches = search.await.log_err()?;
if let Some(this) = this.upgrade(&cx) { let this = this.upgrade(&cx)?;
let mut matches = matches.into_iter().collect::<Vec<_>>();
this.update(&mut cx, |this, cx| {
this.match_ranges.clear();
matches.sort_by_key(|(buffer, _)| buffer.read(cx).file().map(|file| file.path()));
this.excerpts.update(cx, |excerpts, cx| excerpts.clear(cx));
});
for matches_chunk in matches.chunks(100) {
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.match_ranges.clear();
let mut matches = matches.into_iter().collect::<Vec<_>>();
matches
.sort_by_key(|(buffer, _)| buffer.read(cx).file().map(|file| file.path()));
this.excerpts.update(cx, |excerpts, cx| { this.excerpts.update(cx, |excerpts, cx| {
excerpts.clear(cx); for (buffer, buffer_matches) in matches_chunk {
for (buffer, buffer_matches) in matches {
let ranges_to_highlight = excerpts.push_excerpts_with_context_lines( let ranges_to_highlight = excerpts.push_excerpts_with_context_lines(
buffer, buffer.clone(),
buffer_matches.clone(), buffer_matches.clone(),
1, 1,
cx, cx,
@ -139,10 +148,19 @@ impl ProjectSearch {
this.match_ranges.extend(ranges_to_highlight); this.match_ranges.extend(ranges_to_highlight);
} }
}); });
this.pending_search.take();
cx.notify(); cx.notify();
}); });
// Don't starve the main thread when adding lots of excerpts.
smol::future::yield_now().await;
} }
this.update(&mut cx, |this, cx| {
this.pending_search.take();
cx.notify();
});
None None
})); }));
cx.notify(); cx.notify();
@ -398,7 +416,7 @@ impl ProjectSearchView {
whole_word = active_query.whole_word(); whole_word = active_query.whole_word();
} }
} }
cx.observe(&model, |this, _, cx| this.model_changed(true, cx)) cx.observe(&model, |this, _, cx| this.model_changed(cx))
.detach(); .detach();
let query_editor = cx.add_view(|cx| { let query_editor = cx.add_view(|cx| {
@ -433,6 +451,7 @@ impl ProjectSearchView {
.detach(); .detach();
let mut this = ProjectSearchView { let mut this = ProjectSearchView {
search_id: model.read(cx).search_id,
model, model,
query_editor, query_editor,
results_editor, results_editor,
@ -442,7 +461,7 @@ impl ProjectSearchView {
query_contains_error: false, query_contains_error: false,
active_match_index: None, active_match_index: None,
}; };
this.model_changed(false, cx); this.model_changed(cx);
this this
} }
@ -562,11 +581,13 @@ impl ProjectSearchView {
cx.focus(&self.results_editor); cx.focus(&self.results_editor);
} }
fn model_changed(&mut self, reset_selections: bool, cx: &mut ViewContext<Self>) { fn model_changed(&mut self, cx: &mut ViewContext<Self>) {
let match_ranges = self.model.read(cx).match_ranges.clone(); let match_ranges = self.model.read(cx).match_ranges.clone();
if match_ranges.is_empty() { if match_ranges.is_empty() {
self.active_match_index = None; self.active_match_index = None;
} else { } else {
let prev_search_id = mem::replace(&mut self.search_id, self.model.read(cx).search_id);
let reset_selections = self.search_id != prev_search_id;
self.results_editor.update(cx, |editor, cx| { self.results_editor.update(cx, |editor, cx| {
if reset_selections { if reset_selections {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| { editor.change_selections(Some(Autoscroll::fit()), cx, |s| {