WIP

Max Brunsfeld created

Change summary

crates/sidebar/src/sidebar.rs                 | 69 ++++++++++++--------
crates/workspace/src/multi_workspace.rs       | 25 ++++++-
crates/workspace/src/multi_workspace_tests.rs |  6 
crates/workspace/src/workspace.rs             |  2 
4 files changed, 67 insertions(+), 35 deletions(-)

Detailed changes

crates/sidebar/src/sidebar.rs 🔗

@@ -3727,35 +3727,50 @@ pub fn dump_workspace_info(
     let mut output = String::new();
     let this_entity = cx.entity();
 
-    let multi_workspace = workspace.multi_workspace().and_then(|weak| weak.upgrade());
-    let workspaces: Vec<gpui::Entity<Workspace>> = match &multi_workspace {
-        Some(mw) => mw.read(cx).workspaces().collect::<Vec<_>>(),
-        None => vec![this_entity.clone()],
-    };
-    let active_workspace = multi_workspace
-        .as_ref()
-        .map(|mw| mw.read(cx).active_workspace());
-
-    writeln!(output, "MultiWorkspace: {} workspace(s)", workspaces.len()).ok();
-    writeln!(output).ok();
+    let multi_workspace = workspace
+        .multi_workspace()
+        .and_then(|weak| weak.upgrade())
+        .unwrap();
+
+    writeln!(
+        output,
+        "MultiWorkspace: {} project group(s)",
+        multi_workspace.read(cx).project_groups().len()
+    )
+    .ok();
+
+    let active_workspace = multi_workspace.read(cx).workspace();
+
+    // The action handler is already inside an update on `this_entity`,
+    // so we must avoid a nested read/update on that same entity.
+    if *active_workspace == this_entity {
+        dump_single_workspace(workspace, &mut output, cx);
+    } else {
+        active_workspace.read_with(cx, |ws, cx| {
+            dump_single_workspace(ws, &mut output, cx);
+        });
+    }
 
-    for (index, ws) in workspaces.iter().enumerate() {
-        let is_active = active_workspace.as_ref() == Some(ws);
-        writeln!(
-            output,
-            "--- Workspace {index}{} ---",
-            if is_active { " (active)" } else { "" }
-        )
-        .ok();
+    for project_group in multi_workspace.read(cx).project_groups() {
+        writeln!(output, "Project Group: {:?}", project_group.key.path_list()).ok();
+        for group_workspace in &project_group.workspaces {
+            let is_active = active_workspace == group_workspace;
+            writeln!(
+                output,
+                "  Workspace{}: ",
+                if is_active { " (active)" } else { "" }
+            )
+            .ok();
 
-        // The action handler is already inside an update on `this_entity`,
-        // so we must avoid a nested read/update on that same entity.
-        if *ws == this_entity {
-            dump_single_workspace(workspace, &mut output, cx);
-        } else {
-            ws.read_with(cx, |ws, cx| {
-                dump_single_workspace(ws, &mut output, cx);
-            });
+            // The action handler is already inside an update on `this_entity`,
+            // so we must avoid a nested read/update on that same entity.
+            if *group_workspace == this_entity {
+                dump_single_workspace(workspace, &mut output, cx);
+            } else {
+                group_workspace.read_with(cx, |ws, cx| {
+                    dump_single_workspace(ws, &mut output, cx);
+                });
+            }
         }
     }
 

crates/workspace/src/multi_workspace.rs 🔗

@@ -582,7 +582,7 @@ impl MultiWorkspace {
     pub fn replace(
         &mut self,
         workspace: Entity<Workspace>,
-        window: &Window,
+        window: &mut Window,
         cx: &mut Context<Self>,
     ) {
         if !self.multi_workspace_enabled(cx) {
@@ -602,10 +602,24 @@ impl MultiWorkspace {
             return;
         }
 
-        let old_workspace = std::mem::replace(&mut self.active_workspace, workspace.clone());
-
-        self.detach_workspace(&old_workspace, cx);
+        let key = workspace.read(cx).project_group_key(cx);
+        if let Some(group) = self.project_groups.iter_mut().find(|g| g.key == key) {
+            group.workspaces.push(workspace.clone());
+        } else {
+            let active_group_ix = self
+                .project_groups
+                .iter()
+                .position(|group| group.workspaces.contains(&self.active_workspace));
+            if let Some(ix) = active_group_ix {
+                self.remove_group_at_index(ix, window, cx);
+            }
+            self.project_groups.push(ProjectGroup {
+                key,
+                workspaces: vec![workspace.clone()],
+            });
+        };
 
+        self.active_workspace = workspace.clone();
         Self::subscribe_to_workspace(&workspace, window, cx);
         self.sync_sidebar_to_workspace(&workspace, cx);
 
@@ -617,6 +631,9 @@ impl MultiWorkspace {
     }
 
     fn set_single_workspace(&mut self, workspace: Entity<Workspace>, cx: &mut Context<Self>) {
+        self.project_groups.clear();
+        self.project_groups
+            .push(ProjectGroup::from_workspace(workspace.clone(), cx));
         self.active_workspace = workspace;
         cx.emit(MultiWorkspaceEvent::ActiveWorkspaceChanged);
         cx.notify();

crates/workspace/src/multi_workspace_tests.rs 🔗

@@ -108,7 +108,7 @@ async fn test_replace(cx: &mut TestAppContext) {
     // Replace the only workspace (single-workspace case).
     let workspace_b = multi_workspace.update_in(cx, |mw, window, cx| {
         let workspace = cx.new(|cx| Workspace::test_new(project_b.clone(), window, cx));
-        mw.replace(workspace.clone(), &*window, cx);
+        mw.replace(workspace.clone(), window, cx);
         workspace
     });
 
@@ -148,7 +148,7 @@ async fn test_replace(cx: &mut TestAppContext) {
 
     let workspace_d = multi_workspace.update_in(cx, |mw, window, cx| {
         let workspace = cx.new(|cx| Workspace::test_new(project_d.clone(), window, cx));
-        mw.replace(workspace.clone(), &*window, cx);
+        mw.replace(workspace.clone(), window, cx);
         workspace
     });
 
@@ -179,7 +179,7 @@ async fn test_replace(cx: &mut TestAppContext) {
 
     // Replace with workspace_b which is already in the list — should just switch.
     multi_workspace.update_in(cx, |mw, window, cx| {
-        mw.replace(workspace_b.clone(), &*window, cx);
+        mw.replace(workspace_b.clone(), window, cx);
     });
 
     multi_workspace.read_with(cx, |mw, _cx| {

crates/workspace/src/workspace.rs 🔗

@@ -1923,7 +1923,7 @@ impl Workspace {
                         });
                         match open_mode {
                             OpenMode::Replace => {
-                                multi_workspace.replace(workspace.clone(), &*window, cx);
+                                multi_workspace.replace(workspace.clone(), window, cx);
                             }
                             OpenMode::Activate => {
                                 multi_workspace.activate(workspace.clone(), window, cx);