wip: improving things

Eric Holk , Max Brunsfeld , and Anthony Eid created

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>

Change summary

crates/remote_connection/src/remote_connection.rs |   2 
crates/sidebar/src/sidebar.rs                     | 152 ++++++++++------
2 files changed, 95 insertions(+), 59 deletions(-)

Detailed changes

crates/remote_connection/src/remote_connection.rs 🔗

@@ -19,7 +19,7 @@ use ui::{
     prelude::*,
 };
 use ui_input::{ERASED_EDITOR_FACTORY, ErasedEditor};
-use workspace::{DismissDecision, ModalView};
+use workspace::{DismissDecision, ModalView, Workspace};
 
 pub struct RemoteConnectionPrompt {
     connection_string: SharedString,

crates/sidebar/src/sidebar.rs 🔗

@@ -46,7 +46,8 @@ use workspace::{
     AddFolderToProject, CloseWindow, FocusWorkspaceSidebar, MultiWorkspace, MultiWorkspaceEvent,
     NextProject, NextThread, Open, PreviousProject, PreviousThread, SerializedProjectGroupKey,
     ShowFewerThreads, ShowMoreThreads, Sidebar as WorkspaceSidebar, SidebarSide,
-    ToggleWorkspaceSidebar, Workspace, sidebar_side_context_menu,
+    ToggleWorkspaceSidebar, Workspace, notifications::DetachAndPromptErr,
+    sidebar_side_context_menu,
 };
 
 use zed_actions::OpenRecent;
@@ -2212,6 +2213,17 @@ impl Sidebar {
         };
 
         if let Some(connection_options) = project_group_key.host() {
+            // If there's already an open workspace for this remote host,
+            // reuse it instead of establishing a new SSH connection.
+            if let Some(workspace) = multi_workspace
+                .read(cx)
+                .workspace_for_paths(&folder_paths, cx)
+            {
+                multi_workspace.update(cx, |mw, cx| mw.activate(workspace.clone(), window, cx));
+                self.activate_thread(metadata, &workspace, false, window, cx);
+                return;
+            }
+
             let pending_session_id = metadata.session_id.clone();
             self.pending_remote_thread_activation = Some(pending_session_id.clone());
 
@@ -2228,68 +2240,50 @@ impl Sidebar {
                 .app_state()
                 .clone();
             let paths = folder_paths.paths().to_vec();
-
             let provisional_project_group_key = project_group_key.clone();
 
-            cx.spawn_in(window, async move |this, cx| {
-                let result: anyhow::Result<()> = async {
-                    let delegate: std::sync::Arc<dyn remote::RemoteClientDelegate> =
-                        std::sync::Arc::new(remote_connection::HeadlessRemoteClientDelegate);
-                    let remote_connection =
-                        remote::connect(connection_options.clone(), delegate.clone(), cx).await?;
-
-                    let (_cancel_tx, cancel_rx) = futures::channel::oneshot::channel();
-                    let session = cx
-                        .update(|_, cx| {
-                            remote::RemoteClient::new(
-                                remote::remote_client::ConnectionIdentifier::setup(),
-                                remote_connection,
-                                cancel_rx,
-                                delegate,
-                                cx,
-                            )
-                        })?
-                        .await?
-                        .ok_or_else(|| anyhow::anyhow!("Remote connection was cancelled"))?;
-
-                    let new_project = cx.update(|_, cx| {
-                        project::Project::remote(
-                            session,
-                            app_state.client.clone(),
-                            app_state.node_runtime.clone(),
-                            app_state.user_store.clone(),
-                            app_state.languages.clone(),
-                            app_state.fs.clone(),
-                            true,
-                            cx,
-                        )
-                    })?;
-
-                    workspace::open_remote_project_with_existing_connection(
-                        connection_options,
-                        new_project,
-                        paths,
-                        app_state,
-                        window_handle,
-                        Some(provisional_project_group_key),
+            let active_workspace = multi_workspace.read(cx).workspace().clone();
+            let connect_task = active_workspace.update(cx, |workspace, cx| {
+                workspace.toggle_modal(window, cx, |window, cx| {
+                    remote_connection::RemoteConnectionModal::new(
+                        &connection_options,
+                        Vec::new(),
+                        window,
                         cx,
                     )
-                    .await?;
+                });
 
-                    let workspace = window_handle.update(cx, |multi_workspace, window, cx| {
-                        let workspace = multi_workspace.workspace().clone();
-                        multi_workspace.add(workspace.clone(), window, cx);
-                        workspace
-                    })?;
+                let prompt = workspace
+                    .active_modal::<remote_connection::RemoteConnectionModal>(cx)
+                    .expect("Modal just created")
+                    .read(cx)
+                    .prompt
+                    .clone();
 
-                    this.update_in(cx, |this, window, cx| {
-                        this.activate_thread(metadata, &workspace, false, window, cx);
-                    })?;
-                    anyhow::Ok(())
-                }
-                .await;
+                remote_connection::connect(
+                    remote::remote_client::ConnectionIdentifier::setup(),
+                    connection_options.clone(),
+                    prompt,
+                    window,
+                    cx,
+                )
+                .prompt_err("Failed to connect", window, cx, |_, _, _| None)
+            });
+
+            cx.spawn_in(window, async move |this, cx| {
+                let session = connect_task.await;
 
-                if result.is_err() {
+                active_workspace
+                    .update_in(cx, |workspace, _window, cx| {
+                        if let Some(modal) =
+                            workspace.active_modal::<remote_connection::RemoteConnectionModal>(cx)
+                        {
+                            modal.update(cx, |modal, cx| modal.finished(cx));
+                        }
+                    })
+                    .ok();
+
+                let Some(Some(session)) = session else {
                     this.update(cx, |this, _cx| {
                         if this.pending_remote_thread_activation.as_ref()
                             == Some(&pending_session_id)
@@ -2298,9 +2292,51 @@ impl Sidebar {
                         }
                     })
                     .ok();
-                }
+                    return anyhow::Ok(());
+                };
+
+                let new_project = cx.update(|_, cx| {
+                    project::Project::remote(
+                        session,
+                        app_state.client.clone(),
+                        app_state.node_runtime.clone(),
+                        app_state.user_store.clone(),
+                        app_state.languages.clone(),
+                        app_state.fs.clone(),
+                        true,
+                        cx,
+                    )
+                })?;
 
-                result
+                workspace::open_remote_project_with_existing_connection(
+                    connection_options,
+                    new_project,
+                    paths,
+                    app_state,
+                    window_handle,
+                    Some(provisional_project_group_key),
+                    cx,
+                )
+                .await?;
+
+                let workspace = window_handle.update(cx, |multi_workspace, window, cx| {
+                    let workspace = multi_workspace.workspace().clone();
+                    multi_workspace.add(workspace.clone(), window, cx);
+                    workspace
+                })?;
+
+                this.update_in(cx, |this, window, cx| {
+                    this.activate_thread(metadata, &workspace, false, window, cx);
+                })?;
+
+                this.update(cx, |this, _cx| {
+                    if this.pending_remote_thread_activation.as_ref() == Some(&pending_session_id) {
+                        this.pending_remote_thread_activation = None;
+                    }
+                })
+                .ok();
+
+                anyhow::Ok(())
             })
             .detach_and_log_err(cx);
         } else {