Add a draft that fixes things

Kirill Bulatov created

Change summary

crates/action_log/src/action_log.rs | 84 +++++++++++++++++++-----------
crates/agent_ui/src/agent_diff.rs   | 12 ++++
2 files changed, 65 insertions(+), 31 deletions(-)

Detailed changes

crates/action_log/src/action_log.rs 🔗

@@ -216,7 +216,20 @@ impl ActionLog {
         loop {
             futures::select_biased! {
                 buffer_update = buffer_updates.next() => {
-                    if let Some((author, buffer_snapshot)) = buffer_update {
+                    if let Some((mut author, mut buffer_snapshot)) = buffer_update {
+                        // TODO kb `buffer.edit(` made by agent input below fires off this code path again
+                        // as we react on buffer edits and send them under "user" edits here again and again.
+                        // Below is a stub to deduplicate things, but this should be done on the editor level
+
+                        // Drain any pending updates and keep only the latest snapshot.
+                        // This coalesces rapid edits to avoid repeatedly recalculating diffs.
+                        // while let Ok(Some((next_author, next_snapshot))) = buffer_updates.try_next() {
+                        //     // If any update was from Agent, treat the coalesced update as Agent
+                        //     if matches!(next_author, ChangeAuthor::Agent) {
+                        //         author = ChangeAuthor::Agent;
+                        //     }
+                        //     buffer_snapshot = next_snapshot;
+                        // }
                         Self::track_edits(&this, &buffer, author, buffer_snapshot, cx).await?;
                     } else {
                         break;
@@ -246,39 +259,50 @@ impl ActionLog {
                 .get_mut(buffer)
                 .context("buffer not tracked")?;
 
-            let rebase = cx.background_spawn({
-                let mut base_text = tracked_buffer.diff_base.clone();
-                let old_snapshot = tracked_buffer.snapshot.clone();
-                let new_snapshot = buffer_snapshot.clone();
-                let unreviewed_edits = tracked_buffer.unreviewed_edits.clone();
-                let edits = diff_snapshots(&old_snapshot, &new_snapshot);
-                async move {
-                    if let ChangeAuthor::User = author {
-                        apply_non_conflicting_edits(
-                            &unreviewed_edits,
-                            edits,
-                            &mut base_text,
-                            new_snapshot.as_rope(),
-                        );
-                    }
+            let old_snapshot = tracked_buffer.snapshot.clone();
+            let new_snapshot = buffer_snapshot.clone();
 
-                    (Arc::new(base_text.to_string()), base_text)
-                }
-            });
+            if !new_snapshot.version().changed_since(old_snapshot.version()) {
+                Ok(None)
+            } else {
+                let rebase = cx.background_spawn({
+                    let mut base_text = tracked_buffer.diff_base.clone();
+
+                    let unreviewed_edits = tracked_buffer.unreviewed_edits.clone();
+                    let edits = diff_snapshots(&old_snapshot, &new_snapshot);
+                    async move {
+                        if let ChangeAuthor::User = author {
+                            apply_non_conflicting_edits(
+                                &unreviewed_edits,
+                                edits,
+                                &mut base_text,
+                                new_snapshot.as_rope(),
+                            );
+                        }
+
+                        (Arc::new(base_text.to_string()), base_text)
+                    }
+                });
 
-            anyhow::Ok(rebase)
+                anyhow::Ok(Some(rebase))
+            }
         })??;
-        let (new_base_text, new_diff_base) = rebase.await;
 
-        Self::update_diff(
-            this,
-            buffer,
-            buffer_snapshot,
-            new_base_text,
-            new_diff_base,
-            cx,
-        )
-        .await
+        if let Some(rebase) = rebase {
+            let (new_base_text, new_diff_base) = rebase.await;
+
+            Self::update_diff(
+                this,
+                buffer,
+                buffer_snapshot,
+                new_base_text,
+                new_diff_base,
+                cx,
+            )
+            .await?;
+        }
+
+        Ok(())
     }
 
     async fn keep_committed_edits(

crates/agent_ui/src/agent_diff.rs 🔗

@@ -1483,8 +1483,18 @@ impl AgentDiff {
                 };
 
                 let multibuffer = editor.read(cx).buffer().clone();
+                let new_diff = diff_handle.update(cx, |original_diff, cx| {
+                    cx.new(|cx| buffer_diff::BufferDiff::new(original_diff.base_text(), cx))
+                });
                 multibuffer.update(cx, |multibuffer, cx| {
-                    multibuffer.add_diff(diff_handle.clone(), cx);
+                    // TODO kb is there a better way?
+                    // This will force real buffer and agent panel's one to calculate diffs independently.
+                    // Buffer's calculation will be non-instant (debounced by rapid edits) and theoretically may be different
+                    // (as the agent one could be optimized for streaming)
+
+                    multibuffer.add_diff(new_diff, cx);
+                    // If we keep the diff handle shared, real buffer will flicker if the line wrap is enabled and the agent edits multiple lines.
+                    // multibuffer.add_diff(diff_handle.clone(), cx);
                 });
 
                 let reviewing_state = EditorState::Reviewing;