diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 1ae0f6067a..5693dd8f12 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -86,7 +86,7 @@ pub struct LocalWorktree { enum ScanRequest { RescanPaths { - paths: Vec, + relative_paths: Vec>, done: barrier::Sender, }, ExpandDir { @@ -1051,14 +1051,10 @@ impl LocalWorktree { cx: &mut ModelContext, ) -> Option>> { 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 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() { fs.remove_file(&abs_path, Default::default()).await?; } else { @@ -1071,18 +1067,18 @@ impl LocalWorktree { ) .await?; } - anyhow::Ok(abs_path) + anyhow::Ok(entry.path) }); Some(cx.spawn(|this, mut cx| async move { - let abs_path = delete.await?; + let path = delete.await?; let (tx, mut rx) = barrier::channel(); this.update(&mut cx, |this, _| { this.as_local_mut() .unwrap() .scan_requests_tx .try_send(ScanRequest::RescanPaths { - paths: vec![abs_path], + relative_paths: vec![path], done: tx, }) })?; @@ -1168,27 +1164,19 @@ impl LocalWorktree { old_path: Option>, cx: &mut ModelContext, ) -> Task> { - let fs = self.fs.clone(); - let abs_root_path = self.abs_path.clone(); let path_changes_tx = self.scan_requests_tx.clone(); cx.spawn_weak(move |this, mut cx| async move { - let abs_path = fs.canonicalize(&abs_root_path).await?; - let mut paths = Vec::with_capacity(2); - paths.push(if path.file_name().is_some() { - abs_path.join(&path) + let relative_paths = if let Some(old_path) = old_path.as_ref() { + vec![old_path.clone(), path.clone()] } else { - abs_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() - }); - } + vec![path.clone()] + }; 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; this.upgrade(&cx) .ok_or_else(|| anyhow!("worktree was dropped"))? @@ -2975,38 +2963,81 @@ impl BackgroundScanner { } 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 { - ScanRequest::RescanPaths { paths, done } => { - self.reload_entries_for_paths(paths, None).await; + ScanRequest::RescanPaths { + 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::>(); + self.reload_entries_for_paths(root_path, root_canonical_path, abs_paths, None) + .await; self.send_status_update(false, Some(done)) } ScanRequest::ExpandDir { entry_id, done } => { - let path = { + let abs_path; + { let mut state = self.state.lock(); - state.expanded_dirs.insert(entry_id); - state - .snapshot - .entry_for_id(entry_id) - .map(|e| state.snapshot.absolutize(&e.path)) - }; - 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)); + if let Some(entry) = state.snapshot.entry_for_id(entry_id) { + abs_path = root_canonical_path.join(&entry.path); + state.expanded_dirs.insert(entry_id); + } else { + return true; } + }; + + 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 } } } - async fn process_events(&mut self, paths: Vec) { + async fn process_events(&mut self, abs_paths: Vec) { + 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 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; drop(scan_job_tx); 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 let Some(entry) = state.snapshot.entry_for_path(&job.path) { if !state.is_entry_expanded(entry) { + log::debug!("defer scanning directory {:?}", job.path); return Ok(()); } } @@ -3168,6 +3200,8 @@ impl BackgroundScanner { ) }; + log::debug!("scan directory {:?}", job.path); + let mut root_canonical_path = None; let mut child_paths = self.fs.read_dir(&job.abs_path).await?; while let Some(child_abs_path) = child_paths.next().await { @@ -3326,16 +3360,15 @@ impl BackgroundScanner { async fn reload_entries_for_paths( &self, + root_abs_path: Arc, + root_canonical_path: PathBuf, mut abs_paths: Vec, scan_queue_tx: Option>, ) -> Option>> { let doing_recursive_update = scan_queue_tx.is_some(); - abs_paths.sort_unstable(); 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( abs_paths .iter() diff --git a/crates/project/src/worktree_tests.rs b/crates/project/src/worktree_tests.rs index 00697d04a2..216e14f51c 100644 --- a/crates/project/src/worktree_tests.rs +++ b/crates/project/src/worktree_tests.rs @@ -369,7 +369,14 @@ async fn test_rescan_with_gitignore(cx: &mut TestAppContext) { .unwrap(); cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) .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| { let tree = tree.read(cx); assert!(