terminal: Fix terminal cloning on WSL (#39552)

Marco Mihai Condrache created

Should close #39428

The working directory of the `wsl.exe` program is set to a Linux path,
which is invalid on the Windows side, causing the terminal to crash. The
first spawn works because there is no active terminal view, allowing a
new shell (which checks for the remote) to be created. I cannot explain
why it works on SSH remote clients, but I may be missing something in
the remote connection implementation.

I don't have a Windows machine to test this, so I would appreciate
someone testing it. 🙏🏼

Release Notes:

- Fixed an issue where WSL terminals could not be splitted

---------

Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>

Change summary

crates/project/src/terminals.rs            | 55 +++++++++++++----------
crates/terminal/src/terminal.rs            |  8 --
crates/terminal_view/src/terminal_panel.rs |  2 
crates/terminal_view/src/terminal_view.rs  |  2 
4 files changed, 36 insertions(+), 31 deletions(-)

Detailed changes

crates/project/src/terminals.rs 🔗

@@ -404,31 +404,40 @@ impl Project {
         &mut self,
         terminal: &Entity<Terminal>,
         cx: &mut Context<'_, Project>,
-        cwd: impl FnOnce() -> Option<PathBuf>,
+        cwd: Option<PathBuf>,
     ) -> Result<Entity<Terminal>> {
-        terminal.read(cx).clone_builder(cx, cwd).map(|builder| {
-            let terminal_handle = cx.new(|cx| builder.subscribe(cx));
-
-            self.terminals
-                .local_handles
-                .push(terminal_handle.downgrade());
-
-            let id = terminal_handle.entity_id();
-            cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
-                let handles = &mut project.terminals.local_handles;
-
-                if let Some(index) = handles
-                    .iter()
-                    .position(|terminal| terminal.entity_id() == id)
-                {
-                    handles.remove(index);
-                    cx.notify();
-                }
-            })
-            .detach();
+        let local_path = if self.is_via_remote_server() {
+            None
+        } else {
+            cwd
+        };
 
-            terminal_handle
-        })
+        terminal
+            .read(cx)
+            .clone_builder(cx, local_path)
+            .map(|builder| {
+                let terminal_handle = cx.new(|cx| builder.subscribe(cx));
+
+                self.terminals
+                    .local_handles
+                    .push(terminal_handle.downgrade());
+
+                let id = terminal_handle.entity_id();
+                cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
+                    let handles = &mut project.terminals.local_handles;
+
+                    if let Some(index) = handles
+                        .iter()
+                        .position(|terminal| terminal.entity_id() == id)
+                    {
+                        handles.remove(index);
+                        cx.notify();
+                    }
+                })
+                .detach();
+
+                terminal_handle
+            })
     }
 
     pub fn terminal_settings<'a>(

crates/terminal/src/terminal.rs 🔗

@@ -2135,12 +2135,8 @@ impl Terminal {
         self.vi_mode_enabled
     }
 
-    pub fn clone_builder(
-        &self,
-        cx: &App,
-        cwd: impl FnOnce() -> Option<PathBuf>,
-    ) -> Result<TerminalBuilder> {
-        let working_directory = self.working_directory().or_else(cwd);
+    pub fn clone_builder(&self, cx: &App, cwd: Option<PathBuf>) -> Result<TerminalBuilder> {
+        let working_directory = self.working_directory().or_else(|| cwd);
         TerminalBuilder::new(
             working_directory,
             None,

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -466,7 +466,7 @@ impl TerminalPanel {
                     Some(view) => Task::ready(project.clone_terminal(
                         &view.read(cx).terminal.clone(),
                         cx,
-                        || working_directory,
+                        working_directory,
                     )),
                     None => project.create_terminal_shell(working_directory, cx),
                 })

crates/terminal_view/src/terminal_view.rs 🔗

@@ -1225,7 +1225,7 @@ impl Item for TerminalView {
                 let cwd = project
                     .active_project_directory(cx)
                     .map(|it| it.to_path_buf());
-                project.clone_terminal(self.terminal(), cx, || cwd)
+                project.clone_terminal(self.terminal(), cx, cwd)
             })
             .ok()?
             .log_err()?;