mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
project panel: Improve performance in large projects (#13202)
In #12980 I've hoisted out creation of HashSet<PathInWorktree> out of render_entry, which made us not create that hash set for each entry in a worktree on each frame. In current nightly, we do it once per call to render() on the whole worktree, which is better. However, we can still reuse the hashed between the frames, if the worktree has not changed. Once we calculate the hashset for a given worktree state, we keep it around for as long as the state is valid for. We calculate the HashSet lazily, as we may not necessarily need it if the project panel is collapsed. In large worktrees, this helps keep the CPU usage of the main thread low-ish. Release Notes: - Improved performance of project panel in large worktrees.
This commit is contained in:
parent
e4ba336971
commit
5dc54863a4
@ -22,6 +22,7 @@ use project::{Entry, EntryKind, Fs, Project, ProjectEntryId, ProjectPath, Worktr
|
||||
use project_panel_settings::{ProjectPanelDockPosition, ProjectPanelSettings};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
cell::OnceCell,
|
||||
collections::HashSet,
|
||||
ffi::OsStr,
|
||||
ops::Range,
|
||||
@ -46,7 +47,7 @@ pub struct ProjectPanel {
|
||||
fs: Arc<dyn Fs>,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
focus_handle: FocusHandle,
|
||||
visible_entries: Vec<(WorktreeId, Vec<Entry>)>,
|
||||
visible_entries: Vec<(WorktreeId, Vec<Entry>, OnceCell<HashSet<Arc<Path>>>)>,
|
||||
last_worktree_root_id: Option<ProjectEntryId>,
|
||||
last_external_paths_drag_over_entry: Option<ProjectEntryId>,
|
||||
expanded_dir_ids: HashMap<WorktreeId, Vec<ProjectEntryId>>,
|
||||
@ -675,7 +676,7 @@ impl ProjectPanel {
|
||||
return;
|
||||
}
|
||||
|
||||
let (worktree_id, worktree_entries) = &self.visible_entries[worktree_ix];
|
||||
let (worktree_id, worktree_entries, _) = &self.visible_entries[worktree_ix];
|
||||
let selection = SelectedEntry {
|
||||
worktree_id: *worktree_id,
|
||||
entry_id: worktree_entries[entry_ix].id,
|
||||
@ -1120,7 +1121,7 @@ impl ProjectPanel {
|
||||
if let Some(selection) = self.selection {
|
||||
let (mut worktree_ix, mut entry_ix, _) =
|
||||
self.index_for_selection(selection).unwrap_or_default();
|
||||
if let Some((_, worktree_entries)) = self.visible_entries.get(worktree_ix) {
|
||||
if let Some((_, worktree_entries, _)) = self.visible_entries.get(worktree_ix) {
|
||||
if entry_ix + 1 < worktree_entries.len() {
|
||||
entry_ix += 1;
|
||||
} else {
|
||||
@ -1129,7 +1130,8 @@ impl ProjectPanel {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((worktree_id, worktree_entries)) = self.visible_entries.get(worktree_ix) {
|
||||
if let Some((worktree_id, worktree_entries, _)) = self.visible_entries.get(worktree_ix)
|
||||
{
|
||||
if let Some(entry) = worktree_entries.get(entry_ix) {
|
||||
let selection = SelectedEntry {
|
||||
worktree_id: *worktree_id,
|
||||
@ -1170,7 +1172,9 @@ impl ProjectPanel {
|
||||
let worktree = self
|
||||
.visible_entries
|
||||
.first()
|
||||
.and_then(|(worktree_id, _)| self.project.read(cx).worktree_for_id(*worktree_id, cx));
|
||||
.and_then(|(worktree_id, _, _)| {
|
||||
self.project.read(cx).worktree_for_id(*worktree_id, cx)
|
||||
});
|
||||
if let Some(worktree) = worktree {
|
||||
let worktree = worktree.read(cx);
|
||||
let worktree_id = worktree.id();
|
||||
@ -1190,10 +1194,9 @@ impl ProjectPanel {
|
||||
}
|
||||
|
||||
fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
|
||||
let worktree = self
|
||||
.visible_entries
|
||||
.last()
|
||||
.and_then(|(worktree_id, _)| self.project.read(cx).worktree_for_id(*worktree_id, cx));
|
||||
let worktree = self.visible_entries.last().and_then(|(worktree_id, _, _)| {
|
||||
self.project.read(cx).worktree_for_id(*worktree_id, cx)
|
||||
});
|
||||
if let Some(worktree) = worktree {
|
||||
let worktree = worktree.read(cx);
|
||||
let worktree_id = worktree.id();
|
||||
@ -1461,7 +1464,7 @@ impl ProjectPanel {
|
||||
fn index_for_selection(&self, selection: SelectedEntry) -> Option<(usize, usize, usize)> {
|
||||
let mut entry_index = 0;
|
||||
let mut visible_entries_index = 0;
|
||||
for (worktree_index, (worktree_id, worktree_entries)) in
|
||||
for (worktree_index, (worktree_id, worktree_entries, _)) in
|
||||
self.visible_entries.iter().enumerate()
|
||||
{
|
||||
if *worktree_id == selection.worktree_id {
|
||||
@ -1623,7 +1626,7 @@ impl ProjectPanel {
|
||||
snapshot.propagate_git_statuses(&mut visible_worktree_entries);
|
||||
project::sort_worktree_entries(&mut visible_worktree_entries);
|
||||
self.visible_entries
|
||||
.push((worktree_id, visible_worktree_entries));
|
||||
.push((worktree_id, visible_worktree_entries, OnceCell::new()));
|
||||
}
|
||||
|
||||
if let Some((worktree_id, entry_id)) = new_selected_entry {
|
||||
@ -1794,7 +1797,7 @@ impl ProjectPanel {
|
||||
mut callback: impl FnMut(ProjectEntryId, EntryDetails, &mut ViewContext<ProjectPanel>),
|
||||
) {
|
||||
let mut ix = 0;
|
||||
for (worktree_id, visible_worktree_entries) in &self.visible_entries {
|
||||
for (worktree_id, visible_worktree_entries, entries_paths) in &self.visible_entries {
|
||||
if ix >= range.end {
|
||||
return;
|
||||
}
|
||||
@ -1823,10 +1826,12 @@ impl ProjectPanel {
|
||||
.unwrap_or(&[]);
|
||||
|
||||
let entry_range = range.start.saturating_sub(ix)..end_ix - ix;
|
||||
let entries = visible_worktree_entries
|
||||
.iter()
|
||||
.map(|e| (e.path.clone()))
|
||||
.collect();
|
||||
let entries = entries_paths.get_or_init(|| {
|
||||
visible_worktree_entries
|
||||
.iter()
|
||||
.map(|e| (e.path.clone()))
|
||||
.collect()
|
||||
});
|
||||
for entry in visible_worktree_entries[entry_range].iter() {
|
||||
let status = git_status_setting.then(|| entry.git_status).flatten();
|
||||
let is_expanded = expanded_entry_ids.binary_search(&entry.id).is_ok();
|
||||
@ -2298,7 +2303,7 @@ impl Render for ProjectPanel {
|
||||
"entries",
|
||||
self.visible_entries
|
||||
.iter()
|
||||
.map(|(_, worktree_entries)| worktree_entries.len())
|
||||
.map(|(_, worktree_entries, _)| worktree_entries.len())
|
||||
.sum(),
|
||||
{
|
||||
|this, range, cx| {
|
||||
|
Loading…
Reference in New Issue
Block a user