diff --git a/crates/project/src/ignore.rs b/crates/project/src/ignore.rs index 9eb605eaf8..8bac08b96c 100644 --- a/crates/project/src/ignore.rs +++ b/crates/project/src/ignore.rs @@ -4,7 +4,7 @@ use std::{ffi::OsStr, path::Path, sync::Arc}; pub enum IgnoreStack { None, Some { - base: Arc, + abs_base_path: Arc, ignore: Arc, parent: Arc, }, @@ -24,19 +24,19 @@ impl IgnoreStack { matches!(self, IgnoreStack::All) } - pub fn append(self: Arc, base: Arc, ignore: Arc) -> Arc { + pub fn append(self: Arc, abs_base_path: Arc, ignore: Arc) -> Arc { match self.as_ref() { IgnoreStack::All => self, _ => Arc::new(Self::Some { - base, + abs_base_path, ignore, parent: self, }), } } - pub fn is_path_ignored(&self, path: &Path, is_dir: bool) -> bool { - if is_dir && path.file_name() == Some(OsStr::new(".git")) { + pub fn is_abs_path_ignored(&self, abs_path: &Path, is_dir: bool) -> bool { + if is_dir && abs_path.file_name() == Some(OsStr::new(".git")) { return true; } @@ -44,11 +44,11 @@ impl IgnoreStack { Self::None => false, Self::All => true, Self::Some { - base, + abs_base_path, ignore, parent: prev, - } => match ignore.matched(path.strip_prefix(base).unwrap(), is_dir) { - ignore::Match::None => prev.is_path_ignored(path, is_dir), + } => match ignore.matched(abs_path.strip_prefix(abs_base_path).unwrap(), is_dir) { + ignore::Match::None => prev.is_abs_path_ignored(abs_path, is_dir), ignore::Match::Ignore(_) => true, ignore::Match::Whitelist(_) => false, }, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index d48df1b53f..c81d9d5637 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -102,7 +102,7 @@ pub struct Snapshot { #[derive(Clone)] pub struct LocalSnapshot { abs_path: Arc, - ignores: HashMap, (Arc, usize)>, + ignores_by_abs_path: HashMap, (Arc, usize)>, removed_entry_ids: HashMap, next_entry_id: Arc, snapshot: Snapshot, @@ -370,7 +370,7 @@ impl LocalWorktree { let tree = cx.add_model(move |cx: &mut ModelContext| { let mut snapshot = LocalSnapshot { abs_path, - ignores: Default::default(), + ignores_by_abs_path: Default::default(), removed_entry_ids: Default::default(), next_entry_id, snapshot: Snapshot { @@ -819,8 +819,8 @@ impl LocalWorktree { { let mut snapshot = this.background_snapshot.lock(); entry.is_ignored = snapshot - .ignore_stack_for_path(&path, entry.is_dir()) - .is_path_ignored(&path, entry.is_dir()); + .ignore_stack_for_abs_path(&abs_path, entry.is_dir()) + .is_abs_path_ignored(&abs_path, entry.is_dir()); if let Some(old_path) = old_path { snapshot.remove_path(&old_path); } @@ -1333,9 +1333,10 @@ impl LocalSnapshot { let abs_path = self.abs_path.join(&entry.path); match smol::block_on(build_gitignore(&abs_path, fs)) { Ok(ignore) => { - let ignore_dir_path = entry.path.parent().unwrap(); - self.ignores - .insert(ignore_dir_path.into(), (Arc::new(ignore), self.scan_id)); + self.ignores_by_abs_path.insert( + abs_path.parent().unwrap().into(), + (Arc::new(ignore), self.scan_id), + ); } Err(error) => { log::error!( @@ -1387,7 +1388,10 @@ impl LocalSnapshot { }; if let Some(ignore) = ignore { - self.ignores.insert(parent_path, (ignore, self.scan_id)); + self.ignores_by_abs_path.insert( + self.abs_path.join(&parent_path).into(), + (ignore, self.scan_id), + ); } if matches!(parent_entry.kind, EntryKind::PendingDir) { parent_entry.kind = EntryKind::Dir; @@ -1472,16 +1476,18 @@ impl LocalSnapshot { self.entries_by_id.edit(entries_by_id_edits, &()); if path.file_name() == Some(&GITIGNORE) { - if let Some((_, scan_id)) = self.ignores.get_mut(path.parent().unwrap()) { + let abs_parent_path = self.abs_path.join(path.parent().unwrap()); + if let Some((_, scan_id)) = self.ignores_by_abs_path.get_mut(abs_parent_path.as_path()) + { *scan_id = self.snapshot.scan_id; } } } - fn ignore_stack_for_path(&self, path: &Path, is_dir: bool) -> Arc { + fn ignore_stack_for_abs_path(&self, abs_path: &Path, is_dir: bool) -> Arc { let mut new_ignores = Vec::new(); - for ancestor in path.ancestors().skip(1) { - if let Some((ignore, _)) = self.ignores.get(ancestor) { + for ancestor in abs_path.ancestors().skip(1) { + if let Some((ignore, _)) = self.ignores_by_abs_path.get(ancestor) { new_ignores.push((ancestor, Some(ignore.clone()))); } else { new_ignores.push((ancestor, None)); @@ -1489,16 +1495,16 @@ impl LocalSnapshot { } let mut ignore_stack = IgnoreStack::none(); - for (parent_path, ignore) in new_ignores.into_iter().rev() { - if ignore_stack.is_path_ignored(&parent_path, true) { + for (parent_abs_path, ignore) in new_ignores.into_iter().rev() { + if ignore_stack.is_abs_path_ignored(&parent_abs_path, true) { ignore_stack = IgnoreStack::all(); break; } else if let Some(ignore) = ignore { - ignore_stack = ignore_stack.append(Arc::from(parent_path), ignore); + ignore_stack = ignore_stack.append(parent_abs_path.into(), ignore); } } - if ignore_stack.is_path_ignored(path, is_dir) { + if ignore_stack.is_abs_path_ignored(abs_path, is_dir) { ignore_stack = IgnoreStack::all(); } @@ -2120,7 +2126,8 @@ impl BackgroundScanner { match build_gitignore(&child_abs_path, self.fs.as_ref()).await { Ok(ignore) => { let ignore = Arc::new(ignore); - ignore_stack = ignore_stack.append(job.path.clone(), ignore.clone()); + ignore_stack = + ignore_stack.append(job.abs_path.as_path().into(), ignore.clone()); new_ignore = Some(ignore); } Err(error) => { @@ -2138,7 +2145,9 @@ impl BackgroundScanner { // new jobs as well. let mut new_jobs = new_jobs.iter_mut(); for entry in &mut new_entries { - entry.is_ignored = ignore_stack.is_path_ignored(&entry.path, entry.is_dir()); + let entry_abs_path = self.abs_path().join(&entry.path); + entry.is_ignored = + ignore_stack.is_abs_path_ignored(&entry_abs_path, entry.is_dir()); if entry.is_dir() { new_jobs.next().unwrap().ignore_stack = if entry.is_ignored { IgnoreStack::all() @@ -2157,7 +2166,7 @@ impl BackgroundScanner { ); if child_metadata.is_dir { - let is_ignored = ignore_stack.is_path_ignored(&child_path, true); + let is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, true); child_entry.is_ignored = is_ignored; new_entries.push(child_entry); new_jobs.push(ScanJob { @@ -2171,7 +2180,7 @@ impl BackgroundScanner { scan_queue: job.scan_queue.clone(), }); } else { - child_entry.is_ignored = ignore_stack.is_path_ignored(&child_path, false); + child_entry.is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, false); new_entries.push(child_entry); }; } @@ -2200,8 +2209,8 @@ impl BackgroundScanner { next_entry_id = snapshot.next_entry_id.clone(); } - let root_abs_path = if let Ok(abs_path) = self.fs.canonicalize(&root_abs_path).await { - abs_path + let root_canonical_path = if let Ok(path) = self.fs.canonicalize(&root_abs_path).await { + path } else { return false; }; @@ -2221,27 +2230,29 @@ impl BackgroundScanner { let mut snapshot = self.snapshot.lock(); snapshot.scan_id += 1; for event in &events { - if let Ok(path) = event.path.strip_prefix(&root_abs_path) { + if let Ok(path) = event.path.strip_prefix(&root_canonical_path) { snapshot.remove_path(&path); } } for (event, metadata) in events.into_iter().zip(metadata.into_iter()) { - let path: Arc = match event.path.strip_prefix(&root_abs_path) { + let path: Arc = match event.path.strip_prefix(&root_canonical_path) { Ok(path) => Arc::from(path.to_path_buf()), Err(_) => { log::error!( "unexpected event {:?} for root path {:?}", event.path, - root_abs_path + root_canonical_path ); continue; } }; + let abs_path = root_abs_path.join(&path); match metadata { Ok(Some(metadata)) => { - let ignore_stack = snapshot.ignore_stack_for_path(&path, metadata.is_dir); + let ignore_stack = + snapshot.ignore_stack_for_abs_path(&abs_path, metadata.is_dir); let mut fs_entry = Entry::new( path.clone(), &metadata, @@ -2253,7 +2264,7 @@ impl BackgroundScanner { if metadata.is_dir { self.executor .block(scan_queue_tx.send(ScanJob { - abs_path: event.path, + abs_path, path, ignore_stack, scan_queue: scan_queue_tx.clone(), @@ -2301,37 +2312,41 @@ impl BackgroundScanner { let mut ignores_to_update = Vec::new(); let mut ignores_to_delete = Vec::new(); - for (parent_path, (_, scan_id)) in &snapshot.ignores { + for (parent_abs_path, (_, scan_id)) in &snapshot.ignores_by_abs_path { + let parent_path = parent_abs_path.strip_prefix(&snapshot.abs_path).unwrap(); if *scan_id == snapshot.scan_id && snapshot.entry_for_path(parent_path).is_some() { - ignores_to_update.push(parent_path.clone()); + ignores_to_update.push(parent_abs_path.clone()); } let ignore_path = parent_path.join(&*GITIGNORE); if snapshot.entry_for_path(ignore_path).is_none() { - ignores_to_delete.push(parent_path.clone()); + ignores_to_delete.push(parent_abs_path.clone()); } } - for parent_path in ignores_to_delete { - snapshot.ignores.remove(&parent_path); - self.snapshot.lock().ignores.remove(&parent_path); + for parent_abs_path in ignores_to_delete { + snapshot.ignores_by_abs_path.remove(&parent_abs_path); + self.snapshot + .lock() + .ignores_by_abs_path + .remove(&parent_abs_path); } let (ignore_queue_tx, ignore_queue_rx) = channel::unbounded(); ignores_to_update.sort_unstable(); let mut ignores_to_update = ignores_to_update.into_iter().peekable(); - while let Some(parent_path) = ignores_to_update.next() { + while let Some(parent_abs_path) = ignores_to_update.next() { while ignores_to_update .peek() - .map_or(false, |p| p.starts_with(&parent_path)) + .map_or(false, |p| p.starts_with(&parent_abs_path)) { ignores_to_update.next().unwrap(); } - let ignore_stack = snapshot.ignore_stack_for_path(&parent_path, true); + let ignore_stack = snapshot.ignore_stack_for_abs_path(&parent_abs_path, true); ignore_queue_tx .send(UpdateIgnoreStatusJob { - path: parent_path, + abs_path: parent_abs_path, ignore_stack, ignore_queue: ignore_queue_tx.clone(), }) @@ -2355,15 +2370,17 @@ impl BackgroundScanner { async fn update_ignore_status(&self, job: UpdateIgnoreStatusJob, snapshot: &LocalSnapshot) { let mut ignore_stack = job.ignore_stack; - if let Some((ignore, _)) = snapshot.ignores.get(&job.path) { - ignore_stack = ignore_stack.append(job.path.clone(), ignore.clone()); + if let Some((ignore, _)) = snapshot.ignores_by_abs_path.get(&job.abs_path) { + ignore_stack = ignore_stack.append(job.abs_path.clone(), ignore.clone()); } let mut entries_by_id_edits = Vec::new(); let mut entries_by_path_edits = Vec::new(); - for mut entry in snapshot.child_entries(&job.path).cloned() { + let path = job.abs_path.strip_prefix(&snapshot.abs_path).unwrap(); + for mut entry in snapshot.child_entries(path).cloned() { let was_ignored = entry.is_ignored; - entry.is_ignored = ignore_stack.is_path_ignored(&entry.path, entry.is_dir()); + let abs_path = self.abs_path().join(&entry.path); + entry.is_ignored = ignore_stack.is_abs_path_ignored(&abs_path, entry.is_dir()); if entry.is_dir() { let child_ignore_stack = if entry.is_ignored { IgnoreStack::all() @@ -2372,7 +2389,7 @@ impl BackgroundScanner { }; job.ignore_queue .send(UpdateIgnoreStatusJob { - path: entry.path.clone(), + abs_path: abs_path.into(), ignore_stack: child_ignore_stack, ignore_queue: job.ignore_queue.clone(), }) @@ -2393,6 +2410,20 @@ impl BackgroundScanner { snapshot.entries_by_path.edit(entries_by_path_edits, &()); snapshot.entries_by_id.edit(entries_by_id_edits, &()); } + + async fn build_root_ignore_stack(&self) { + // let parent_abs_path = if let Some(path) = self.abs_path().parent() { + // path + // } else { + // return IgnoreStack::none() + // } + + // let mut cur_path = PathBuf::new(); + // for component in self.abs_path().components() { + // // self.snapshot.lock().ignores.insert(parent_path, (ignore, self.scan_id)); + // cur_path.push(compo) + // } + } } fn char_bag_for_path(root_char_bag: CharBag, path: &Path) -> CharBag { @@ -2413,7 +2444,7 @@ struct ScanJob { } struct UpdateIgnoreStatusJob { - path: Arc, + abs_path: Arc, ignore_stack: Arc, ignore_queue: Sender, } @@ -2891,7 +2922,7 @@ mod tests { let mut initial_snapshot = LocalSnapshot { abs_path: root_dir.path().into(), removed_entry_ids: Default::default(), - ignores: Default::default(), + ignores_by_abs_path: Default::default(), next_entry_id: next_entry_id.clone(), snapshot: Snapshot { id: WorktreeId::from_usize(0), @@ -3176,8 +3207,10 @@ mod tests { .collect::>(); assert_eq!(dfs_paths_via_traversal, dfs_paths_via_iter); - for (ignore_parent_path, _) in &self.ignores { - assert!(self.entry_for_path(ignore_parent_path).is_some()); + for (ignore_parent_abs_path, _) in &self.ignores_by_abs_path { + let ignore_parent_path = + ignore_parent_abs_path.strip_prefix(&self.abs_path).unwrap(); + assert!(self.entry_for_path(&ignore_parent_path).is_some()); assert!(self .entry_for_path(ignore_parent_path.join(&*GITIGNORE)) .is_some());