terminal: Do not auto close shell terminals if they error out (#38180)

Lukas Wirth created

cc https://github.com/zed-industries/zed/issues/38134
Release Notes:

- N/A

Change summary

crates/terminal/src/terminal.rs            | 12 +++++++++---
crates/terminal_view/src/terminal_panel.rs |  6 +++---
2 files changed, 12 insertions(+), 6 deletions(-)

Detailed changes

crates/terminal/src/terminal.rs 🔗

@@ -25,7 +25,7 @@ use alacritty_terminal::{
         ClearMode, CursorStyle as AlacCursorStyle, Handler, NamedPrivateMode, PrivateMode,
     },
 };
-use anyhow::{Result, bail};
+use anyhow::{Context as _, Result, bail};
 
 use futures::{
     FutureExt,
@@ -486,7 +486,8 @@ impl TerminalBuilder {
             pty,
             pty_options.drain_on_exit,
             false,
-        )?;
+        )
+        .context("failed to create event loop")?;
 
         //Kick things off
         let pty_tx = event_loop.channel();
@@ -528,6 +529,7 @@ impl TerminalBuilder {
                 max_scroll_history_lines,
                 window_id,
             },
+            child_exited: None,
         };
 
         if !activation_script.is_empty() && no_task {
@@ -726,6 +728,7 @@ pub struct Terminal {
     shell_program: Option<String>,
     template: CopyTemplate,
     activation_script: Vec<String>,
+    child_exited: Option<ExitStatus>,
 }
 
 struct CopyTemplate {
@@ -1921,10 +1924,13 @@ impl Terminal {
         if let Some(tx) = &self.completion_tx {
             tx.try_send(e).ok();
         }
+        if let Some(e) = e {
+            self.child_exited = Some(e);
+        }
         let task = match &mut self.task {
             Some(task) => task,
             None => {
-                if error_code.is_none() {
+                if self.child_exited.is_none_or(|e| e.code() == Some(0)) {
                     cx.emit(Event::CloseTerminal);
                 }
                 return;

crates/terminal_view/src/terminal_panel.rs 🔗

@@ -471,7 +471,7 @@ impl TerminalPanel {
                 })
                 .ok()?
                 .await
-                .ok()?;
+                .log_err()?;
 
             panel
                 .update_in(cx, move |terminal_panel, window, cx| {
@@ -769,7 +769,7 @@ impl TerminalPanel {
         })
     }
 
-    pub fn add_terminal_shell(
+    fn add_terminal_shell(
         &mut self,
         cwd: Option<PathBuf>,
         reveal_strategy: RevealStrategy,
@@ -779,7 +779,7 @@ impl TerminalPanel {
         let workspace = self.workspace.clone();
         cx.spawn_in(window, async move |terminal_panel, cx| {
             if workspace.update(cx, |workspace, cx| !is_enabled_in_workspace(workspace, cx))? {
-                anyhow::bail!("terminal not yet supported for remote projects");
+                anyhow::bail!("terminal not yet supported for collaborative projects");
             }
             let pane = terminal_panel.update(cx, |terminal_panel, _| {
                 terminal_panel.pending_terminals_to_add += 1;