Update list incrementally

Agus Zubiaga and Conrad Irwin created

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/acp/src/acp.rs         | 38 ++++++++++++++++++++++--------------
crates/acp/src/thread_view.rs | 17 +++++++++++----
2 files changed, 35 insertions(+), 20 deletions(-)

Detailed changes

crates/acp/src/acp.rs 🔗

@@ -108,6 +108,13 @@ pub struct AcpThread {
     project: Entity<Project>,
 }
 
+enum AcpThreadEvent {
+    NewEntry,
+    LastEntryUpdated,
+}
+
+impl EventEmitter<AcpThreadEvent> for AcpThread {}
+
 impl AcpThread {
     pub fn new(
         server: Arc<AcpServer>,
@@ -146,33 +153,34 @@ impl AcpThread {
             id: self.next_entry_id.post_inc(),
             content: entry,
         });
-        cx.notify();
+        cx.emit(AcpThreadEvent::NewEntry)
     }
 
     pub fn push_assistant_chunk(&mut self, chunk: MessageChunk, cx: &mut Context<Self>) {
-        if let Some(last_entry) = self.entries.last_mut() {
-            if let AgentThreadEntryContent::Message(Message {
+        if let Some(last_entry) = self.entries.last_mut()
+            && let AgentThreadEntryContent::Message(Message {
                 ref mut chunks,
                 role: Role::Assistant,
             }) = last_entry.content
-            {
-                if let (
-                    Some(MessageChunk::Text { chunk: old_chunk }),
-                    MessageChunk::Text { chunk: new_chunk },
-                ) = (chunks.last_mut(), &chunk)
-                {
-                    old_chunk.push_str(&new_chunk);
-                    return cx.notify();
-                }
+        {
+            cx.emit(AcpThreadEvent::LastEntryUpdated);
 
+            if let (
+                Some(MessageChunk::Text { chunk: old_chunk }),
+                MessageChunk::Text { chunk: new_chunk },
+            ) = (chunks.last_mut(), &chunk)
+            {
+                old_chunk.push_str(&new_chunk);
+            } else {
                 chunks.push(chunk);
                 return cx.notify();
             }
+
+            return;
         }
 
-        self.entries.push(ThreadEntry {
-            id: self.next_entry_id.post_inc(),
-            content: AgentThreadEntryContent::Message(Message {
+        self.push_entry(
+            AgentThreadEntryContent::Message(Message {
                 role: Role::Assistant,
                 chunks: vec![chunk],
             }),

crates/acp/src/thread_view.rs 🔗

@@ -10,7 +10,9 @@ use ui::Tooltip;
 use ui::prelude::*;
 use zed_actions::agent::Chat;
 
-use crate::{AcpThread, AgentThreadEntryContent, Message, MessageChunk, Role, ThreadEntry};
+use crate::{
+    AcpThread, AcpThreadEvent, AgentThreadEntryContent, Message, MessageChunk, Role, ThreadEntry,
+};
 
 pub struct AcpThreadView {
     thread: Entity<AcpThread>,
@@ -42,11 +44,16 @@ impl AcpThreadView {
             editor
         });
 
-        let subscription = cx.observe(&thread, |this, thread, cx| {
+        let subscription = cx.subscribe(&thread, |this, _, event, cx| {
             let count = this.list_state.item_count();
-            // TODO: Incremental updates
-            this.list_state
-                .splice(0..count, thread.read(cx).entries.len());
+            match event {
+                AcpThreadEvent::NewEntry => {
+                    this.list_state.splice(count..count, 1);
+                }
+                AcpThreadEvent::LastEntryUpdated => {
+                    this.list_state.splice(count - 1..count, 1);
+                }
+            }
             cx.notify();
         });