project search: Render results in batches (#16960)

This improves performance, because we don't render after every single
match/range that was added to the results.

I think for my example search it's twice as fast?

## Numbers

Recorded in debug mode (because it's 6:30pm and my poor computer has
spun its fan enough for today and also because you can see the change of
the effect more in debug mode), rendering `<` searched in `zed.dev`
repo:

- Before: `14.59558225s`
- After: `2.604320875s`




## Videos
Before (recorded in release mode):



https://github.com/user-attachments/assets/909260fa-3e69-49ab-8786-dd384e2a27ee

After (recorded in release mode):



https://github.com/user-attachments/assets/fc8a85d3-e575-470f-b59c-16a6df8b3f80
## Release Notes

Release Notes:

- Improved performance of rendering project-search results in the
multi-buffer after finding them.
This commit is contained in:
Thorsten Ball 2024-08-27 18:37:26 +02:00 committed by GitHub
parent ff26abdc2f
commit 1d868e19f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -221,36 +221,56 @@ impl ProjectSearch {
let mut limit_reached = false;
while let Some(results) = matches.next().await {
for result in results {
match result {
project::search::SearchResult::Buffer { buffer, ranges } => {
let mut match_ranges = this
.update(&mut cx, |this, cx| {
this.excerpts.update(cx, |excerpts, cx| {
excerpts.stream_excerpts_with_context_lines(
buffer,
ranges,
editor::DEFAULT_MULTIBUFFER_CONTEXT,
cx,
)
})
})
.ok()?;
let tasks = results
.into_iter()
.map(|result| {
let this = this.clone();
while let Some(range) = match_ranges.next().await {
this.update(&mut cx, |this, _| {
this.no_results = Some(false);
this.match_ranges.push(range)
})
.ok()?;
cx.spawn(|mut cx| async move {
match result {
project::search::SearchResult::Buffer { buffer, ranges } => {
let mut match_ranges_rx =
this.update(&mut cx, |this, cx| {
this.excerpts.update(cx, |excerpts, cx| {
excerpts.stream_excerpts_with_context_lines(
buffer,
ranges,
editor::DEFAULT_MULTIBUFFER_CONTEXT,
cx,
)
})
})?;
let mut match_ranges = vec![];
while let Some(range) = match_ranges_rx.next().await {
match_ranges.push(range);
}
anyhow::Ok((match_ranges, false))
}
project::search::SearchResult::LimitReached => {
anyhow::Ok((vec![], true))
}
}
this.update(&mut cx, |_, cx| cx.notify()).ok()?;
}
project::search::SearchResult::LimitReached => {
limit_reached = true;
}
})
})
.collect::<Vec<_>>();
let result_ranges = futures::future::join_all(tasks).await;
let mut combined_ranges = vec![];
for (ranges, result_limit_reached) in result_ranges.into_iter().flatten() {
combined_ranges.extend(ranges);
if result_limit_reached {
limit_reached = result_limit_reached;
}
}
this.update(&mut cx, |this, cx| {
if !combined_ranges.is_empty() {
this.no_results = Some(false);
this.match_ranges.extend(combined_ranges);
cx.notify();
}
})
.ok()?;
}
this.update(&mut cx, |this, cx| {