search: New old search implementation (#39956)
This is an in-progress work on changing how task scheduler affects performance of project search. Instead of relying on tasks being executed at a discretion of the task scheduler, we want to experiment with having a set of "agents" that prioritize driving in-progress project search matches to completion over pushing the whole thing to completion. This should hopefully significantly improve throughput & latency of project search. Release Notes: - Improved project search performance --------- Co-authored-by: Smit Barmase <smit@zed.dev> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This commit is contained in:
co-authored by
Smit Barmase
Smit Barmase
parent
85c2aa7325
commit
7c4fb5a899
@@ -8,10 +8,7 @@ use std::{
|
||||
use anyhow::{Context as _, Result, anyhow, bail};
|
||||
use collections::{HashMap, HashSet};
|
||||
use fs::{Fs, copy_recursive};
|
||||
use futures::{
|
||||
FutureExt, SinkExt,
|
||||
future::{BoxFuture, Shared},
|
||||
};
|
||||
use futures::{FutureExt, SinkExt, future::Shared};
|
||||
use gpui::{
|
||||
App, AppContext as _, AsyncApp, Context, Entity, EntityId, EventEmitter, Task, WeakEntity,
|
||||
};
|
||||
@@ -999,148 +996,14 @@ impl WorktreeStore {
|
||||
matching_paths_rx
|
||||
}
|
||||
|
||||
fn scan_ignored_dir<'a>(
|
||||
fs: &'a Arc<dyn Fs>,
|
||||
snapshot: &'a worktree::Snapshot,
|
||||
path: &'a RelPath,
|
||||
query: &'a SearchQuery,
|
||||
filter_tx: &'a Sender<MatchingEntry>,
|
||||
output_tx: &'a Sender<oneshot::Receiver<ProjectPath>>,
|
||||
) -> BoxFuture<'a, Result<()>> {
|
||||
async move {
|
||||
let abs_path = snapshot.absolutize(path);
|
||||
let Some(mut files) = fs
|
||||
.read_dir(&abs_path)
|
||||
.await
|
||||
.with_context(|| format!("listing ignored path {abs_path:?}"))
|
||||
.log_err()
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut results = Vec::new();
|
||||
|
||||
while let Some(Ok(file)) = files.next().await {
|
||||
let Some(metadata) = fs
|
||||
.metadata(&file)
|
||||
.await
|
||||
.with_context(|| format!("fetching fs metadata for {abs_path:?}"))
|
||||
.log_err()
|
||||
.flatten()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if metadata.is_symlink || metadata.is_fifo {
|
||||
continue;
|
||||
}
|
||||
let relative_path = file.strip_prefix(snapshot.abs_path())?;
|
||||
let relative_path = RelPath::new(&relative_path, snapshot.path_style())
|
||||
.context("getting relative path")?;
|
||||
results.push((relative_path.into_arc(), !metadata.is_dir))
|
||||
}
|
||||
results.sort_by(|(a_path, _), (b_path, _)| a_path.cmp(b_path));
|
||||
for (path, is_file) in results {
|
||||
if is_file {
|
||||
if query.filters_path() {
|
||||
let matched_path = if query.match_full_paths() {
|
||||
let mut full_path = snapshot.root_name().as_std_path().to_owned();
|
||||
full_path.push(path.as_std_path());
|
||||
query.match_path(&full_path)
|
||||
} else {
|
||||
query.match_path(&path.as_std_path())
|
||||
};
|
||||
if !matched_path {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let (tx, rx) = oneshot::channel();
|
||||
output_tx.send(rx).await?;
|
||||
filter_tx
|
||||
.send(MatchingEntry {
|
||||
respond: tx,
|
||||
worktree_root: snapshot.abs_path().clone(),
|
||||
path: ProjectPath {
|
||||
worktree_id: snapshot.id(),
|
||||
path: path.into_arc(),
|
||||
},
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
Self::scan_ignored_dir(fs, snapshot, &path, query, filter_tx, output_tx)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
async fn find_candidate_paths(
|
||||
fs: Arc<dyn Fs>,
|
||||
snapshots: Vec<(worktree::Snapshot, WorktreeSettings)>,
|
||||
open_entries: HashSet<ProjectEntryId>,
|
||||
query: SearchQuery,
|
||||
filter_tx: Sender<MatchingEntry>,
|
||||
output_tx: Sender<oneshot::Receiver<ProjectPath>>,
|
||||
_: Arc<dyn Fs>,
|
||||
_: Vec<(worktree::Snapshot, WorktreeSettings)>,
|
||||
_: HashSet<ProjectEntryId>,
|
||||
_: SearchQuery,
|
||||
_: Sender<MatchingEntry>,
|
||||
_: Sender<oneshot::Receiver<ProjectPath>>,
|
||||
) -> Result<()> {
|
||||
for (snapshot, settings) in snapshots {
|
||||
for entry in snapshot.entries(query.include_ignored(), 0) {
|
||||
if entry.is_dir() && entry.is_ignored {
|
||||
if !settings.is_path_excluded(&entry.path) {
|
||||
Self::scan_ignored_dir(
|
||||
&fs,
|
||||
&snapshot,
|
||||
&entry.path,
|
||||
&query,
|
||||
&filter_tx,
|
||||
&output_tx,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if entry.is_fifo || !entry.is_file() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if query.filters_path() {
|
||||
let matched_path = if query.match_full_paths() {
|
||||
let mut full_path = snapshot.root_name().as_std_path().to_owned();
|
||||
full_path.push(entry.path.as_std_path());
|
||||
query.match_path(&full_path)
|
||||
} else {
|
||||
query.match_path(entry.path.as_std_path())
|
||||
};
|
||||
if !matched_path {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let (mut tx, rx) = oneshot::channel();
|
||||
|
||||
if open_entries.contains(&entry.id) {
|
||||
tx.send(ProjectPath {
|
||||
worktree_id: snapshot.id(),
|
||||
path: entry.path.clone(),
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
filter_tx
|
||||
.send(MatchingEntry {
|
||||
respond: tx,
|
||||
worktree_root: snapshot.abs_path().clone(),
|
||||
path: ProjectPath {
|
||||
worktree_id: snapshot.id(),
|
||||
path: entry.path.clone(),
|
||||
},
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
|
||||
output_tx.send(rx).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user