repl: Check process status and propagate to output (#14782)

Kyle Kelley created

<img width="582" alt="image"
src="https://github.com/user-attachments/assets/14bd321d-f5fc-4cc0-9386-f435423057ad">

Release Notes:

- N/A

Change summary

crates/repl/src/kernels.rs |  1 
crates/repl/src/outputs.rs |  2 
crates/repl/src/session.rs | 53 +++++++++++++++++++++++++++++++++++++++
3 files changed, 53 insertions(+), 3 deletions(-)

Detailed changes

crates/repl/src/kernels.rs 🔗

@@ -218,7 +218,6 @@ impl RunningKernel {
                 .with_context(|| format!("Failed to create jupyter runtime dir {runtime_dir:?}"))?;
             let connection_path = runtime_dir.join(format!("kernel-zed-{entity_id}.json"));
             let content = serde_json::to_string(&connection_info)?;
-            // write out file to disk for kernel
             fs.atomic_write(connection_path.clone(), content).await?;
 
             let mut cmd = kernel_specification.command(&connection_path)?;

crates/repl/src/outputs.rs 🔗

@@ -363,7 +363,7 @@ impl LineHeight for OutputType {
     }
 }
 
-#[derive(Default, Clone)]
+#[derive(Default, Clone, Debug)]
 pub enum ExecutionStatus {
     #[default]
     Unknown,

crates/repl/src/session.rs 🔗

@@ -177,11 +177,62 @@ impl Session {
                 let kernel = kernel.await;
 
                 match kernel {
-                    Ok((kernel, mut messages_rx)) => {
+                    Ok((mut kernel, mut messages_rx)) => {
                         this.update(&mut cx, |this, cx| {
                             // At this point we can create a new kind of kernel that has the process and our long running background tasks
+
+                            let status = kernel.process.status();
                             this.kernel = Kernel::RunningKernel(kernel);
 
+                            cx.spawn(|session, mut cx| async move {
+                                let error_message = match status.await {
+                                    Ok(status) => {
+                                        if status.success() {
+                                            log::info!("kernel process exited successfully");
+                                            return;
+                                        }
+
+                                        format!("kernel process exited with status: {:?}", status)
+                                    }
+                                    Err(err) => {
+                                        format!("kernel process exited with error: {:?}", err)
+                                    }
+                                };
+
+                                log::error!("{}", error_message);
+
+                                session
+                                    .update(&mut cx, |session, cx| {
+                                        session.kernel =
+                                            Kernel::ErroredLaunch(error_message.clone());
+
+                                        session.blocks.values().for_each(|block| {
+                                            block.execution_view.update(
+                                                cx,
+                                                |execution_view, cx| {
+                                                    match execution_view.status {
+                                                        ExecutionStatus::Finished => {
+                                                            // Do nothing when the output was good
+                                                        }
+                                                        _ => {
+                                                            // All other cases, set the status to errored
+                                                            execution_view.status =
+                                                                ExecutionStatus::KernelErrored(
+                                                                    error_message.clone(),
+                                                                )
+                                                        }
+                                                    }
+                                                    cx.notify();
+                                                },
+                                            );
+                                        });
+
+                                        cx.notify();
+                                    })
+                                    .ok();
+                            })
+                            .detach();
+
                             this.messaging_task = cx.spawn(|session, mut cx| async move {
                                 while let Some(message) = messages_rx.next().await {
                                     session