Fix confusion between canonical vs non-canonical paths when rescanning, expanding paths

This commit is contained in:
Max Brunsfeld 2023-06-16 11:53:05 -07:00
parent 3c06bd056a
commit 1b71589514
2 changed files with 87 additions and 47 deletions

View file

@ -86,7 +86,7 @@ pub struct LocalWorktree {
enum ScanRequest { enum ScanRequest {
RescanPaths { RescanPaths {
paths: Vec<PathBuf>, relative_paths: Vec<Arc<Path>>,
done: barrier::Sender, done: barrier::Sender,
}, },
ExpandDir { ExpandDir {
@ -1051,14 +1051,10 @@ impl LocalWorktree {
cx: &mut ModelContext<Worktree>, cx: &mut ModelContext<Worktree>,
) -> Option<Task<Result<()>>> { ) -> Option<Task<Result<()>>> {
let entry = self.entry_for_id(entry_id)?.clone(); let entry = self.entry_for_id(entry_id)?.clone();
let abs_path = self.abs_path.clone(); let abs_path = self.absolutize(&entry.path);
let fs = self.fs.clone(); let fs = self.fs.clone();
let delete = cx.background().spawn(async move { let delete = cx.background().spawn(async move {
let mut abs_path = fs.canonicalize(&abs_path).await?;
if entry.path.file_name().is_some() {
abs_path = abs_path.join(&entry.path);
}
if entry.is_file() { if entry.is_file() {
fs.remove_file(&abs_path, Default::default()).await?; fs.remove_file(&abs_path, Default::default()).await?;
} else { } else {
@ -1071,18 +1067,18 @@ impl LocalWorktree {
) )
.await?; .await?;
} }
anyhow::Ok(abs_path) anyhow::Ok(entry.path)
}); });
Some(cx.spawn(|this, mut cx| async move { Some(cx.spawn(|this, mut cx| async move {
let abs_path = delete.await?; let path = delete.await?;
let (tx, mut rx) = barrier::channel(); let (tx, mut rx) = barrier::channel();
this.update(&mut cx, |this, _| { this.update(&mut cx, |this, _| {
this.as_local_mut() this.as_local_mut()
.unwrap() .unwrap()
.scan_requests_tx .scan_requests_tx
.try_send(ScanRequest::RescanPaths { .try_send(ScanRequest::RescanPaths {
paths: vec![abs_path], relative_paths: vec![path],
done: tx, done: tx,
}) })
})?; })?;
@ -1168,27 +1164,19 @@ impl LocalWorktree {
old_path: Option<Arc<Path>>, old_path: Option<Arc<Path>>,
cx: &mut ModelContext<Worktree>, cx: &mut ModelContext<Worktree>,
) -> Task<Result<Entry>> { ) -> Task<Result<Entry>> {
let fs = self.fs.clone();
let abs_root_path = self.abs_path.clone();
let path_changes_tx = self.scan_requests_tx.clone(); let path_changes_tx = self.scan_requests_tx.clone();
cx.spawn_weak(move |this, mut cx| async move { cx.spawn_weak(move |this, mut cx| async move {
let abs_path = fs.canonicalize(&abs_root_path).await?; let relative_paths = if let Some(old_path) = old_path.as_ref() {
let mut paths = Vec::with_capacity(2); vec![old_path.clone(), path.clone()]
paths.push(if path.file_name().is_some() {
abs_path.join(&path)
} else { } else {
abs_path.clone() vec![path.clone()]
}); };
if let Some(old_path) = old_path {
paths.push(if old_path.file_name().is_some() {
abs_path.join(&old_path)
} else {
abs_path.clone()
});
}
let (tx, mut rx) = barrier::channel(); let (tx, mut rx) = barrier::channel();
path_changes_tx.try_send(ScanRequest::RescanPaths { paths, done: tx })?; path_changes_tx.try_send(ScanRequest::RescanPaths {
relative_paths,
done: tx,
})?;
rx.recv().await; rx.recv().await;
this.upgrade(&cx) this.upgrade(&cx)
.ok_or_else(|| anyhow!("worktree was dropped"))? .ok_or_else(|| anyhow!("worktree was dropped"))?
@ -2975,38 +2963,81 @@ impl BackgroundScanner {
} }
async fn process_scan_request(&self, request: ScanRequest) -> bool { async fn process_scan_request(&self, request: ScanRequest) -> bool {
let root_path = self.state.lock().snapshot.abs_path.clone();
let root_canonical_path = match self.fs.canonicalize(&root_path).await {
Ok(path) => path,
Err(err) => {
log::error!("failed to canonicalize root path: {}", err);
return false;
}
};
match request { match request {
ScanRequest::RescanPaths { paths, done } => { ScanRequest::RescanPaths {
self.reload_entries_for_paths(paths, None).await; relative_paths,
done,
} => {
let abs_paths = relative_paths
.into_iter()
.map(|path| {
if path.file_name().is_some() {
root_canonical_path.join(path)
} else {
root_canonical_path.clone()
}
})
.collect::<Vec<_>>();
self.reload_entries_for_paths(root_path, root_canonical_path, abs_paths, None)
.await;
self.send_status_update(false, Some(done)) self.send_status_update(false, Some(done))
} }
ScanRequest::ExpandDir { entry_id, done } => { ScanRequest::ExpandDir { entry_id, done } => {
let path = { let abs_path;
{
let mut state = self.state.lock(); let mut state = self.state.lock();
state.expanded_dirs.insert(entry_id); if let Some(entry) = state.snapshot.entry_for_id(entry_id) {
state abs_path = root_canonical_path.join(&entry.path);
.snapshot state.expanded_dirs.insert(entry_id);
.entry_for_id(entry_id) } else {
.map(|e| state.snapshot.absolutize(&e.path)) return true;
};
if let Some(path) = path {
let (scan_job_tx, mut scan_job_rx) = channel::unbounded();
self.reload_entries_for_paths(vec![path.clone()], Some(scan_job_tx))
.await;
if let Some(job) = scan_job_rx.next().await {
self.scan_dir(&job).await.log_err();
self.send_status_update(false, Some(done));
} }
};
let (scan_job_tx, mut scan_job_rx) = channel::unbounded();
self.reload_entries_for_paths(
root_path,
root_canonical_path,
vec![abs_path],
Some(scan_job_tx),
)
.await;
if let Some(job) = scan_job_rx.next().await {
self.scan_dir(&job).await.log_err();
self.send_status_update(false, Some(done));
} }
true true
} }
} }
} }
async fn process_events(&mut self, paths: Vec<PathBuf>) { async fn process_events(&mut self, abs_paths: Vec<PathBuf>) {
let root_path = self.state.lock().snapshot.abs_path.clone();
let root_canonical_path = match self.fs.canonicalize(&root_path).await {
Ok(path) => path,
Err(err) => {
log::error!("failed to canonicalize root path: {}", err);
return;
}
};
let (scan_job_tx, scan_job_rx) = channel::unbounded(); let (scan_job_tx, scan_job_rx) = channel::unbounded();
let paths = self let paths = self
.reload_entries_for_paths(paths, Some(scan_job_tx.clone())) .reload_entries_for_paths(
root_path,
root_canonical_path,
abs_paths,
Some(scan_job_tx.clone()),
)
.await; .await;
drop(scan_job_tx); drop(scan_job_tx);
self.scan_dirs(false, scan_job_rx).await; self.scan_dirs(false, scan_job_rx).await;
@ -3152,6 +3183,7 @@ impl BackgroundScanner {
if job.is_outside_root || job.ignore_stack.is_all() { if job.is_outside_root || job.ignore_stack.is_all() {
if let Some(entry) = state.snapshot.entry_for_path(&job.path) { if let Some(entry) = state.snapshot.entry_for_path(&job.path) {
if !state.is_entry_expanded(entry) { if !state.is_entry_expanded(entry) {
log::debug!("defer scanning directory {:?}", job.path);
return Ok(()); return Ok(());
} }
} }
@ -3168,6 +3200,8 @@ impl BackgroundScanner {
) )
}; };
log::debug!("scan directory {:?}", job.path);
let mut root_canonical_path = None; let mut root_canonical_path = None;
let mut child_paths = self.fs.read_dir(&job.abs_path).await?; let mut child_paths = self.fs.read_dir(&job.abs_path).await?;
while let Some(child_abs_path) = child_paths.next().await { while let Some(child_abs_path) = child_paths.next().await {
@ -3326,16 +3360,15 @@ impl BackgroundScanner {
async fn reload_entries_for_paths( async fn reload_entries_for_paths(
&self, &self,
root_abs_path: Arc<Path>,
root_canonical_path: PathBuf,
mut abs_paths: Vec<PathBuf>, mut abs_paths: Vec<PathBuf>,
scan_queue_tx: Option<Sender<ScanJob>>, scan_queue_tx: Option<Sender<ScanJob>>,
) -> Option<Vec<Arc<Path>>> { ) -> Option<Vec<Arc<Path>>> {
let doing_recursive_update = scan_queue_tx.is_some(); let doing_recursive_update = scan_queue_tx.is_some();
abs_paths.sort_unstable(); abs_paths.sort_unstable();
abs_paths.dedup_by(|a, b| a.starts_with(&b)); abs_paths.dedup_by(|a, b| a.starts_with(&b));
let root_abs_path = self.state.lock().snapshot.abs_path.clone();
let root_canonical_path = self.fs.canonicalize(&root_abs_path).await.log_err()?;
let metadata = futures::future::join_all( let metadata = futures::future::join_all(
abs_paths abs_paths
.iter() .iter()

View file

@ -369,7 +369,14 @@ async fn test_rescan_with_gitignore(cx: &mut TestAppContext) {
.unwrap(); .unwrap();
cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete())
.await; .await;
tree.flush_fs_events(cx).await;
tree.update(cx, |tree, cx| {
let tree = tree.as_local_mut().unwrap();
tree.expand_dir(tree.entry_for_path("ignored-dir").unwrap().id, cx)
})
.recv()
.await;
cx.read(|cx| { cx.read(|cx| {
let tree = tree.read(cx); let tree = tree.read(cx);
assert!( assert!(