@@ -88,6 +88,23 @@ struct State {
// Currently selected leaf entry (see auto-folding for a definition of that) in a file tree
selection: Option<SelectedEntry>,
edit_state: Option<EditState>,
+ unfolded_dir_ids: HashSet<ProjectEntryId>,
+ expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
+}
+
+impl State {
+ fn derive(old: &Self) -> Self {
+ Self {
+ last_worktree_root_id: None,
+ ancestors: Default::default(),
+ visible_entries: Default::default(),
+ max_width_item_index: None,
+ edit_state: old.edit_state.clone(),
+ unfolded_dir_ids: old.unfolded_dir_ids.clone(),
+ selection: old.selection.clone(),
+ expanded_dir_ids: old.expanded_dir_ids.clone(),
+ }
+ }
}
pub struct ProjectPanel {
@@ -100,8 +117,6 @@ pub struct ProjectPanel {
hover_scroll_task: Option<Task<()>>,
folded_directory_drag_target: Option<FoldedDirectoryDragTarget>,
drag_target_entry: Option<DragTarget>,
- expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
- unfolded_dir_ids: HashSet<ProjectEntryId>,
marked_entries: Vec<SelectedEntry>,
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
filename_editor: Entity<Editor>,
@@ -298,7 +313,7 @@ actions!(
]
);
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
struct FoldedAncestors {
current_ancestor_depth: usize,
ancestors: Vec<ProjectEntryId>,
@@ -522,7 +537,7 @@ impl ProjectPanel {
}
}
project::Event::WorktreeRemoved(id) => {
- this.expanded_dir_ids.remove(id);
+ this.state.expanded_dir_ids.remove(id);
this.update_visible_entries(None, cx);
cx.notify();
}
@@ -536,7 +551,7 @@ impl ProjectPanel {
if let Some((worktree, expanded_dir_ids)) = project
.read(cx)
.worktree_for_id(*worktree_id, cx)
- .zip(this.expanded_dir_ids.get_mut(worktree_id))
+ .zip(this.state.expanded_dir_ids.get_mut(worktree_id))
{
let worktree = worktree.read(cx);
@@ -560,7 +575,7 @@ impl ProjectPanel {
if let Err(ix) = expanded_dir_ids.binary_search(&child.id) {
expanded_dir_ids.insert(ix, child.id);
}
- this.unfolded_dir_ids.insert(child.id);
+ this.state.unfolded_dir_ids.insert(child.id);
}
}
this.update_visible_entries(None, cx);
@@ -642,8 +657,7 @@ impl ProjectPanel {
focus_handle,
folded_directory_drag_target: None,
drag_target_entry: None,
- expanded_dir_ids: Default::default(),
- unfolded_dir_ids: Default::default(),
+
marked_entries: Default::default(),
context_menu: None,
filename_editor,
@@ -667,6 +681,8 @@ impl ProjectPanel {
last_worktree_root_id: Default::default(),
visible_entries: Default::default(),
ancestors: Default::default(),
+ expanded_dir_ids: Default::default(),
+ unfolded_dir_ids: Default::default(),
},
update_visible_entries_task: Task::ready(()),
};
@@ -1022,7 +1038,7 @@ impl ProjectPanel {
}
fn is_unfoldable(&self, entry: &Entry, worktree: &Worktree) -> bool {
- if !entry.is_dir() || self.unfolded_dir_ids.contains(&entry.id) {
+ if !entry.is_dir() || self.state.unfolded_dir_ids.contains(&entry.id) {
return false;
}
@@ -1069,12 +1085,13 @@ impl ProjectPanel {
if entry.is_dir() {
let worktree_id = worktree.id();
let entry_id = entry.id;
- let expanded_dir_ids =
- if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
- expanded_dir_ids
- } else {
- return;
- };
+ let expanded_dir_ids = if let Some(expanded_dir_ids) =
+ self.state.expanded_dir_ids.get_mut(&worktree_id)
+ {
+ expanded_dir_ids
+ } else {
+ return;
+ };
match expanded_dir_ids.binary_search(&entry_id) {
Ok(_) => self.select_next(&SelectNext, window, cx),
@@ -1115,7 +1132,7 @@ impl ProjectPanel {
}
let worktree_id = worktree.id();
let expanded_dir_ids =
- if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
+ if let Some(expanded_dir_ids) = self.state.expanded_dir_ids.get_mut(&worktree_id) {
expanded_dir_ids
} else {
return;
@@ -1155,7 +1172,8 @@ impl ProjectPanel {
let multiple_worktrees = self.project.read(cx).worktrees(cx).count() > 1;
let project = self.project.read(cx);
- self.expanded_dir_ids
+ self.state
+ .expanded_dir_ids
.iter_mut()
.for_each(|(worktree_id, expanded_entries)| {
if multiple_worktrees {
@@ -1189,7 +1207,7 @@ impl ProjectPanel {
cx: &mut Context<Self>,
) {
if let Some(worktree_id) = self.project.read(cx).worktree_id_for_entry(entry_id, cx)
- && let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id)
+ && let Some(expanded_dir_ids) = self.state.expanded_dir_ids.get_mut(&worktree_id)
{
self.project.update(cx, |project, cx| {
match expanded_dir_ids.binary_search(&entry_id) {
@@ -1215,7 +1233,7 @@ impl ProjectPanel {
cx: &mut Context<Self>,
) {
if let Some(worktree_id) = self.project.read(cx).worktree_id_for_entry(entry_id, cx)
- && let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id)
+ && let Some(expanded_dir_ids) = self.state.expanded_dir_ids.get_mut(&worktree_id)
{
match expanded_dir_ids.binary_search(&entry_id) {
Ok(_ix) => {
@@ -1240,7 +1258,7 @@ impl ProjectPanel {
self.project.update(cx, |project, cx| {
if let Some((worktree, expanded_dir_ids)) = project
.worktree_for_id(worktree_id, cx)
- .zip(self.expanded_dir_ids.get_mut(&worktree_id))
+ .zip(self.state.expanded_dir_ids.get_mut(&worktree_id))
{
if let Some(task) = project.expand_all_for_entry(worktree_id, entry_id, cx) {
task.detach();
@@ -1276,7 +1294,7 @@ impl ProjectPanel {
self.project.update(cx, |project, cx| {
if let Some((worktree, expanded_dir_ids)) = project
.worktree_for_id(worktree_id, cx)
- .zip(self.expanded_dir_ids.get_mut(&worktree_id))
+ .zip(self.state.expanded_dir_ids.get_mut(&worktree_id))
{
let worktree = worktree.read(cx);
let mut dirs_to_collapse = vec![entry_id];
@@ -1289,7 +1307,7 @@ impl ProjectPanel {
expanded_dir_ids.remove(ix);
}
if auto_fold_enabled {
- self.unfolded_dir_ids.remove(¤t_id);
+ self.state.unfolded_dir_ids.remove(¤t_id);
}
for child in worktree.child_entries(¤t_entry.path) {
if child.is_dir() {
@@ -1706,7 +1724,7 @@ impl ProjectPanel {
.project
.read(cx)
.worktree_for_id(worktree_id, cx)
- .zip(self.expanded_dir_ids.get_mut(&worktree_id))
+ .zip(self.state.expanded_dir_ids.get_mut(&worktree_id))
{
let worktree = worktree.read(cx);
if let Some(mut entry) = worktree.entry_for_id(new_entry_id) {
@@ -2039,7 +2057,7 @@ impl ProjectPanel {
fn unfold_directory(&mut self, _: &UnfoldDirectory, _: &mut Window, cx: &mut Context<Self>) {
if let Some((worktree, entry)) = self.selected_entry(cx) {
- self.unfolded_dir_ids.insert(entry.id);
+ self.state.unfolded_dir_ids.insert(entry.id);
let snapshot = worktree.snapshot();
let mut parent_path = entry.path.parent();
@@ -2051,7 +2069,7 @@ impl ProjectPanel {
break;
}
- self.unfolded_dir_ids.insert(parent_entry.id);
+ self.state.unfolded_dir_ids.insert(parent_entry.id);
parent_path = path.parent();
} else {
break;
@@ -2066,7 +2084,7 @@ impl ProjectPanel {
fn fold_directory(&mut self, _: &FoldDirectory, _: &mut Window, cx: &mut Context<Self>) {
if let Some((worktree, entry)) = self.selected_entry(cx) {
- self.unfolded_dir_ids.remove(&entry.id);
+ self.state.unfolded_dir_ids.remove(&entry.id);
let snapshot = worktree.snapshot();
let mut path = &*entry.path;
@@ -2074,7 +2092,7 @@ impl ProjectPanel {
let mut child_entries_iter = snapshot.child_entries(path);
if let Some(child) = child_entries_iter.next() {
if child_entries_iter.next().is_none() && child.is_dir() {
- self.unfolded_dir_ids.remove(&child.id);
+ self.state.unfolded_dir_ids.remove(&child.id);
path = &*child.path;
} else {
break;
@@ -2990,7 +3008,11 @@ impl ProjectPanel {
fn expand_to_selection(&mut self, cx: &mut Context<Self>) -> Option<()> {
let (worktree, entry) = self.selected_entry(cx)?;
- let expanded_dir_ids = self.expanded_dir_ids.entry(worktree.id()).or_default();
+ let expanded_dir_ids = self
+ .state
+ .expanded_dir_ids
+ .entry(worktree.id())
+ .or_default();
for path in entry.path.ancestors() {
let Some(entry) = worktree.entry_for_path(path) else {
@@ -3042,249 +3064,263 @@ impl ProjectPanel {
let hide_gitignore = settings.hide_gitignore;
let project = self.project.read(cx);
let repo_snapshots = project.git_store().read(cx).repo_snapshots(cx);
- self.state.last_worktree_root_id = project
+
+ let old_ancestors = self.state.ancestors.clone();
+ let mut new_state = State::derive(&self.state);
+ new_state.last_worktree_root_id = project
.visible_worktrees(cx)
.next_back()
.and_then(|worktree| worktree.read(cx).root_entry())
.map(|entry| entry.id);
-
- let old_ancestors = std::mem::take(&mut self.state.ancestors);
- self.state.visible_entries.clear();
let mut max_width_item = None;
- let visible_worktrees: Vec<_> = project.visible_worktrees(cx).collect();
+ let visible_worktrees: Vec<_> = project
+ .visible_worktrees(cx)
+ .map(|worktree| worktree.read(cx).snapshot())
+ .collect();
let hide_root = settings.hide_root && visible_worktrees.len() == 1;
- for worktree in visible_worktrees {
- let worktree_snapshot = worktree.read(cx).snapshot();
- let worktree_id = worktree_snapshot.id();
-
- let expanded_dir_ids = match self.expanded_dir_ids.entry(worktree_id) {
- hash_map::Entry::Occupied(e) => e.into_mut(),
- hash_map::Entry::Vacant(e) => {
- // The first time a worktree's root entry becomes available,
- // mark that root entry as expanded.
- if let Some(entry) = worktree_snapshot.root_entry() {
- e.insert(vec![entry.id]).as_slice()
- } else {
- &[]
- }
- }
- };
-
- let mut new_entry_parent_id = None;
- let mut new_entry_kind = EntryKind::Dir;
- if let Some(edit_state) = &self.state.edit_state
- && edit_state.worktree_id == worktree_id
- && edit_state.is_new_entry()
- {
- new_entry_parent_id = Some(edit_state.entry_id);
- new_entry_kind = if edit_state.is_dir {
- EntryKind::Dir
- } else {
- EntryKind::File
- };
- }
+ self.update_visible_entries_task = cx.spawn(async move |this, cx| {
+ let new_state = cx
+ .background_spawn(async move {
+ for worktree_snapshot in visible_worktrees {
+ let worktree_id = worktree_snapshot.id();
+
+ let expanded_dir_ids = match new_state.expanded_dir_ids.entry(worktree_id) {
+ hash_map::Entry::Occupied(e) => e.into_mut(),
+ hash_map::Entry::Vacant(e) => {
+ // The first time a worktree's root entry becomes available,
+ // mark that root entry as expanded.
+ if let Some(entry) = worktree_snapshot.root_entry() {
+ e.insert(vec![entry.id]).as_slice()
+ } else {
+ &[]
+ }
+ }
+ };
- let mut visible_worktree_entries = Vec::new();
- let mut entry_iter =
- GitTraversal::new(&repo_snapshots, worktree_snapshot.entries(true, 0));
- let mut auto_folded_ancestors = vec![];
- while let Some(entry) = entry_iter.entry() {
- if hide_root && Some(entry.entry) == worktree.read(cx).root_entry() {
- if new_entry_parent_id == Some(entry.id) {
- visible_worktree_entries.push(Self::create_new_git_entry(
- entry.entry,
- entry.git_summary,
- new_entry_kind,
- ));
- new_entry_parent_id = None;
- }
- entry_iter.advance();
- continue;
- }
- if auto_collapse_dirs && entry.kind.is_dir() {
- auto_folded_ancestors.push(entry.id);
- if !self.unfolded_dir_ids.contains(&entry.id)
- && let Some(root_path) = worktree_snapshot.root_entry()
- {
- let mut child_entries = worktree_snapshot.child_entries(&entry.path);
- if let Some(child) = child_entries.next()
- && entry.path != root_path.path
- && child_entries.next().is_none()
- && child.kind.is_dir()
+ let mut new_entry_parent_id = None;
+ let mut new_entry_kind = EntryKind::Dir;
+ if let Some(edit_state) = &new_state.edit_state
+ && edit_state.worktree_id == worktree_id
+ && edit_state.is_new_entry()
{
- entry_iter.advance();
-
- continue;
+ new_entry_parent_id = Some(edit_state.entry_id);
+ new_entry_kind = if edit_state.is_dir {
+ EntryKind::Dir
+ } else {
+ EntryKind::File
+ };
}
- }
- let depth = old_ancestors
- .get(&entry.id)
- .map(|ancestor| ancestor.current_ancestor_depth)
- .unwrap_or_default()
- .min(auto_folded_ancestors.len());
- if let Some(edit_state) = &mut self.state.edit_state
- && edit_state.entry_id == entry.id
- {
- edit_state.depth = depth;
- }
- let mut ancestors = std::mem::take(&mut auto_folded_ancestors);
- if ancestors.len() > 1 {
- ancestors.reverse();
- self.state.ancestors.insert(
- entry.id,
- FoldedAncestors {
- current_ancestor_depth: depth,
- ancestors,
- },
- );
- }
- }
- auto_folded_ancestors.clear();
- if !hide_gitignore || !entry.is_ignored {
- visible_worktree_entries.push(entry.to_owned());
- }
- let precedes_new_entry = if let Some(new_entry_id) = new_entry_parent_id {
- entry.id == new_entry_id || {
- self.state
- .ancestors
- .get(&entry.id)
- .is_some_and(|entries| entries.ancestors.contains(&new_entry_id))
- }
- } else {
- false
- };
- if precedes_new_entry && (!hide_gitignore || !entry.is_ignored) {
- visible_worktree_entries.push(Self::create_new_git_entry(
- entry.entry,
- entry.git_summary,
- new_entry_kind,
- ));
- }
- let worktree_abs_path = worktree.read(cx).abs_path();
- let (depth, chars) =
- if Some(entry.entry) == worktree.read(cx).root_entry() {
- let Some(path_name) = worktree_abs_path.file_name() else {
- continue;
- };
- let depth = 0;
- (depth, path_name.to_string_lossy().chars().count())
- } else if entry.is_file() {
- let Some(path_name) = entry
- .path
- .file_name()
- .with_context(|| format!("Non-root entry has no file name: {entry:?}"))
- .log_err()
- else {
- continue;
- };
- let depth = entry.path.ancestors().count() - 1;
- (depth, path_name.chars().count())
- } else {
- let path =
- self.state
- .ancestors
- .get(&entry.id)
- .and_then(|ancestors| {
- let outermost_ancestor = ancestors.ancestors.last()?;
- let root_folded_entry = worktree
- .read(cx)
- .entry_for_id(*outermost_ancestor)?
- .path
- .as_ref();
- entry.path.strip_prefix(root_folded_entry).ok().and_then(
- |suffix| {
- Some(
- RelPath::new(root_folded_entry.file_name()?)
- .unwrap()
- .join(suffix),
- )
+
+ let mut visible_worktree_entries = Vec::new();
+ let mut entry_iter =
+ GitTraversal::new(&repo_snapshots, worktree_snapshot.entries(true, 0));
+ let mut auto_folded_ancestors = vec![];
+ let worktree_abs_path = worktree_snapshot.abs_path();
+ while let Some(entry) = entry_iter.entry() {
+ if hide_root && Some(entry.entry) == worktree_snapshot.root_entry() {
+ if new_entry_parent_id == Some(entry.id) {
+ visible_worktree_entries.push(Self::create_new_git_entry(
+ entry.entry,
+ entry.git_summary,
+ new_entry_kind,
+ ));
+ new_entry_parent_id = None;
+ }
+ entry_iter.advance();
+ continue;
+ }
+ if auto_collapse_dirs && entry.kind.is_dir() {
+ auto_folded_ancestors.push(entry.id);
+ if !new_state.unfolded_dir_ids.contains(&entry.id)
+ && let Some(root_path) = worktree_snapshot.root_entry()
+ {
+ let mut child_entries =
+ worktree_snapshot.child_entries(&entry.path);
+ if let Some(child) = child_entries.next()
+ && entry.path != root_path.path
+ && child_entries.next().is_none()
+ && child.kind.is_dir()
+ {
+ entry_iter.advance();
+
+ continue;
+ }
+ }
+ let depth = old_ancestors
+ .get(&entry.id)
+ .map(|ancestor| ancestor.current_ancestor_depth)
+ .unwrap_or_default()
+ .min(auto_folded_ancestors.len());
+ if let Some(edit_state) = &mut new_state.edit_state
+ && edit_state.entry_id == entry.id
+ {
+ edit_state.depth = depth;
+ }
+ let mut ancestors = std::mem::take(&mut auto_folded_ancestors);
+ if ancestors.len() > 1 {
+ ancestors.reverse();
+ new_state.ancestors.insert(
+ entry.id,
+ FoldedAncestors {
+ current_ancestor_depth: depth,
+ ancestors,
},
- )
- })
- .or_else(|| {
- entry
- .path
- .file_name()
- .map(|file_name| RelPath::new(file_name).unwrap().into())
- })
- .unwrap_or_else(|| entry.path.clone());
- let depth = path.components().count();
- (depth, path.as_str().chars().count())
- };
- let width_estimate =
- item_width_estimate(depth, chars, entry.canonical_path.is_some());
-
- match max_width_item.as_mut() {
- Some((id, worktree_id, width)) => {
- if *width < width_estimate {
- *id = entry.id;
- *worktree_id = worktree.read(cx).id();
- *width = width_estimate;
- }
- }
- None => {
- max_width_item = Some((entry.id, worktree.read(cx).id(), width_estimate))
- }
- }
+ );
+ }
+ }
+ auto_folded_ancestors.clear();
+ if !hide_gitignore || !entry.is_ignored {
+ visible_worktree_entries.push(entry.to_owned());
+ }
+ let precedes_new_entry = if let Some(new_entry_id) = new_entry_parent_id
+ {
+ entry.id == new_entry_id || {
+ new_state.ancestors.get(&entry.id).is_some_and(|entries| {
+ entries.ancestors.contains(&new_entry_id)
+ })
+ }
+ } else {
+ false
+ };
+ if precedes_new_entry && (!hide_gitignore || !entry.is_ignored) {
+ visible_worktree_entries.push(Self::create_new_git_entry(
+ entry.entry,
+ entry.git_summary,
+ new_entry_kind,
+ ));
+ }
- if expanded_dir_ids.binary_search(&entry.id).is_err()
- && entry_iter.advance_to_sibling()
- {
- continue;
- }
- entry_iter.advance();
- }
+ let (depth, chars) = if Some(entry.entry)
+ == worktree_snapshot.root_entry()
+ {
+ let Some(path_name) = worktree_abs_path.file_name() else {
+ continue;
+ };
+ let depth = 0;
+ (depth, path_name.to_string_lossy().chars().count())
+ } else if entry.is_file() {
+ let Some(path_name) = entry
+ .path
+ .file_name()
+ .with_context(|| {
+ format!("Non-root entry has no file name: {entry:?}")
+ })
+ .log_err()
+ else {
+ continue;
+ };
+ let depth = entry.path.ancestors().count() - 1;
+ (depth, path_name.chars().count())
+ } else {
+ let path = new_state
+ .ancestors
+ .get(&entry.id)
+ .and_then(|ancestors| {
+ let outermost_ancestor = ancestors.ancestors.last()?;
+ let root_folded_entry = worktree_snapshot
+ .entry_for_id(*outermost_ancestor)?
+ .path
+ .as_ref();
+ entry.path.strip_prefix(root_folded_entry).ok().and_then(
+ |suffix| {
+ Some(
+ RelPath::new(root_folded_entry.file_name()?)
+ .unwrap()
+ .join(suffix),
+ )
+ },
+ )
+ })
+ .or_else(|| {
+ entry.path.file_name().map(|file_name| {
+ RelPath::new(file_name).unwrap().into()
+ })
+ })
+ .unwrap_or_else(|| entry.path.clone());
+ let depth = path.components().count();
+ (depth, path.as_str().chars().count())
+ };
+ let width_estimate =
+ item_width_estimate(depth, chars, entry.canonical_path.is_some());
+
+ match max_width_item.as_mut() {
+ Some((id, worktree_id, width)) => {
+ if *width < width_estimate {
+ *id = entry.id;
+ *worktree_id = worktree_snapshot.id();
+ *width = width_estimate;
+ }
+ }
+ None => {
+ max_width_item =
+ Some((entry.id, worktree_snapshot.id(), width_estimate))
+ }
+ }
- project::sort_worktree_entries(&mut visible_worktree_entries);
+ if expanded_dir_ids.binary_search(&entry.id).is_err()
+ && entry_iter.advance_to_sibling()
+ {
+ continue;
+ }
+ entry_iter.advance();
+ }
- self.state.visible_entries.push(VisibleEntriesForWorktree {
- worktree_id,
- entries: visible_worktree_entries,
- index: OnceCell::new(),
- })
- }
+ project::sort_worktree_entries(&mut visible_worktree_entries);
- if let Some((project_entry_id, worktree_id, _)) = max_width_item {
- let mut visited_worktrees_length = 0;
- let index = self
- .state
- .visible_entries
- .iter()
- .find_map(|visible_entries| {
- if worktree_id == visible_entries.worktree_id {
- visible_entries
- .entries
+ new_state.visible_entries.push(VisibleEntriesForWorktree {
+ worktree_id,
+ entries: visible_worktree_entries,
+ index: OnceCell::new(),
+ })
+ }
+ if let Some((project_entry_id, worktree_id, _)) = max_width_item {
+ let mut visited_worktrees_length = 0;
+ let index = new_state
+ .visible_entries
.iter()
- .position(|entry| entry.id == project_entry_id)
- } else {
- visited_worktrees_length += visible_entries.entries.len();
- None
+ .find_map(|visible_entries| {
+ if worktree_id == visible_entries.worktree_id {
+ visible_entries
+ .entries
+ .iter()
+ .position(|entry| entry.id == project_entry_id)
+ } else {
+ visited_worktrees_length += visible_entries.entries.len();
+ None
+ }
+ });
+ if let Some(index) = index {
+ new_state.max_width_item_index = Some(visited_worktrees_length + index);
+ }
}
- });
- if let Some(index) = index {
- self.state.max_width_item_index = Some(visited_worktrees_length + index);
- }
- }
- if let Some((worktree_id, entry_id)) = new_selected_entry {
- self.state.selection = Some(SelectedEntry {
- worktree_id,
- entry_id,
- });
- }
- let elapsed = now.elapsed();
- if self.last_reported_update.elapsed() > Duration::from_secs(3600) {
- telemetry::event!(
- "Project Panel Updated",
- elapsed_ms = elapsed.as_millis() as u64,
- worktree_entries = self
- .state
- .visible_entries
- .iter()
- .map(|worktree| worktree.entries.len())
- .sum::<usize>(),
- )
- }
+ if let Some((worktree_id, entry_id)) = new_selected_entry {
+ new_state.selection = Some(SelectedEntry {
+ worktree_id,
+ entry_id,
+ });
+ }
+ new_state
+ })
+ .await;
+ this.update(cx, |this, _| {
+ this.state = new_state;
+ let elapsed = now.elapsed();
+ if this.last_reported_update.elapsed() > Duration::from_secs(3600) {
+ telemetry::event!(
+ "Project Panel Updated",
+ elapsed_ms = elapsed.as_millis() as u64,
+ worktree_entries = this
+ .state
+ .visible_entries
+ .iter()
+ .map(|worktree| worktree.entries.len())
+ .sum::<usize>(),
+ )
+ }
+ })
+ .ok();
+ });
}
fn expand_entry(
@@ -3296,7 +3332,7 @@ impl ProjectPanel {
self.project.update(cx, |project, cx| {
if let Some((worktree, expanded_dir_ids)) = project
.worktree_for_id(worktree_id, cx)
- .zip(self.expanded_dir_ids.get_mut(&worktree_id))
+ .zip(self.state.expanded_dir_ids.get_mut(&worktree_id))
{
project.expand_entry(worktree_id, entry_id, cx);
let worktree = worktree.read(cx);
@@ -4308,6 +4344,7 @@ impl ProjectPanel {
if !kind.is_dir()
|| this
+ .state
.expanded_dir_ids
.get(&details.worktree_id)
.is_some_and(|ids| ids.binary_search(&entry_id).is_ok())
@@ -4765,6 +4802,7 @@ impl ProjectPanel {
};
let expanded_entry_ids = self
+ .state
.expanded_dir_ids
.get(&worktree_id)
.map(Vec::as_slice)
@@ -4921,6 +4959,7 @@ impl ProjectPanel {
loop {
let is_expanded_dir = entry.is_dir()
&& self
+ .state
.expanded_dir_ids
.get(&worktree.id())
.map(|ids| ids.binary_search(&entry.id).is_ok())