From c7aae6bd62321bde345a667bccbe0e3550dfd4ae Mon Sep 17 00:00:00 2001 From: smit Date: Mon, 19 May 2025 21:26:30 +0530 Subject: [PATCH] zed: Fix no way to open local folder from remote window (#30954) Closes #27642 Currently, the `Open (cmd-o)` action is used to open a local folder picker when in a local project, and Zed's remote path modal in the case of a remote project. While this looks intentional, there is now no way to open a local project when you are in a remote project window. Neither by shortcut, nor by UI, as the "Open Local Folder" button uses the same `Open` action. The reverse is not true, as we already have an `Open Remote (ctrl-cmd-o)` action to open the remote modal, where you can select "Add Folder" which opens the same Zed's remote path modal. This already works in both local and remote window cases. This PR makes two changes: 1. It changes `Open (cmd-o)` action such that it should always open the local file picker regardless of which project is currently open, local or remote. This way we have two non-ambiguios actions `Open` and `Open Remote`. 2. It also changes the "Open a project" button (which shows up when no project is open in the project panel) to open the recent modal (which contains buttons to open either local or remote) instead of choosing on behalf of the user. P.S. If we want to open Zed's remote path modal directly, it should be different action altogether. Not covered for now. Release Notes: - Fixed issue where "Open local folder" was not opening folder picker when connected to a remote host. - Added `from_existing_connection` flag to `OpenRemote` action to directly open path picker for current connection, bypassing the Remote Projects modal. --- assets/keymaps/default-linux.json | 2 + assets/keymaps/default-macos.json | 1 + crates/project_panel/src/project_panel.rs | 10 ++++- crates/recent_projects/src/recent_projects.rs | 16 ++++++- crates/recent_projects/src/remote_servers.rs | 6 ++- crates/title_bar/src/title_bar.rs | 12 +++++- crates/zed/src/zed.rs | 43 ++++++++++++++++--- crates/zed/src/zed/app_menus.rs | 7 ++- crates/zed_actions/src/lib.rs | 10 ++++- 9 files changed, 91 insertions(+), 16 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index b541ff90e8..7f02488407 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -512,6 +512,8 @@ "alt-ctrl-o": "projects::OpenRecent", "alt-shift-open": "projects::OpenRemote", "alt-ctrl-shift-o": "projects::OpenRemote", + // Change to open path modal for existing remote connection by setting the parameter + // "alt-ctrl-shift-o": "["projects::OpenRemote", { "from_existing_connection": true }]", "alt-ctrl-shift-b": "branches::OpenRecent", "alt-shift-enter": "toast::RunAction", "ctrl-~": "workspace::NewTerminal", diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 57c0e7c2be..6506aae9aa 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -588,6 +588,7 @@ // "alt-cmd-o": ["projects::OpenRecent", {"create_new_window": true }], "alt-cmd-o": "projects::OpenRecent", "ctrl-cmd-o": "projects::OpenRemote", + "ctrl-cmd-shift-o": ["projects::OpenRemote", { "from_existing_connection": true }], "alt-cmd-b": "branches::OpenRecent", "ctrl-~": "workspace::NewTerminal", "cmd-s": "workspace::Save", diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 00f49a5e82..8667036b7e 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -65,6 +65,7 @@ use workspace::{ notifications::{DetachAndPromptErr, NotifyTaskExt}, }; use worktree::CreatedEntry; +use zed_actions::OpenRecent; const PROJECT_PANEL_KEY: &str = "ProjectPanel"; const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX; @@ -4881,11 +4882,16 @@ impl Render for ProjectPanel { .child( Button::new("open_project", "Open a project") .full_width() - .key_binding(KeyBinding::for_action(&workspace::Open, window, cx)) + .key_binding(KeyBinding::for_action_in( + &OpenRecent::default(), + &self.focus_handle, + window, + cx, + )) .on_click(cx.listener(|this, _, window, cx| { this.workspace .update(cx, |_, cx| { - window.dispatch_action(Box::new(workspace::Open), cx) + window.dispatch_action(OpenRecent::default().boxed_clone(), cx); }) .log_err(); })), diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 03444f03e5..60f7b38d56 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -468,9 +468,21 @@ impl PickerDelegate for RecentProjectsDelegate { .border_color(cx.theme().colors().border_variant) .child( Button::new("remote", "Open Remote Folder") - .key_binding(KeyBinding::for_action(&OpenRemote, window, cx)) + .key_binding(KeyBinding::for_action( + &OpenRemote { + from_existing_connection: false, + }, + window, + cx, + )) .on_click(|_, window, cx| { - window.dispatch_action(OpenRemote.boxed_clone(), cx) + window.dispatch_action( + OpenRemote { + from_existing_connection: false, + } + .boxed_clone(), + cx, + ) }), ) .child( diff --git a/crates/recent_projects/src/remote_servers.rs b/crates/recent_projects/src/remote_servers.rs index c12b3462ae..d2c985946f 100644 --- a/crates/recent_projects/src/remote_servers.rs +++ b/crates/recent_projects/src/remote_servers.rs @@ -350,7 +350,11 @@ impl RemoteServerProjects { _window: Option<&mut Window>, _: &mut Context, ) { - workspace.register_action(|workspace, _: &OpenRemote, window, cx| { + workspace.register_action(|workspace, action: &OpenRemote, window, cx| { + if action.from_existing_connection { + cx.propagate(); + return; + } let handle = cx.entity().downgrade(); let fs = workspace.project().read(cx).fs().clone(); workspace.toggle_modal(window, cx, |window, cx| Self::new(fs, window, cx, handle)) diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index c629981541..668a0828f3 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -432,14 +432,22 @@ impl TitleBar { .tooltip(move |window, cx| { Tooltip::with_meta( "Remote Project", - Some(&OpenRemote), + Some(&OpenRemote { + from_existing_connection: false, + }), meta.clone(), window, cx, ) }) .on_click(|_, window, cx| { - window.dispatch_action(OpenRemote.boxed_clone(), cx); + window.dispatch_action( + OpenRemote { + from_existing_connection: false, + } + .boxed_clone(), + cx, + ); }) .into_any_element(), ) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 2d092f7eb4..d330241184 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -503,7 +503,7 @@ fn register_actions( directories: true, multiple: true, }, - DirectoryLister::Project(workspace.project().clone()), + DirectoryLister::Local(workspace.app_state().fs.clone()), window, cx, ); @@ -515,11 +515,42 @@ fn register_actions( if let Some(task) = this .update_in(cx, |this, window, cx| { - if this.project().read(cx).is_local() { - this.open_workspace_for_paths(false, paths, window, cx) - } else { - open_new_ssh_project_from_project(this, paths, window, cx) - } + this.open_workspace_for_paths(false, paths, window, cx) + }) + .log_err() + { + task.await.log_err(); + } + }) + .detach() + }) + .register_action(|workspace, action: &zed_actions::OpenRemote, window, cx| { + if !action.from_existing_connection { + cx.propagate(); + return; + } + // You need existing remote connection to open it this way + if workspace.project().read(cx).is_local() { + return; + } + telemetry::event!("Project Opened"); + let paths = workspace.prompt_for_open_path( + PathPromptOptions { + files: true, + directories: true, + multiple: true, + }, + DirectoryLister::Project(workspace.project().clone()), + window, + cx, + ); + cx.spawn_in(window, async move |this, cx| { + let Some(paths) = paths.await.log_err().flatten() else { + return; + }; + if let Some(task) = this + .update_in(cx, |this, window, cx| { + open_new_ssh_project_from_project(this, paths, window, cx) }) .log_err() { diff --git a/crates/zed/src/zed/app_menus.rs b/crates/zed/src/zed/app_menus.rs index 17a7e43927..c6c3ef5959 100644 --- a/crates/zed/src/zed/app_menus.rs +++ b/crates/zed/src/zed/app_menus.rs @@ -70,7 +70,12 @@ pub fn app_menus() -> Vec { create_new_window: true, }, ), - MenuItem::action("Open Remote...", zed_actions::OpenRemote), + MenuItem::action( + "Open Remote...", + zed_actions::OpenRemote { + from_existing_connection: false, + }, + ), MenuItem::separator(), MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject), MenuItem::separator(), diff --git a/crates/zed_actions/src/lib.rs b/crates/zed_actions/src/lib.rs index 61887d3563..8ad7a6edc9 100644 --- a/crates/zed_actions/src/lib.rs +++ b/crates/zed_actions/src/lib.rs @@ -243,8 +243,14 @@ pub struct OpenRecent { pub create_new_window: bool, } -impl_actions!(projects, [OpenRecent]); -actions!(projects, [OpenRemote]); +#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct OpenRemote { + #[serde(default)] + pub from_existing_connection: bool, +} + +impl_actions!(projects, [OpenRecent, OpenRemote]); /// Where to spawn the task in the UI. #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]