From 7ae642b9b8746252b2b1cbb42d75c096fea295df Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 19 May 2023 14:23:35 -0700 Subject: [PATCH] Avoid storing removed_entry_ids on the LocalSnapshot --- crates/project/src/worktree.rs | 239 ++++++++++++++++++--------------- 1 file changed, 134 insertions(+), 105 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 24e8a004824c6fec4c99ddad5333de2fa8d71150..b7cb82f628f8df7a02994c8128652946229f6a75 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -227,7 +227,7 @@ impl RepositoryEntry { work_directory_id: self.work_directory_id().to_proto(), branch: self.branch.as_ref().map(|str| str.to_string()), removed_repo_paths: removed_statuses, - updated_statuses: updated_statuses, + updated_statuses, } } } @@ -307,12 +307,22 @@ impl<'a> From for WorkDirectoryEntry { #[derive(Debug, Clone)] pub struct LocalSnapshot { - ignores_by_parent_abs_path: HashMap, (Arc, bool)>, // (gitignore, needs_update) - // The ProjectEntryId corresponds to the entry for the .git dir - // work_directory_id + snapshot: Snapshot, + /// All of the gitignore files in the worktree, indexed by their relative path. + /// The boolean indicates whether the gitignore needs to be updated. + ignores_by_parent_abs_path: HashMap, (Arc, bool)>, + /// All of the git repositories in the worktree, indexed by the project entry + /// id of their parent directory. git_repositories: TreeMap, +} + +pub struct LocalMutableSnapshot { + snapshot: LocalSnapshot, + /// The ids of all of the entries that were removed from the snapshot + /// as part of the current update. These entry ids may be re-used + /// if the same inode is discovered at a new path, or if the given + /// path is re-created after being deleted. removed_entry_ids: HashMap, - snapshot: Snapshot, } #[derive(Debug, Clone)] @@ -346,6 +356,20 @@ impl DerefMut for LocalSnapshot { } } +impl Deref for LocalMutableSnapshot { + type Target = LocalSnapshot; + + fn deref(&self) -> &Self::Target { + &self.snapshot + } +} + +impl DerefMut for LocalMutableSnapshot { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.snapshot + } +} + enum ScanState { Started, Updated { @@ -396,7 +420,6 @@ impl Worktree { let mut snapshot = LocalSnapshot { ignores_by_parent_abs_path: Default::default(), - removed_entry_ids: Default::default(), git_repositories: Default::default(), snapshot: Snapshot { id: WorktreeId::from_usize(cx.model_id()), @@ -1208,7 +1231,6 @@ impl LocalWorktree { let mut share_tx = Some(share_tx); let mut prev_snapshot = LocalSnapshot { ignores_by_parent_abs_path: Default::default(), - removed_entry_ids: Default::default(), git_repositories: Default::default(), snapshot: Snapshot { id: WorktreeId(worktree_id as usize), @@ -1910,8 +1932,6 @@ impl LocalSnapshot { } } - self.reuse_entry_id(&mut entry); - if entry.kind == EntryKind::PendingDir { if let Some(existing_entry) = self.entries_by_path.get(&PathKey(entry.path.clone()), &()) @@ -1940,60 +1960,6 @@ impl LocalSnapshot { entry } - fn populate_dir( - &mut self, - parent_path: Arc, - entries: impl IntoIterator, - ignore: Option>, - fs: &dyn Fs, - ) { - let mut parent_entry = if let Some(parent_entry) = - self.entries_by_path.get(&PathKey(parent_path.clone()), &()) - { - parent_entry.clone() - } else { - log::warn!( - "populating a directory {:?} that has been removed", - parent_path - ); - return; - }; - - match parent_entry.kind { - EntryKind::PendingDir => { - parent_entry.kind = EntryKind::Dir; - } - EntryKind::Dir => {} - _ => return, - } - - if let Some(ignore) = ignore { - self.ignores_by_parent_abs_path - .insert(self.abs_path.join(&parent_path).into(), (ignore, false)); - } - - if parent_path.file_name() == Some(&DOT_GIT) { - self.build_repo(parent_path, fs); - } - - let mut entries_by_path_edits = vec![Edit::Insert(parent_entry)]; - let mut entries_by_id_edits = Vec::new(); - - for mut entry in entries { - self.reuse_entry_id(&mut entry); - entries_by_id_edits.push(Edit::Insert(PathEntry { - id: entry.id, - path: entry.path.clone(), - is_ignored: entry.is_ignored, - scan_id: self.scan_id, - })); - entries_by_path_edits.push(Edit::Insert(entry)); - } - - self.entries_by_path.edit(entries_by_path_edits, &()); - self.entries_by_id.edit(entries_by_id_edits, &()); - } - fn build_repo(&mut self, parent_path: Arc, fs: &dyn Fs) -> Option<()> { let abs_path = self.abs_path.join(&parent_path); let work_dir: Arc = parent_path.parent().unwrap().into(); @@ -2041,6 +2007,46 @@ impl LocalSnapshot { Some(()) } + + fn ancestor_inodes_for_path(&self, path: &Path) -> TreeSet { + let mut inodes = TreeSet::default(); + for ancestor in path.ancestors().skip(1) { + if let Some(entry) = self.entry_for_path(ancestor) { + inodes.insert(entry.inode); + } + } + inodes + } + + fn ignore_stack_for_abs_path(&self, abs_path: &Path, is_dir: bool) -> Arc { + let mut new_ignores = Vec::new(); + for ancestor in abs_path.ancestors().skip(1) { + if let Some((ignore, _)) = self.ignores_by_parent_abs_path.get(ancestor) { + new_ignores.push((ancestor, Some(ignore.clone()))); + } else { + new_ignores.push((ancestor, None)); + } + } + + let mut ignore_stack = IgnoreStack::none(); + 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(parent_abs_path.into(), ignore); + } + } + + if ignore_stack.is_abs_path_ignored(abs_path, is_dir) { + ignore_stack = IgnoreStack::all(); + } + + ignore_stack + } +} + +impl LocalMutableSnapshot { fn reuse_entry_id(&mut self, entry: &mut Entry) { if let Some(removed_entry_id) = self.removed_entry_ids.remove(&entry.inode) { entry.id = removed_entry_id; @@ -2049,6 +2055,66 @@ impl LocalSnapshot { } } + fn insert_entry(&mut self, mut entry: Entry, fs: &dyn Fs) -> Entry { + self.reuse_entry_id(&mut entry); + self.snapshot.insert_entry(entry, fs) + } + + fn populate_dir( + &mut self, + parent_path: Arc, + entries: impl IntoIterator, + ignore: Option>, + fs: &dyn Fs, + ) { + let mut parent_entry = if let Some(parent_entry) = + self.entries_by_path.get(&PathKey(parent_path.clone()), &()) + { + parent_entry.clone() + } else { + log::warn!( + "populating a directory {:?} that has been removed", + parent_path + ); + return; + }; + + match parent_entry.kind { + EntryKind::PendingDir => { + parent_entry.kind = EntryKind::Dir; + } + EntryKind::Dir => {} + _ => return, + } + + if let Some(ignore) = ignore { + let abs_parent_path = self.abs_path.join(&parent_path).into(); + self.ignores_by_parent_abs_path + .insert(abs_parent_path, (ignore, false)); + } + + if parent_path.file_name() == Some(&DOT_GIT) { + self.build_repo(parent_path, fs); + } + + let mut entries_by_path_edits = vec![Edit::Insert(parent_entry)]; + let mut entries_by_id_edits = Vec::new(); + + for mut entry in entries { + self.reuse_entry_id(&mut entry); + entries_by_id_edits.push(Edit::Insert(PathEntry { + id: entry.id, + path: entry.path.clone(), + is_ignored: entry.is_ignored, + scan_id: self.scan_id, + })); + entries_by_path_edits.push(Edit::Insert(entry)); + } + + self.entries_by_path.edit(entries_by_path_edits, &()); + self.entries_by_id.edit(entries_by_id_edits, &()); + } + fn remove_path(&mut self, path: &Path) { let mut new_entries; let removed_entries; @@ -2081,43 +2147,6 @@ impl LocalSnapshot { } } } - - fn ancestor_inodes_for_path(&self, path: &Path) -> TreeSet { - let mut inodes = TreeSet::default(); - for ancestor in path.ancestors().skip(1) { - if let Some(entry) = self.entry_for_path(ancestor) { - inodes.insert(entry.inode); - } - } - inodes - } - - fn ignore_stack_for_abs_path(&self, abs_path: &Path, is_dir: bool) -> Arc { - let mut new_ignores = Vec::new(); - for ancestor in abs_path.ancestors().skip(1) { - if let Some((ignore, _)) = self.ignores_by_parent_abs_path.get(ancestor) { - new_ignores.push((ancestor, Some(ignore.clone()))); - } else { - new_ignores.push((ancestor, None)); - } - } - - let mut ignore_stack = IgnoreStack::none(); - 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(parent_abs_path.into(), ignore); - } - } - - if ignore_stack.is_abs_path_ignored(abs_path, is_dir) { - ignore_stack = IgnoreStack::all(); - } - - ignore_stack - } } async fn build_gitignore(abs_path: &Path, fs: &dyn Fs) -> Result { @@ -2562,7 +2591,7 @@ impl<'a> sum_tree::Dimension<'a, EntrySummary> for PathKey { } struct BackgroundScanner { - snapshot: Mutex, + snapshot: Mutex, fs: Arc, status_updates_tx: UnboundedSender, executor: Arc, @@ -2596,7 +2625,10 @@ impl BackgroundScanner { snapshot: snapshot.snapshot.clone(), event_paths: Default::default(), }), - snapshot: Mutex::new(snapshot), + snapshot: Mutex::new(LocalMutableSnapshot { + snapshot, + removed_entry_ids: Default::default(), + }), finished_initial_scan: false, } } @@ -2750,10 +2782,7 @@ impl BackgroundScanner { .is_some() }); snapshot.snapshot.repository_entries = git_repository_entries; - - snapshot.removed_entry_ids.clear(); snapshot.completed_scan_id = snapshot.scan_id; - drop(snapshot); self.send_status_update(false, None);