From 8be5ed22f6e01089cfa76f622fc07019642df24f Mon Sep 17 00:00:00 2001 From: Smit Barmase Date: Tue, 1 Apr 2025 14:05:28 +0530 Subject: [PATCH] workspace: Fix SSH remote restore on second open + Fix panel not opening automatically on new SSH remote (#27830) Closes #26902 - We used to serialize SSH remote only when opened via recent entries, and not on first time. This broke restore, when opening same folder for second time from recent entries. Once opened for second time, restoring used to. work correctly. This PR fixes this by serializing when opened for first time. - We didn't handle window replace post worktree creation in first time flow, this resulted in project panel not opening automatically like it does with recent entries, or local projects. This PR fixes it by following same flow as recent entries. Release Notes: - Fixed SSH remote not restoring when opening for second time. - Fixed project panel not opening when opening new SSH remote folder. --- crates/recent_projects/src/remote_servers.rs | 51 +++--- crates/recent_projects/src/ssh_connections.rs | 2 +- crates/workspace/src/workspace.rs | 158 ++++++++++++------ 3 files changed, 127 insertions(+), 84 deletions(-) diff --git a/crates/recent_projects/src/remote_servers.rs b/crates/recent_projects/src/remote_servers.rs index 392ee3048cd61a339d2888ecaa999c1284692972..30671289efd18432ced9296725f6cd46c08f6305 100644 --- a/crates/recent_projects/src/remote_servers.rs +++ b/crates/recent_projects/src/remote_servers.rs @@ -32,7 +32,10 @@ use util::ResultExt; use workspace::OpenOptions; use workspace::Toast; use workspace::notifications::NotificationId; -use workspace::{ModalView, Workspace, notifications::DetachAndPromptErr}; +use workspace::{ + ModalView, Workspace, notifications::DetachAndPromptErr, + open_ssh_project_with_existing_connection, +}; use crate::OpenRemote; use crate::ssh_connections::RemoteSettingsContent; @@ -157,13 +160,8 @@ impl ProjectPicker { let app_state = workspace .update(cx, |workspace, _| workspace.app_state().clone()) .ok()?; - let options = cx - .update(|_, cx| (app_state.build_window_options)(None, cx)) - .log_err()?; - - cx.open_window(options, |window, cx| { - window.activate_window(); + cx.update(|_, cx| { let fs = app_state.fs.clone(); update_settings_file::(fs, cx, { let paths = paths @@ -180,32 +178,27 @@ impl ProjectPicker { } } }); + }) + .log_err(); - let tasks = paths - .into_iter() - .map(|path| { - project.update(cx, |project, cx| { - project.find_or_create_worktree(&path, true, cx) - }) - }) - .collect::>(); - window - .spawn(cx, async move |_| { - for task in tasks { - task.await?; - } - Ok(()) + let options = cx + .update(|_, cx| (app_state.build_window_options)(None, cx)) + .log_err()?; + let window = cx + .open_window(options, |window, cx| { + cx.new(|cx| { + telemetry::event!("SSH Project Created"); + Workspace::new(None, project.clone(), app_state.clone(), window, cx) }) - .detach_and_prompt_err("Failed to open path", window, cx, |_, _, _| { - None - }); - - cx.new(|cx| { - telemetry::event!("SSH Project Created"); - Workspace::new(None, project.clone(), app_state.clone(), window, cx) }) - }) + .log_err()?; + + open_ssh_project_with_existing_connection( + connection, project, paths, app_state, window, cx, + ) + .await .log_err(); + this.update(cx, |_, cx| { cx.emit(DismissEvent); }) diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index 988f5880e0e76e7c6a1f1fc32a06a0477b092b39..846aab2985182fa3c1e90f567669fccf21e3e4fd 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -599,7 +599,7 @@ pub async fn open_ssh_project( let did_open_ssh_project = cx .update(|cx| { - workspace::open_ssh_project( + workspace::open_ssh_project_with_new_connection( window, connection_options.clone(), cancel_rx, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 8c0c33808be771d195e8664f8f99bf03bf3fbeb6..843bbd57863a6322264e1f31757361dea9031d8b 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -6279,7 +6279,7 @@ pub fn create_and_open_local_file( }) } -pub fn open_ssh_project( +pub fn open_ssh_project_with_new_connection( window: WindowHandle, connection_options: SshConnectionOptions, cancel_rx: oneshot::Receiver<()>, @@ -6320,68 +6320,118 @@ pub fn open_ssh_project( ) })?; - let toolchains = DB.toolchains(workspace_id).await?; - for (toolchain, worktree_id, path) in toolchains { - project - .update(cx, |this, cx| { - this.activate_toolchain(ProjectPath { worktree_id, path }, toolchain, cx) - })? - .await; - } - let mut project_paths_to_open = vec![]; - let mut project_path_errors = vec![]; + open_ssh_project_inner( + project, + paths, + serialized_ssh_project, + workspace_id, + serialized_workspace, + app_state, + window, + cx, + ) + .await + }) +} - for path in paths { - let result = cx - .update(|cx| Workspace::project_path_for_path(project.clone(), &path, true, cx))? - .await; - match result { - Ok((_, project_path)) => { - project_paths_to_open.push((path.clone(), Some(project_path))); - } - Err(error) => { - project_path_errors.push(error); - } - }; - } +pub fn open_ssh_project_with_existing_connection( + connection_options: SshConnectionOptions, + project: Entity, + paths: Vec, + app_state: Arc, + window: WindowHandle, + cx: &mut AsyncApp, +) -> Task> { + cx.spawn(async move |cx| { + let (serialized_ssh_project, workspace_id, serialized_workspace) = + serialize_ssh_project(connection_options.clone(), paths.clone(), &cx).await?; - if project_paths_to_open.is_empty() { - return Err(project_path_errors - .pop() - .unwrap_or_else(|| anyhow!("no paths given"))); - } + open_ssh_project_inner( + project, + paths, + serialized_ssh_project, + workspace_id, + serialized_workspace, + app_state, + window, + cx, + ) + .await + }) +} + +async fn open_ssh_project_inner( + project: Entity, + paths: Vec, + serialized_ssh_project: SerializedSshProject, + workspace_id: WorkspaceId, + serialized_workspace: Option, + app_state: Arc, + window: WindowHandle, + cx: &mut AsyncApp, +) -> Result<()> { + let toolchains = DB.toolchains(workspace_id).await?; + for (toolchain, worktree_id, path) in toolchains { + project + .update(cx, |this, cx| { + this.activate_toolchain(ProjectPath { worktree_id, path }, toolchain, cx) + })? + .await; + } + let mut project_paths_to_open = vec![]; + let mut project_path_errors = vec![]; - cx.update_window(window.into(), |_, window, cx| { - window.replace_root(cx, |window, cx| { - telemetry::event!("SSH Project Opened"); + for path in paths { + let result = cx + .update(|cx| Workspace::project_path_for_path(project.clone(), &path, true, cx))? + .await; + match result { + Ok((_, project_path)) => { + project_paths_to_open.push((path.clone(), Some(project_path))); + } + Err(error) => { + project_path_errors.push(error); + } + }; + } - let mut workspace = - Workspace::new(Some(workspace_id), project, app_state.clone(), window, cx); - workspace.set_serialized_ssh_project(serialized_ssh_project); - workspace - }); - })?; + if project_paths_to_open.is_empty() { + return Err(project_path_errors + .pop() + .unwrap_or_else(|| anyhow!("no paths given"))); + } - window - .update(cx, |_, window, cx| { - window.activate_window(); + cx.update_window(window.into(), |_, window, cx| { + window.replace_root(cx, |window, cx| { + telemetry::event!("SSH Project Opened"); - open_items(serialized_workspace, project_paths_to_open, window, cx) - })? - .await?; + let mut workspace = + Workspace::new(Some(workspace_id), project, app_state.clone(), window, cx); + workspace.set_serialized_ssh_project(serialized_ssh_project); + workspace + }); + })?; - window.update(cx, |workspace, _, cx| { - for error in project_path_errors { - if error.error_code() == proto::ErrorCode::DevServerProjectPathDoesNotExist { - if let Some(path) = error.error_tag("path") { - workspace.show_error(&anyhow!("'{path}' does not exist"), cx) - } - } else { - workspace.show_error(&error, cx) + window + .update(cx, |_, window, cx| { + window.activate_window(); + open_items(serialized_workspace, project_paths_to_open, window, cx) + })? + .await?; + + window.update(cx, |workspace, _, cx| { + for error in project_path_errors { + if error.error_code() == proto::ErrorCode::DevServerProjectPathDoesNotExist { + if let Some(path) = error.error_tag("path") { + workspace.show_error(&anyhow!("'{path}' does not exist"), cx) } + } else { + workspace.show_error(&error, cx) } - }) - }) + } + })?; + + Ok(()) } fn serialize_ssh_project(