Add "move to new window" action

cameron created

Change summary

crates/sidebar/src/sidebar.rs           | 34 +++++++++++++
crates/workspace/src/multi_workspace.rs | 63 +++++++++++++++++++++++++++
crates/zed_actions/src/lib.rs           |  2 
3 files changed, 97 insertions(+), 2 deletions(-)

Detailed changes

crates/sidebar/src/sidebar.rs 🔗

@@ -1477,7 +1477,7 @@ impl Sidebar {
 
                     let workspace_for_add = workspace.clone();
                     let multi_workspace_for_add = multi_workspace.clone();
-                    menu.separator().entry(
+                    let menu = menu.separator().entry(
                         "Add Folder to Project",
                         Some(Box::new(AddFolderToProject)),
                         move |window, cx| {
@@ -1490,7 +1490,37 @@ impl Sidebar {
                                 workspace.add_folder_to_project(&AddFolderToProject, window, cx);
                             });
                         },
-                    )
+                    );
+
+                    let workspace_count = multi_workspace
+                        .upgrade()
+                        .map_or(0, |mw| mw.read(cx).workspaces().len());
+                    if workspace_count > 1 {
+                        let workspace_for_move = workspace.clone();
+                        let multi_workspace_for_move = multi_workspace.clone();
+                        menu.entry(
+                            "Move to New Window",
+                            Some(Box::new(
+                                zed_actions::agents_sidebar::MoveWorkspaceToNewWindow,
+                            )),
+                            move |window, cx| {
+                                if let Some(mw) = multi_workspace_for_move.upgrade() {
+                                    mw.update(cx, |multi_workspace, cx| {
+                                        if let Some(index) = multi_workspace
+                                            .workspaces()
+                                            .iter()
+                                            .position(|w| *w == workspace_for_move)
+                                        {
+                                            multi_workspace
+                                                .move_workspace_to_new_window(index, window, cx);
+                                        }
+                                    });
+                                }
+                            },
+                        )
+                    } else {
+                        menu
+                    }
                 });
 
                 let this = this.clone();

crates/workspace/src/multi_workspace.rs 🔗

@@ -11,8 +11,10 @@ use project::Project;
 use settings::Settings;
 use std::future::Future;
 use std::path::PathBuf;
+use std::sync::Arc;
 use ui::prelude::*;
 use util::ResultExt;
+use zed_actions::agents_sidebar::MoveWorkspaceToNewWindow;
 
 const SIDEBAR_RESIZE_HANDLE_SIZE: Pixels = px(6.0);
 
@@ -671,6 +673,66 @@ impl MultiWorkspace {
         cx.notify();
     }
 
+    pub fn move_workspace_to_new_window(
+        &mut self,
+        index: usize,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        if self.workspaces.len() <= 1 || index >= self.workspaces.len() {
+            return;
+        }
+
+        let is_active = index == self.active_workspace_index;
+        let workspace = &self.workspaces[index];
+        let paths: Vec<PathBuf> = workspace
+            .read(cx)
+            .worktrees(cx)
+            .map(|worktree| worktree.read(cx).abs_path().to_path_buf())
+            .collect();
+        let app_state: Arc<crate::AppState> = workspace.read(cx).app_state().clone();
+
+        self.remove_workspace(index, window, cx);
+
+        let open_task = cx.spawn(async move |_, cx| {
+            let open_result = cx
+                .update(|cx| {
+                    crate::open_paths(
+                        &paths,
+                        app_state,
+                        crate::OpenOptions {
+                            open_new_workspace: Some(true),
+                            ..Default::default()
+                        },
+                        cx,
+                    )
+                })
+                .await?;
+
+            if is_active {
+                open_result
+                    .window
+                    .update(cx, |_, window, _cx| {
+                        window.activate_window();
+                    })
+                    .log_err();
+            }
+
+            anyhow::Ok(())
+        });
+        open_task.detach_and_log_err(cx);
+    }
+
+    fn move_active_workspace_to_new_window(
+        &mut self,
+        _: &MoveWorkspaceToNewWindow,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        let index = self.active_workspace_index;
+        self.move_workspace_to_new_window(index, window, cx);
+    }
+
     pub fn open_project(
         &mut self,
         paths: Vec<PathBuf>,
@@ -789,6 +851,7 @@ impl Render for MultiWorkspace {
                     ))
                     .on_action(cx.listener(Self::next_workspace))
                     .on_action(cx.listener(Self::previous_workspace))
+                    .on_action(cx.listener(Self::move_active_workspace_to_new_window))
                 })
                 .when(
                     self.sidebar_open() && self.multi_workspace_enabled(cx),

crates/zed_actions/src/lib.rs 🔗

@@ -786,6 +786,8 @@ pub mod agents_sidebar {
         [
             /// Moves focus to the sidebar's search/filter editor.
             FocusSidebarFilter,
+            /// Moves the active workspace to a new window.
+            MoveWorkspaceToNewWindow,
         ]
     );
 }