sidebar: Fix behavior of "Remove Project" button (#53242)

Danilo Leal created

- Fix an issue where the "remove project" button, available in the
header's ellipsis menu, wouldn't do anything if the sidebar contained
only one project
- Fix another issue where attempting to remove a project when the
sidebar has more than one project wouldn't actually remove it. This is
fixed by cleaning up the project group keys after its been already
removed.

Release Notes:

- N/A

Change summary

crates/workspace/src/multi_workspace.rs | 85 ++++++++++++++++++++++----
1 file changed, 70 insertions(+), 15 deletions(-)

Detailed changes

crates/workspace/src/multi_workspace.rs 🔗

@@ -6,9 +6,7 @@ use gpui::{
     ManagedView, MouseButton, Pixels, Render, Subscription, Task, Tiling, Window, WindowId,
     actions, deferred, px,
 };
-#[cfg(any(test, feature = "test-support"))]
-use project::Project;
-use project::{DirectoryLister, DisableAiSettings, ProjectGroupKey};
+use project::{DirectoryLister, DisableAiSettings, Project, ProjectGroupKey};
 use settings::Settings;
 pub use settings::SidebarSide;
 use std::future::Future;
@@ -468,6 +466,9 @@ impl MultiWorkspace {
     }
 
     pub fn add_project_group_key(&mut self, project_group_key: ProjectGroupKey) {
+        if project_group_key.path_list().paths().is_empty() {
+            return;
+        }
         if self.project_group_keys.contains(&project_group_key) {
             return;
         }
@@ -1040,26 +1041,80 @@ impl MultiWorkspace {
         let Some(index) = self.workspaces.iter().position(|w| w == workspace) else {
             return false;
         };
+
+        let old_key = workspace.read(cx).project_group_key(cx);
+
         if self.workspaces.len() <= 1 {
-            return false;
-        }
+            let has_worktrees = workspace.read(cx).visible_worktrees(cx).next().is_some();
+
+            if !has_worktrees {
+                return false;
+            }
+
+            let old_workspace = workspace.clone();
+            let old_entity_id = old_workspace.entity_id();
 
-        let removed_workspace = self.workspaces.remove(index);
+            let app_state = old_workspace.read(cx).app_state().clone();
+
+            let project = Project::local(
+                app_state.client.clone(),
+                app_state.node_runtime.clone(),
+                app_state.user_store.clone(),
+                app_state.languages.clone(),
+                app_state.fs.clone(),
+                None,
+                project::LocalProjectFlags::default(),
+                cx,
+            );
+
+            let new_workspace = cx.new(|cx| Workspace::new(None, project, app_state, window, cx));
+
+            self.workspaces[0] = new_workspace.clone();
+            self.active_workspace_index = 0;
+
+            Self::subscribe_to_workspace(&new_workspace, window, cx);
+
+            self.sync_sidebar_to_workspace(&new_workspace, cx);
+
+            let weak_self = cx.weak_entity();
 
-        if self.active_workspace_index >= self.workspaces.len() {
-            self.active_workspace_index = self.workspaces.len() - 1;
-        } else if self.active_workspace_index > index {
-            self.active_workspace_index -= 1;
+            new_workspace.update(cx, |workspace, cx| {
+                workspace.set_multi_workspace(weak_self, cx);
+            });
+
+            self.detach_workspace(&old_workspace, cx);
+
+            cx.emit(MultiWorkspaceEvent::WorkspaceRemoved(old_entity_id));
+            cx.emit(MultiWorkspaceEvent::WorkspaceAdded(new_workspace));
+            cx.emit(MultiWorkspaceEvent::ActiveWorkspaceChanged);
+        } else {
+            let removed_workspace = self.workspaces.remove(index);
+
+            if self.active_workspace_index >= self.workspaces.len() {
+                self.active_workspace_index = self.workspaces.len() - 1;
+            } else if self.active_workspace_index > index {
+                self.active_workspace_index -= 1;
+            }
+
+            self.detach_workspace(&removed_workspace, cx);
+
+            cx.emit(MultiWorkspaceEvent::WorkspaceRemoved(
+                removed_workspace.entity_id(),
+            ));
+            cx.emit(MultiWorkspaceEvent::ActiveWorkspaceChanged);
         }
 
-        self.detach_workspace(&removed_workspace, cx);
+        let key_still_in_use = self
+            .workspaces
+            .iter()
+            .any(|ws| ws.read(cx).project_group_key(cx) == old_key);
+
+        if !key_still_in_use {
+            self.project_group_keys.retain(|k| k != &old_key);
+        }
 
         self.serialize(cx);
         self.focus_active_workspace(window, cx);
-        cx.emit(MultiWorkspaceEvent::WorkspaceRemoved(
-            removed_workspace.entity_id(),
-        ));
-        cx.emit(MultiWorkspaceEvent::ActiveWorkspaceChanged);
         cx.notify();
 
         true