From 12091a9f2cad81c8a9e1707123eb6fbe48b5a183 Mon Sep 17 00:00:00 2001 From: Smit Barmase Date: Thu, 5 Feb 2026 11:43:08 +0530 Subject: [PATCH] project_panel: Fix collapse_all_entries collapsing single worktree root completely (#48443) For: https://github.com/zed-industries/zed/pull/47328 Release Notes: - Fixed an issue where selecting "Collapse All" on the root directory or triggering the collapse all action would sometimes collapse the entire root instead of keeping it expanded when there's a single worktree. --- crates/project_panel/src/project_panel.rs | 2 +- .../project_panel/src/project_panel_tests.rs | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 3ce53b8418df4d7d94be494d37a7c03898d0cf27..9657f13a62491ace717880660338b77db527f540 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1375,7 +1375,7 @@ impl ProjectPanel { ) { // By keeping entries for fully collapsed worktrees, we avoid expanding them within update_visible_entries // (which is it's default behavior when there's no entry for a worktree in expanded_dir_ids). - let multiple_worktrees = self.project.read(cx).worktrees(cx).count() > 1; + let multiple_worktrees = self.project.read(cx).visible_worktrees(cx).count() > 1; let project = self.project.read(cx); self.state diff --git a/crates/project_panel/src/project_panel_tests.rs b/crates/project_panel/src/project_panel_tests.rs index 5e2c189ca8048f05371fa759961990ae97878d7f..9dd1a025d697bef63b8a2f7c24a5aec021a44cb5 100644 --- a/crates/project_panel/src/project_panel_tests.rs +++ b/crates/project_panel/src/project_panel_tests.rs @@ -3561,6 +3561,68 @@ async fn test_collapse_all_entries_with_collapsed_root(cx: &mut gpui::TestAppCon ); } +#[gpui::test] +async fn test_collapse_all_entries_with_invisible_worktree(cx: &mut gpui::TestAppContext) { + init_test_with_editor(cx); + + let fs = FakeFs::new(cx.executor()); + fs.insert_tree( + "/project_root", + json!({ + "dir_1": { + "nested_dir": { + "file_a.py": "# File contents", + }, + "file_1.py": "# File contents", + }, + "dir_2": { + "file_1.py": "# File contents", + } + }), + ) + .await; + fs.insert_tree( + "/external", + json!({ + "external_file.py": "# External file", + }), + ) + .await; + + let project = Project::test(fs.clone(), ["/project_root".as_ref()], cx).await; + let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); + let cx = &mut VisualTestContext::from_window(*workspace, cx); + let panel = workspace.update(cx, ProjectPanel::new).unwrap(); + cx.run_until_parked(); + + let (_invisible_worktree, _) = project + .update(cx, |project, cx| { + project.find_or_create_worktree("/external/external_file.py", false, cx) + }) + .await + .unwrap(); + cx.run_until_parked(); + + assert_eq!( + visible_entries_as_strings(&panel, 0..10, cx), + &["v project_root", " > dir_1", " > dir_2",], + "invisible worktree should not appear in project panel" + ); + + toggle_expand_dir(&panel, "project_root/dir_1", cx); + cx.executor().run_until_parked(); + + panel.update_in(cx, |panel, window, cx| { + panel.collapse_all_entries(&CollapseAllEntries, window, cx) + }); + cx.executor().run_until_parked(); + assert_eq!( + visible_entries_as_strings(&panel, 0..10, cx), + &["v project_root", " > dir_1 <== selected", " > dir_2",], + "with single visible worktree, root should stay expanded even if invisible worktrees exist" + ); +} + #[gpui::test] async fn test_new_file_move(cx: &mut gpui::TestAppContext) { init_test(cx);