Cargo.lock 🔗
@@ -22,6 +22,7 @@ dependencies = [
"markdown",
"parking_lot",
"project",
+ "proto",
"serde_json",
"settings",
"smol",
Conrad Irwin and Mikayla Maki created
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Cargo.lock | 1
crates/acp/Cargo.toml | 1
crates/acp/src/server.rs | 22 ++++++++++-
crates/acp/src/thread_view.rs | 66 +++++++++++++++++++++++++++++++++++-
4 files changed, 84 insertions(+), 6 deletions(-)
@@ -22,6 +22,7 @@ dependencies = [
"markdown",
"parking_lot",
"project",
+ "proto",
"serde_json",
"settings",
"smol",
@@ -31,6 +31,7 @@ log.workspace = true
markdown.workspace = true
parking_lot.workspace = true
project.workspace = true
+proto.workspace = true
settings.workspace = true
smol.workspace = true
theme.workspace = true
@@ -270,13 +270,18 @@ impl AcpServer {
}
pub async fn create_thread(self: Arc<Self>, cx: &mut AsyncApp) -> Result<Entity<AcpThread>> {
- let response = self.connection.request(acp::CreateThreadParams).await?;
+ let response = self
+ .connection
+ .request(acp::CreateThreadParams)
+ .await
+ .map_err(to_anyhow)?;
+
let thread_id: ThreadId = response.thread_id.into();
let server = self.clone();
let thread = cx.new(|_| AcpThread {
// todo!
title: "ACP Thread".into(),
- id: thread_id.clone(),
+ id: thread_id.clone(), // Either<ErrorState, Id>
next_entry_id: ThreadEntryId(0),
entries: Vec::default(),
project: self.project.clone(),
@@ -297,7 +302,8 @@ impl AcpServer {
thread_id: thread_id.clone().into(),
message,
})
- .await?;
+ .await
+ .map_err(to_anyhow)?;
Ok(())
}
@@ -306,6 +312,16 @@ impl AcpServer {
}
}
+#[track_caller]
+fn to_anyhow(e: acp::Error) -> anyhow::Error {
+ log::error!(
+ "failed to send message: {code}: {message}",
+ code = e.code,
+ message = e.message
+ );
+ anyhow::anyhow!(e.message)
+}
+
impl From<acp::ThreadId> for ThreadId {
fn from(thread_id: acp::ThreadId) -> Self {
Self(thread_id.0.into())
@@ -12,8 +12,14 @@ use gpui::{
};
use gpui::{FocusHandle, Task};
use language::Buffer;
+<<<<<<< HEAD
use language::language_settings::SoftWrap;
use markdown::{HeadingLevelStyles, MarkdownElement, MarkdownStyle};
+||||||| parent of 47b80cc740 (Show errors from ACP when requests error)
+use markdown::{HeadingLevelStyles, MarkdownElement, MarkdownStyle};
+=======
+use markdown::{HeadingLevelStyles, Markdown, MarkdownElement, MarkdownStyle};
+>>>>>>> 47b80cc740 (Show errors from ACP when requests error)
use project::Project;
use settings::Settings as _;
use theme::ThemeSettings;
@@ -32,6 +38,7 @@ pub struct AcpThreadView {
// todo! reconsider structure. currently pretty sparse, but easy to clean up if we need to delete entries.
thread_entry_views: Vec<Option<ThreadEntryView>>,
message_editor: Entity<Editor>,
+ last_error: Option<Entity<Markdown>>,
list_state: ListState,
send_task: Option<Task<Result<()>>>,
}
@@ -96,6 +103,7 @@ impl AcpThreadView {
message_editor,
send_task: None,
list_state: list_state,
+ last_error: None,
}
}
@@ -137,8 +145,37 @@ impl AcpThreadView {
this.update_in(cx, |this, window, cx| {
match result {
Ok(thread) => {
+<<<<<<< HEAD
let subscription =
cx.subscribe_in(&thread, window, Self::handle_thread_event);
+||||||| parent of 47b80cc740 (Show errors from ACP when requests error)
+ let subscription = cx.subscribe(&thread, |this, _, event, cx| {
+ let count = this.list_state.item_count();
+ match event {
+ AcpThreadEvent::NewEntry => {
+ this.list_state.splice(count..count, 1);
+ }
+ AcpThreadEvent::EntryUpdated(index) => {
+ this.list_state.splice(*index..*index + 1, 1);
+ }
+ }
+ cx.notify();
+ });
+=======
+ dbg!(&thread);
+ let subscription = cx.subscribe(&thread, |this, _, event, cx| {
+ let count = this.list_state.item_count();
+ match event {
+ AcpThreadEvent::NewEntry => {
+ this.list_state.splice(count..count, 1);
+ }
+ AcpThreadEvent::EntryUpdated(index) => {
+ this.list_state.splice(*index..*index + 1, 1);
+ }
+ }
+ cx.notify();
+ });
+>>>>>>> 47b80cc740 (Show errors from ACP when requests error)
this.list_state
.splice(0..0, thread.read(cx).entries().len());
@@ -148,6 +185,7 @@ impl AcpThreadView {
};
}
Err(e) => {
+ dbg!(&e);
if let Some(exit_status) = agent.exit_status() {
this.thread_state = ThreadState::LoadError(
format!(
@@ -189,6 +227,7 @@ impl AcpThreadView {
}
fn chat(&mut self, _: &Chat, window: &mut Window, cx: &mut Context<Self>) {
+ self.last_error.take();
let text = self.message_editor.read(cx).text(cx);
if text.is_empty() {
return;
@@ -198,9 +237,15 @@ impl AcpThreadView {
let task = thread.update(cx, |thread, cx| thread.send(&text, cx));
self.send_task = Some(cx.spawn(async move |this, cx| {
- task.await?;
+ let result = task.await;
- this.update(cx, |this, _cx| {
+ this.update(cx, |this, cx| {
+ if let Err(err) = result {
+ this.last_error =
+ Some(cx.new(|cx| {
+ Markdown::new(format!("Error: {err}").into(), None, None, cx)
+ }))
+ }
this.send_task.take();
})
}));
@@ -865,7 +910,7 @@ impl Focusable for AcpThreadView {
}
impl Render for AcpThreadView {
- fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let text = self.message_editor.read(cx).text(cx);
let is_editor_empty = text.is_empty();
let focus_handle = self.message_editor.focus_handle(cx);
@@ -907,6 +952,21 @@ impl Render for AcpThreadView {
.into()
})),
})
+ .when_some(self.last_error.clone(), |el, error| {
+ el.child(
+ div()
+ .text_xs()
+ .p_2()
+ .gap_2()
+ .border_t_1()
+ .border_color(cx.theme().status().error_border)
+ .bg(cx.theme().status().error_background)
+ .child(MarkdownElement::new(
+ error,
+ default_markdown_style(window, cx),
+ )),
+ )
+ })
.child(
v_flex()
.bg(cx.theme().colors().editor_background)