diff --git a/crates/acp/src/server.rs b/crates/acp/src/server.rs index e1cdbbae401deeab516d560f6ee53bc07398c71f..5d7115219a0219bdecc35a026162b00d4e0e199b 100644 --- a/crates/acp/src/server.rs +++ b/crates/acp/src/server.rs @@ -7,13 +7,14 @@ use gpui::{App, AppContext, AsyncApp, Context, Entity, Task, WeakEntity}; use parking_lot::Mutex; use project::Project; use smol::process::Child; -use std::{io::Write as _, path::Path, sync::Arc}; +use std::{io::Write as _, path::Path, process::ExitStatus, sync::Arc}; use util::ResultExt; pub struct AcpServer { connection: Arc, threads: Arc>>>, project: Entity, + exit_status: Arc>>, _handler_task: Task<()>, _io_task: Task<()>, } @@ -248,22 +249,26 @@ impl AcpServer { stdout, ); - let io_task = cx.background_spawn(async move { - io_fut.await.log_err(); - process.status().await.log_err(); + let exit_status: Arc>> = Default::default(); + let io_task = cx.background_spawn({ + let exit_status = exit_status.clone(); + async move { + io_fut.await.log_err(); + let result = process.status().await.log_err(); + *exit_status.lock() = result; + } }); Arc::new(Self { project, connection: Arc::new(connection), threads, + exit_status, _handler_task: cx.foreground_executor().spawn(handler_fut), _io_task: io_task, }) } -} -impl AcpServer { pub async fn create_thread(self: Arc, cx: &mut AsyncApp) -> Result> { let response = self.connection.request(acp::CreateThreadParams).await?; let thread_id: ThreadId = response.thread_id.into(); @@ -295,6 +300,10 @@ impl AcpServer { .await?; Ok(()) } + + pub fn exit_status(&self) -> Option { + self.exit_status.lock().clone() + } } impl From for ThreadId { diff --git a/crates/acp/src/thread_view.rs b/crates/acp/src/thread_view.rs index 9853fd9523c5d5bdec3babfbb92be4194056160a..4c570c9c073a8fe3bbd7c8bfeb0b76a3817b307c 100644 --- a/crates/acp/src/thread_view.rs +++ b/crates/acp/src/thread_view.rs @@ -92,7 +92,7 @@ impl AcpThreadView { let project = project.clone(); let load_task = cx.spawn_in(window, async move |this, cx| { let agent = AcpServer::stdio(child, project, cx); - let result = agent.create_thread(cx).await; + let result = agent.clone().create_thread(cx).await; this.update(cx, |this, cx| { match result { @@ -117,7 +117,19 @@ impl AcpThreadView { _subscription: subscription, }; } - Err(e) => this.thread_state = ThreadState::LoadError(e.to_string().into()), + Err(e) => { + if let Some(exit_status) = agent.exit_status() { + this.thread_state = ThreadState::LoadError( + format!( + "Gemini exited with status {}", + exit_status.code().unwrap_or(-127) + ) + .into(), + ) + } else { + this.thread_state = ThreadState::LoadError(e.to_string().into()) + } + } }; cx.notify(); }) @@ -743,7 +755,7 @@ impl Render for AcpThreadView { .p_2() .flex_1() .justify_end() - .child(Label::new(format!("Failed to load {e}")).into_any_element()), + .child(Label::new(format!("Failed to load: {e}")).into_any_element()), ThreadState::Ready { thread, .. } => v_flex() .flex_1() .gap_2()