diff --git a/crates/acp_thread/src/diff.rs b/crates/acp_thread/src/diff.rs index 25878501f4bc6a71933f9099e7de120f94aa94c1..e070bb02ef2e83a374f1ae4bfb1736ddafb4106d 100644 --- a/crates/acp_thread/src/diff.rs +++ b/crates/acp_thread/src/diff.rs @@ -1,6 +1,6 @@ use anyhow::Result; use buffer_diff::{BufferDiff, InternalDiffHunk}; -use gpui::{App, AppContext, AsyncApp, Context, Entity, Task}; +use gpui::{App, AppContext, AsyncApp, Context, Entity, Subscription, Task}; use itertools::Itertools; use language::{ Anchor, Buffer, Capability, LanguageRegistry, OffsetRangeExt as _, Point, TextBuffer, @@ -112,20 +112,27 @@ impl Diff { Self::Pending(PendingDiff { multibuffer, base_text: Arc::from(buffer_text_snapshot.text().as_str()), - // _subscription: cx.observe(&buffer, |this, _, cx| { - // if let Diff::Pending(diff) = this { - // diff.update(cx); - // } - // }), + _subscription: cx.observe(&buffer, |this, _, cx| { + if let Diff::Pending(diff) = this { + diff.update(cx); + } + }), new_buffer: buffer, diff: buffer_diff, revealed_ranges: Vec::new(), update_diff: Task::ready(Ok(())), pending_update: None, is_updating: false, + auto_update: false, }) } + pub fn disable_auto_update(&mut self) { + if let Self::Pending(diff) = self { + diff.auto_update = false; + } + } + pub fn reveal_range(&mut self, range: Range, cx: &mut Context) { if let Self::Pending(diff) = self { diff.reveal_range(range, cx); @@ -227,7 +234,7 @@ impl Diff { cx: &mut Context, ) { match self { - Diff::Pending(diff) => diff.update(operations, snapshot, cx), + Diff::Pending(diff) => diff.update_manually(operations, snapshot, cx), Diff::Finalized(_) => {} } } @@ -239,7 +246,8 @@ pub struct PendingDiff { new_buffer: Entity, diff: Entity, revealed_ranges: Vec>, - // _subscription: Subscription, + _subscription: Subscription, + auto_update: bool, update_diff: Task>, // The latest update waiting to be processed. Storing only the latest means // intermediate chunks are coalesced when the worker task can't keep up. @@ -357,15 +365,12 @@ fn compute_hunks( } impl PendingDiff { - pub fn update( + pub fn update_manually( &mut self, operations: Vec, base_snapshot: text::BufferSnapshot, cx: &mut Context, ) { - // Capture the buffer snapshot now, synchronously, so it matches the - // line operations. Capturing it inside the spawned task would race with - // subsequent chunks arriving before the task starts. let text_snapshot = self.new_buffer.read(cx).text_snapshot(); self.pending_update = Some(PendingUpdate { operations, @@ -377,6 +382,42 @@ impl PendingDiff { } } + pub fn update(&mut self, cx: &mut Context) { + let buffer = self.new_buffer.clone(); + let buffer_diff = self.diff.clone(); + let base_text = self.base_text.clone(); + self.update_diff = cx.spawn(async move |diff, cx| { + let text_snapshot = buffer.read_with(cx, |buffer, _| buffer.text_snapshot()); + let language = buffer.read_with(cx, |buffer, _| buffer.language().cloned()); + let update = buffer_diff + .update(cx, |diff, cx| { + diff.update_diff( + text_snapshot.clone(), + Some(base_text.clone()), + None, + language, + cx, + ) + }) + .await; + let (task1, task2) = buffer_diff.update(cx, |diff, cx| { + let task1 = diff.set_snapshot(update.clone(), &text_snapshot, cx); + let task2 = diff + .secondary_diff() + .unwrap() + .update(cx, |diff, cx| diff.set_snapshot(update, &text_snapshot, cx)); + (task1, task2) + }); + task1.await; + task2.await; + diff.update(cx, |diff, cx| { + if let Diff::Pending(diff) = diff { + diff.update_visible_ranges(cx); + } + }) + }); + } + fn flush_pending_update(&mut self, cx: &mut Context) { let Some(PendingUpdate { operations, diff --git a/crates/agent/src/tools/streaming_edit_file_tool.rs b/crates/agent/src/tools/streaming_edit_file_tool.rs index f2d3feb33af8c6a5f56e330b3dfc96e126ccfd28..b8fbf3301fd521f202357dccfecdb48bbc21f6f6 100644 --- a/crates/agent/src/tools/streaming_edit_file_tool.rs +++ b/crates/agent/src/tools/streaming_edit_file_tool.rs @@ -455,7 +455,6 @@ enum EditPipelineEntry { }, StreamingNewText { streaming_diff: StreamingDiff, - line_diff: LineDiff, edit_cursor: usize, reindenter: Reindenter, original_snapshot: text::BufferSnapshot, @@ -607,7 +606,6 @@ impl EditPipeline { let text_snapshot = buffer.read_with(cx, |buffer, _cx| buffer.text_snapshot()); self.current_edit = Some(EditPipelineEntry::StreamingNewText { streaming_diff: StreamingDiff::new(old_text_in_buffer), - line_diff: LineDiff::default(), edit_cursor: range.start, reindenter: Reindenter::new(indent_delta), original_snapshot: text_snapshot, @@ -623,7 +621,6 @@ impl EditPipeline { } => { let Some(EditPipelineEntry::StreamingNewText { streaming_diff, - line_diff, edit_cursor, reindenter, original_snapshot, @@ -647,10 +644,6 @@ impl EditPipeline { &tool.action_log, cx, ); - line_diff.push_char_operations(&char_ops, original_snapshot.as_rope()); - diff.update(cx, |diff, cx| { - diff.update_pending(line_diff.line_operations(), original_snapshot.clone(), cx) - }); let position = original_snapshot.anchor_before(*edit_cursor); cx.update(|cx| { @@ -662,7 +655,6 @@ impl EditPipeline { } => { let Some(EditPipelineEntry::StreamingNewText { mut streaming_diff, - mut line_diff, mut edit_cursor, mut reindenter, original_snapshot, @@ -684,14 +676,6 @@ impl EditPipeline { &tool.action_log, cx, ); - line_diff.push_char_operations(&char_ops, original_snapshot.as_rope()); - diff.update(cx, |diff, cx| { - diff.update_pending( - line_diff.line_operations(), - original_snapshot.clone(), - cx, - ) - }); } let remaining_ops = streaming_diff.finish(); @@ -703,11 +687,6 @@ impl EditPipeline { &tool.action_log, cx, ); - line_diff.push_char_operations(&remaining_ops, original_snapshot.as_rope()); - line_diff.finish(original_snapshot.as_rope()); - diff.update(cx, |diff, cx| { - diff.update_pending(line_diff.line_operations(), original_snapshot.clone(), cx) - }); let position = original_snapshot.anchor_before(edit_cursor); cx.update(|cx| { @@ -764,7 +743,13 @@ impl EditSession { tool.action_log .update(cx, |log, cx| log.buffer_read(buffer.clone(), cx)); - let diff = cx.new(|cx| Diff::new(buffer.clone(), cx)); + let diff = cx.new(|cx| { + let mut diff = Diff::new(buffer.clone(), cx); + if matches!(mode, StreamingEditFileMode::Write) { + diff.disable_auto_update(); + } + diff + }); event_stream.update_diff(diff.clone()); let finalize_diff_guard = util::defer(Box::new({ let diff = diff.downgrade();