From e06666759a728503fed8b9f291dfea0b5f1eaf16 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Mon, 24 Feb 2025 17:03:52 -0500 Subject: [PATCH] Improve performance of project panel with many git statuses (#25465) Closes #ISSUE Release Notes: - Improved performance of project panel in large git repositories --- crates/project_panel/src/project_panel.rs | 12 ++++----- crates/worktree/src/worktree.rs | 30 +++++++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index bbac6deca2..fd5c230995 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -18,7 +18,7 @@ use file_icons::FileIcons; use git::status::GitSummary; use gpui::{ actions, anchored, deferred, div, impl_actions, point, px, size, uniform_list, Action, - AnyElement, App, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent, Div, + AnyElement, App, ArcCow, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent, Div, DragMoveEvent, Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, Hsla, InteractiveElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior, MouseButton, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, ScrollStrategy, Stateful, @@ -2700,7 +2700,7 @@ impl ProjectPanel { else { continue; }; - let path = Arc::from(Path::new(path_name)); + let path = ArcCow::Borrowed(Path::new(path_name)); let depth = 0; (depth, path) } else if entry.is_file() { @@ -2712,7 +2712,7 @@ impl ProjectPanel { else { continue; }; - let path = Arc::from(Path::new(path_name)); + let path = ArcCow::Borrowed(Path::new(path_name)); let depth = entry.path.ancestors().count() - 1; (depth, path) } else { @@ -2732,11 +2732,11 @@ impl ProjectPanel { .ok() .and_then(|suffix| { let full_path = Path::new(root_folded_entry.file_name()?); - Some(Arc::::from(full_path.join(suffix))) + Some(ArcCow::Owned(Arc::::from(full_path.join(suffix)))) }) }) - .or_else(|| entry.path.file_name().map(Path::new).map(Arc::from)) - .unwrap_or_else(|| entry.path.clone()); + .or_else(|| entry.path.file_name().map(Path::new).map(ArcCow::Borrowed)) + .unwrap_or_else(|| ArcCow::Owned(entry.path.clone())); let depth = path.components().count(); (depth, path) }; diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 52e2ae5554..8dcb4d43a9 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -206,15 +206,19 @@ pub struct RepositoryEntry { pub current_merge_conflicts: TreeSet, } -impl Deref for RepositoryEntry { - type Target = WorkDirectory; - - fn deref(&self) -> &Self::Target { - &self.work_directory - } -} - impl RepositoryEntry { + pub fn relativize(&self, path: &Path) -> Result { + self.work_directory.relativize(path) + } + + pub fn unrelativize(&self, path: &RepoPath) -> Option> { + self.work_directory.unrelativize(path) + } + + pub fn directory_contains(&self, path: impl AsRef) -> bool { + self.work_directory.directory_contains(path) + } + pub fn branch(&self) -> Option<&Branch> { self.current_branch.as_ref() } @@ -2834,7 +2838,7 @@ impl Snapshot { pub fn repository_for_path(&self, path: &Path) -> Option<&RepositoryEntry> { self.repositories .iter() - .filter(|repo| repo.work_directory.directory_contains(path)) + .filter(|repo| repo.directory_contains(path)) .last() } @@ -6023,7 +6027,13 @@ impl<'a> GitTraversal<'a> { }; // Update our state if we changed repositories. - if reset || self.repo_location.as_ref().map(|(prev_repo, _)| prev_repo) != Some(&repo) { + if reset + || self + .repo_location + .as_ref() + .map(|(prev_repo, _)| &prev_repo.work_directory) + != Some(&repo.work_directory) + { self.repo_location = Some((repo, repo.statuses_by_path.cursor::(&()))); }