Better handle shell for remote ssh projects (#19297)

Kirill Bulatov and Conrad Irwin created

Release Notes:

- N/A

Co-authored-by: Conrad Irwin <conrad@zed.dev>

Change summary

crates/project/src/terminals.rs            | 12 +++++-------
crates/terminal_view/src/terminal_panel.rs | 16 +++++++++++++++-
2 files changed, 20 insertions(+), 8 deletions(-)

Detailed changes

crates/project/src/terminals.rs 🔗

@@ -6,6 +6,7 @@ use itertools::Itertools;
 use settings::{Settings, SettingsLocation};
 use smol::channel::bounded;
 use std::{
+    borrow::Cow,
     env::{self},
     iter,
     path::{Path, PathBuf},
@@ -341,10 +342,9 @@ pub fn wrap_for_ssh(
     venv_directory: Option<PathBuf>,
 ) -> (String, Vec<String>) {
     let to_run = if let Some((command, args)) = command {
-        iter::once(command)
-            .chain(args)
-            .filter_map(|arg| shlex::try_quote(arg).ok())
-            .join(" ")
+        let command = Cow::Borrowed(command.as_str());
+        let args = args.iter().filter_map(|arg| shlex::try_quote(arg).ok());
+        iter::once(command).chain(args).join(" ")
     } else {
         "exec ${SHELL:-sh} -l".to_string()
     };
@@ -390,9 +390,7 @@ pub fn wrap_for_ssh(
         SshCommand::Direct(ssh_args) => ("ssh".to_string(), ssh_args.clone()),
     };
 
-    if command.is_none() {
-        args.push("-t".to_string())
-    }
+    args.push("-t".to_string());
     args.push(shell_invocation);
     (program, args)
 }

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -395,7 +395,21 @@ impl TerminalPanel {
         let mut spawn_task = spawn_in_terminal.clone();
         // Set up shell args unconditionally, as tasks are always spawned inside of a shell.
         let Some((shell, mut user_args)) = (match spawn_in_terminal.shell.clone() {
-            Shell::System => retrieve_system_shell().map(|shell| (shell, Vec::new())),
+            Shell::System => {
+                match self
+                    .workspace
+                    .update(cx, |workspace, cx| workspace.project().read(cx).is_local())
+                {
+                    Ok(local) => {
+                        if local {
+                            retrieve_system_shell().map(|shell| (shell, Vec::new()))
+                        } else {
+                            Some(("\"${SHELL:-sh}\"".to_string(), Vec::new()))
+                        }
+                    }
+                    Err(_no_window_e) => return,
+                }
+            }
             Shell::Program(shell) => Some((shell, Vec::new())),
             Shell::WithArguments { program, args } => Some((program, args)),
         }) else {