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,11 +221,16 @@ impl ProjectSearch {
let mut limit_reached = false; let mut limit_reached = false;
while let Some(results) = matches.next().await { while let Some(results) = matches.next().await {
for result in results { let tasks = results
.into_iter()
.map(|result| {
let this = this.clone();
cx.spawn(|mut cx| async move {
match result { match result {
project::search::SearchResult::Buffer { buffer, ranges } => { project::search::SearchResult::Buffer { buffer, ranges } => {
let mut match_ranges = this let mut match_ranges_rx =
.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.excerpts.update(cx, |excerpts, cx| { this.excerpts.update(cx, |excerpts, cx| {
excerpts.stream_excerpts_with_context_lines( excerpts.stream_excerpts_with_context_lines(
buffer, buffer,
@ -234,24 +239,39 @@ impl ProjectSearch {
cx, cx,
) )
}) })
}) })?;
.ok()?;
while let Some(range) = match_ranges.next().await { let mut match_ranges = vec![];
this.update(&mut cx, |this, _| { while let Some(range) = match_ranges_rx.next().await {
this.no_results = Some(false); match_ranges.push(range);
this.match_ranges.push(range)
})
.ok()?;
} }
this.update(&mut cx, |_, cx| cx.notify()).ok()?; anyhow::Ok((match_ranges, false))
} }
project::search::SearchResult::LimitReached => { project::search::SearchResult::LimitReached => {
limit_reached = true; anyhow::Ok((vec![], 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| { this.update(&mut cx, |this, cx| {
this.limit_reached = limit_reached; this.limit_reached = limit_reached;