@@ -1147,8 +1147,32 @@ 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 project = self.project.read(cx);
+
self.expanded_dir_ids
- .retain(|_, expanded_entries| expanded_entries.is_empty());
+ .iter_mut()
+ .for_each(|(worktree_id, expanded_entries)| {
+ if multiple_worktrees {
+ *expanded_entries = Default::default();
+ return;
+ }
+
+ let root_entry_id = project
+ .worktree_for_id(*worktree_id, cx)
+ .map(|worktree| worktree.read(cx).snapshot())
+ .and_then(|worktree_snapshot| {
+ worktree_snapshot.root_entry().map(|entry| entry.id)
+ });
+
+ match root_entry_id {
+ Some(id) => {
+ expanded_entries.retain(|entry_id| entry_id == &id);
+ }
+ None => *expanded_entries = Default::default(),
+ };
+ });
+
self.update_visible_entries(None, cx);
cx.notify();
}
@@ -2747,6 +2747,111 @@ async fn test_collapse_all_entries(cx: &mut gpui::TestAppContext) {
);
}
+#[gpui::test]
+async fn test_collapse_all_entries_multiple_worktrees(cx: &mut gpui::TestAppContext) {
+ init_test_with_editor(cx);
+
+ let fs = FakeFs::new(cx.executor());
+ let worktree_content = json!({
+ "dir_1": {
+ "file_1.py": "# File contents",
+ },
+ "dir_2": {
+ "file_1.py": "# File contents",
+ }
+ });
+
+ fs.insert_tree("/project_root_1", worktree_content.clone())
+ .await;
+ fs.insert_tree("/project_root_2", worktree_content).await;
+
+ let project = Project::test(
+ fs.clone(),
+ ["/project_root_1".as_ref(), "/project_root_2".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();
+
+ 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),
+ &["> project_root_1", "> project_root_2",]
+ );
+}
+
+#[gpui::test]
+async fn test_collapse_all_entries_with_collapsed_root(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_b.py": "# File contents",
+ "file_c.py": "# File contents",
+ },
+ "file_1.py": "# File contents",
+ "file_2.py": "# File contents",
+ "file_3.py": "# File contents",
+ },
+ "dir_2": {
+ "file_1.py": "# File contents",
+ "file_2.py": "# File contents",
+ "file_3.py": "# File contents",
+ }
+ }),
+ )
+ .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();
+
+ // Open project_root/dir_1 to ensure that a nested directory is expanded
+ toggle_expand_dir(&panel, "project_root/dir_1", cx);
+ cx.executor().run_until_parked();
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..10, cx),
+ &[
+ "v project_root",
+ " v dir_1 <== selected",
+ " > nested_dir",
+ " file_1.py",
+ " file_2.py",
+ " file_3.py",
+ " > dir_2",
+ ]
+ );
+
+ // Close root directory
+ toggle_expand_dir(&panel, "project_root", cx);
+ cx.executor().run_until_parked();
+ assert_eq!(
+ visible_entries_as_strings(&panel, 0..10, cx),
+ &["> project_root <== selected"]
+ );
+
+ // Run collapse_all_entries and make sure root is not expanded
+ 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),
+ &["> project_root <== selected"]
+ );
+}
+
#[gpui::test]
async fn test_new_file_move(cx: &mut gpui::TestAppContext) {
init_test(cx);