diff --git a/crates/action_log/src/action_log.rs b/crates/action_log/src/action_log.rs index 6eb18a4f12..672366db02 100644 --- a/crates/action_log/src/action_log.rs +++ b/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(); + + 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) } + }); - (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( diff --git a/crates/agent_ui/src/agent_diff.rs b/crates/agent_ui/src/agent_diff.rs index 91d345b7eb..d45399f38a 100644 --- a/crates/agent_ui/src/agent_diff.rs +++ b/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;