Output non-ignored files first

This commit is contained in:
Kirill Bulatov 2023-11-22 19:03:27 +02:00
parent 92953fb53d
commit 566857b0b7
2 changed files with 108 additions and 84 deletions

View File

@ -557,6 +557,7 @@ enum SearchMatchCandidate {
}, },
Path { Path {
worktree_id: WorktreeId, worktree_id: WorktreeId,
is_ignored: bool,
path: Arc<Path>, path: Arc<Path>,
}, },
} }
@ -5743,88 +5744,9 @@ impl Project {
.log_err(); .log_err();
} }
// TODO kb parallelize directory traversal
background background
.scoped(|scope| { .scoped(|scope| {
if query.include_ignored() {
scope.spawn(async move {
for snapshot in snapshots {
for ignored_entry in snapshot
.entries(query.include_ignored())
.filter(|e| e.is_ignored)
{
let mut ignored_paths_to_process =
VecDeque::from([snapshot.abs_path().join(&ignored_entry.path)]);
while let Some(ignored_abs_path) =
ignored_paths_to_process.pop_front()
{
if !query.file_matches(Some(&ignored_abs_path))
|| snapshot.is_abs_path_excluded(&ignored_abs_path)
{
continue;
}
if let Some(fs_metadata) = fs
.metadata(&ignored_abs_path)
.await
.with_context(|| {
format!("fetching fs metadata for {ignored_abs_path:?}")
})
.log_err()
.flatten()
{
if fs_metadata.is_dir {
if let Some(mut subfiles) = fs
.read_dir(&ignored_abs_path)
.await
.with_context(|| {
format!(
"listing ignored path {ignored_abs_path:?}"
)
})
.log_err()
{
while let Some(subfile) = subfiles.next().await {
if let Some(subfile) = subfile.log_err() {
ignored_paths_to_process
.push_front(subfile);
}
}
}
} else if !fs_metadata.is_symlink {
let matches = if let Some(file) = fs
.open_sync(&ignored_abs_path)
.await
.with_context(|| {
format!(
"Opening ignored path {ignored_abs_path:?}"
)
})
.log_err()
{
query.detect(file).unwrap_or(false)
} else {
false
};
if matches {
let project_path = SearchMatchCandidate::Path {
worktree_id: snapshot.id(),
path: ignored_entry.path.clone(),
};
if matching_paths_tx
.send(project_path)
.await
.is_err()
{
return;
}
}
}
}
}
}
}
});
}
for worker_ix in 0..workers { for worker_ix in 0..workers {
let worker_start_ix = worker_ix * paths_per_worker; let worker_start_ix = worker_ix * paths_per_worker;
let worker_end_ix = worker_start_ix + paths_per_worker; let worker_end_ix = worker_start_ix + paths_per_worker;
@ -5878,6 +5800,7 @@ impl Project {
let project_path = SearchMatchCandidate::Path { let project_path = SearchMatchCandidate::Path {
worktree_id: snapshot.id(), worktree_id: snapshot.id(),
path: entry.path.clone(), path: entry.path.clone(),
is_ignored: entry.is_ignored,
}; };
if matching_paths_tx.send(project_path).await.is_err() { if matching_paths_tx.send(project_path).await.is_err() {
break; break;
@ -5890,6 +5813,92 @@ impl Project {
} }
}); });
} }
if query.include_ignored() {
scope.spawn(async move {
for snapshot in snapshots {
for ignored_entry in snapshot
.entries(query.include_ignored())
.filter(|e| e.is_ignored)
{
let mut ignored_paths_to_process =
VecDeque::from([snapshot.abs_path().join(&ignored_entry.path)]);
while let Some(ignored_abs_path) =
ignored_paths_to_process.pop_front()
{
if !query.file_matches(Some(&ignored_abs_path))
|| snapshot.is_abs_path_excluded(&ignored_abs_path)
{
continue;
}
if let Some(fs_metadata) = fs
.metadata(&ignored_abs_path)
.await
.with_context(|| {
format!("fetching fs metadata for {ignored_abs_path:?}")
})
.log_err()
.flatten()
{
if fs_metadata.is_dir {
if let Some(mut subfiles) = fs
.read_dir(&ignored_abs_path)
.await
.with_context(|| {
format!(
"listing ignored path {ignored_abs_path:?}"
)
})
.log_err()
{
while let Some(subfile) = subfiles.next().await {
if let Some(subfile) = subfile.log_err() {
ignored_paths_to_process.push_back(subfile);
}
}
}
} else if !fs_metadata.is_symlink {
let matches = if let Some(file) = fs
.open_sync(&ignored_abs_path)
.await
.with_context(|| {
format!(
"Opening ignored path {ignored_abs_path:?}"
)
})
.log_err()
{
query.detect(file).unwrap_or(false)
} else {
false
};
if matches {
let project_path = SearchMatchCandidate::Path {
worktree_id: snapshot.id(),
path: Arc::from(
ignored_abs_path
.strip_prefix(snapshot.abs_path())
.expect(
"scanning worktree-related files",
),
),
is_ignored: true,
};
if matching_paths_tx
.send(project_path)
.await
.is_err()
{
return;
}
}
}
}
}
}
}
});
}
}) })
.await; .await;
} }
@ -5998,11 +6007,24 @@ impl Project {
let (buffers_tx, buffers_rx) = smol::channel::bounded(1024); let (buffers_tx, buffers_rx) = smol::channel::bounded(1024);
let (sorted_buffers_tx, sorted_buffers_rx) = futures::channel::oneshot::channel(); let (sorted_buffers_tx, sorted_buffers_rx) = futures::channel::oneshot::channel();
cx.spawn(|this, cx| async move { cx.spawn(|this, cx| async move {
let mut buffers = vec![]; let mut buffers = Vec::new();
let mut ignored_buffers = Vec::new();
while let Some(entry) = matching_paths_rx.next().await { while let Some(entry) = matching_paths_rx.next().await {
buffers.push(entry); if matches!(
entry,
SearchMatchCandidate::Path {
is_ignored: true,
..
}
) {
ignored_buffers.push(entry);
} else {
buffers.push(entry);
}
} }
buffers.sort_by_key(|candidate| candidate.path()); buffers.sort_by_key(|candidate| candidate.path());
ignored_buffers.sort_by_key(|candidate| candidate.path());
buffers.extend(ignored_buffers);
let matching_paths = buffers.clone(); let matching_paths = buffers.clone();
let _ = sorted_buffers_tx.send(buffers); let _ = sorted_buffers_tx.send(buffers);
for (index, candidate) in matching_paths.into_iter().enumerate() { for (index, candidate) in matching_paths.into_iter().enumerate() {
@ -6014,7 +6036,9 @@ impl Project {
cx.spawn(|mut cx| async move { cx.spawn(|mut cx| async move {
let buffer = match candidate { let buffer = match candidate {
SearchMatchCandidate::OpenBuffer { buffer, .. } => Some(buffer), SearchMatchCandidate::OpenBuffer { buffer, .. } => Some(buffer),
SearchMatchCandidate::Path { worktree_id, path } => this SearchMatchCandidate::Path {
worktree_id, path, ..
} => this
.update(&mut cx, |this, cx| { .update(&mut cx, |this, cx| {
this.open_buffer((worktree_id, path), cx) this.open_buffer((worktree_id, path), cx)
}) })

View File

@ -219,7 +219,7 @@ impl ProjectSearch {
this.no_results = Some(true); this.no_results = Some(true);
}); });
// TODO kb check for cancellations here and actually stop the search // TODO kb check for cancellations here and actually stop the search?
while let Some((buffer, anchors)) = matches.next().await { while let Some((buffer, anchors)) = matches.next().await {
let mut ranges = this.update(&mut cx, |this, cx| { let mut ranges = this.update(&mut cx, |this, cx| {
this.no_results = Some(false); this.no_results = Some(false);