1use agent_client_protocol as acp;
2use anyhow::Result;
3use buffer_diff::{BufferDiff, BufferDiffSnapshot};
4use editor::{MultiBuffer, PathKey};
5use gpui::{App, AppContext, AsyncApp, Context, Entity, Subscription, Task};
6use itertools::Itertools;
7use language::{
8 Anchor, Buffer, Capability, LanguageRegistry, OffsetRangeExt as _, Point, Rope, TextBuffer,
9};
10use std::{
11 cmp::Reverse,
12 ops::Range,
13 path::{Path, PathBuf},
14 sync::Arc,
15};
16use util::ResultExt;
17
18pub enum Diff {
19 Pending(PendingDiff),
20 Finalized(FinalizedDiff),
21}
22
23impl Diff {
24 pub fn from_acp(
25 diff: acp::Diff,
26 language_registry: Arc<LanguageRegistry>,
27 cx: &mut Context<Self>,
28 ) -> Self {
29 let acp::Diff {
30 path,
31 old_text,
32 new_text,
33 } = diff;
34
35 let multibuffer = cx.new(|_cx| MultiBuffer::without_headers(Capability::ReadOnly));
36
37 let new_buffer = cx.new(|cx| Buffer::local(new_text, cx));
38 let old_buffer = cx.new(|cx| Buffer::local(old_text.unwrap_or("".into()), cx));
39 let new_buffer_snapshot = new_buffer.read(cx).text_snapshot();
40 let buffer_diff = cx.new(|cx| BufferDiff::new(&new_buffer_snapshot, cx));
41
42 let task = cx.spawn({
43 let multibuffer = multibuffer.clone();
44 let path = path.clone();
45 async move |_, cx| {
46 let language = language_registry
47 .language_for_file_path(&path)
48 .await
49 .log_err();
50
51 new_buffer.update(cx, |buffer, cx| buffer.set_language(language.clone(), cx))?;
52
53 let old_buffer_snapshot = old_buffer.update(cx, |buffer, cx| {
54 buffer.set_language(language, cx);
55 buffer.snapshot()
56 })?;
57
58 buffer_diff
59 .update(cx, |diff, cx| {
60 diff.set_base_text(
61 old_buffer_snapshot,
62 Some(language_registry),
63 new_buffer_snapshot,
64 cx,
65 )
66 })?
67 .await?;
68
69 multibuffer
70 .update(cx, |multibuffer, cx| {
71 let hunk_ranges = {
72 let buffer = new_buffer.read(cx);
73 let diff = buffer_diff.read(cx);
74 diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer, cx)
75 .map(|diff_hunk| diff_hunk.buffer_range.to_point(&buffer))
76 .collect::<Vec<_>>()
77 };
78
79 multibuffer.set_excerpts_for_path(
80 PathKey::for_buffer(&new_buffer, cx),
81 new_buffer.clone(),
82 hunk_ranges,
83 editor::DEFAULT_MULTIBUFFER_CONTEXT,
84 cx,
85 );
86 multibuffer.add_diff(buffer_diff, cx);
87 })
88 .log_err();
89
90 anyhow::Ok(())
91 }
92 });
93
94 Self::Finalized(FinalizedDiff {
95 multibuffer,
96 path,
97 _update_diff: task,
98 })
99 }
100
101 pub fn new(buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Self {
102 let buffer_snapshot = buffer.read(cx).snapshot();
103 let base_text = buffer_snapshot.text();
104 let language_registry = buffer.read(cx).language_registry();
105 let text_snapshot = buffer.read(cx).text_snapshot();
106 let buffer_diff = cx.new(|cx| {
107 let mut diff = BufferDiff::new(&text_snapshot, cx);
108 let _ = diff.set_base_text(
109 buffer_snapshot.clone(),
110 language_registry,
111 text_snapshot,
112 cx,
113 );
114 diff
115 });
116
117 let multibuffer = cx.new(|cx| {
118 let mut multibuffer = MultiBuffer::without_headers(Capability::ReadOnly);
119 multibuffer.add_diff(buffer_diff.clone(), cx);
120 multibuffer
121 });
122
123 Self::Pending(PendingDiff {
124 multibuffer,
125 base_text: Arc::new(base_text),
126 _subscription: cx.observe(&buffer, |this, _, cx| {
127 if let Diff::Pending(diff) = this {
128 diff.update(cx);
129 }
130 }),
131 buffer,
132 diff: buffer_diff,
133 revealed_ranges: Vec::new(),
134 update_diff: Task::ready(Ok(())),
135 })
136 }
137
138 pub fn reveal_range(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
139 if let Self::Pending(diff) = self {
140 diff.reveal_range(range, cx);
141 }
142 }
143
144 pub fn finalize(&mut self, cx: &mut Context<Self>) {
145 if let Self::Pending(diff) = self {
146 *self = Self::Finalized(diff.finalize(cx));
147 }
148 }
149
150 pub fn multibuffer(&self) -> &Entity<MultiBuffer> {
151 match self {
152 Self::Pending(PendingDiff { multibuffer, .. }) => multibuffer,
153 Self::Finalized(FinalizedDiff { multibuffer, .. }) => multibuffer,
154 }
155 }
156
157 pub fn to_markdown(&self, cx: &App) -> String {
158 let buffer_text = self
159 .multibuffer()
160 .read(cx)
161 .all_buffers()
162 .iter()
163 .map(|buffer| buffer.read(cx).text())
164 .join("\n");
165 let path = match self {
166 Diff::Pending(PendingDiff { buffer, .. }) => {
167 buffer.read(cx).file().map(|file| file.path().as_ref())
168 }
169 Diff::Finalized(FinalizedDiff { path, .. }) => Some(path.as_path()),
170 };
171 format!(
172 "Diff: {}\n```\n{}\n```\n",
173 path.unwrap_or(Path::new("untitled")).display(),
174 buffer_text
175 )
176 }
177}
178
179pub struct PendingDiff {
180 multibuffer: Entity<MultiBuffer>,
181 base_text: Arc<String>,
182 buffer: Entity<Buffer>,
183 diff: Entity<BufferDiff>,
184 revealed_ranges: Vec<Range<Anchor>>,
185 _subscription: Subscription,
186 update_diff: Task<Result<()>>,
187}
188
189impl PendingDiff {
190 pub fn update(&mut self, cx: &mut Context<Diff>) {
191 let buffer = self.buffer.clone();
192 let buffer_diff = self.diff.clone();
193 let base_text = self.base_text.clone();
194 self.update_diff = cx.spawn(async move |diff, cx| {
195 let text_snapshot = buffer.read_with(cx, |buffer, _| buffer.text_snapshot())?;
196 let diff_snapshot = BufferDiff::update_diff(
197 buffer_diff.clone(),
198 text_snapshot.clone(),
199 Some(base_text),
200 false,
201 false,
202 None,
203 None,
204 cx,
205 )
206 .await?;
207 buffer_diff.update(cx, |diff, cx| {
208 diff.set_snapshot(diff_snapshot, &text_snapshot, cx)
209 })?;
210 diff.update(cx, |diff, cx| {
211 if let Diff::Pending(diff) = diff {
212 diff.update_visible_ranges(cx);
213 }
214 })
215 });
216 }
217
218 pub fn reveal_range(&mut self, range: Range<Anchor>, cx: &mut Context<Diff>) {
219 self.revealed_ranges.push(range);
220 self.update_visible_ranges(cx);
221 }
222
223 fn finalize(&self, cx: &mut Context<Diff>) -> FinalizedDiff {
224 let ranges = self.excerpt_ranges(cx);
225 let base_text = self.base_text.clone();
226 let language_registry = self.buffer.read(cx).language_registry().clone();
227
228 let path = self
229 .buffer
230 .read(cx)
231 .file()
232 .map(|file| file.path().as_ref())
233 .unwrap_or(Path::new("untitled"))
234 .into();
235
236 // Replace the buffer in the multibuffer with the snapshot
237 let buffer = cx.new(|cx| {
238 let language = self.buffer.read(cx).language().cloned();
239 let buffer = TextBuffer::new_normalized(
240 0,
241 cx.entity_id().as_non_zero_u64().into(),
242 self.buffer.read(cx).line_ending(),
243 self.buffer.read(cx).as_rope().clone(),
244 );
245 let mut buffer = Buffer::build(buffer, None, Capability::ReadWrite);
246 buffer.set_language(language, cx);
247 buffer
248 });
249
250 let buffer_diff = cx.spawn({
251 let buffer = buffer.clone();
252 let language_registry = language_registry.clone();
253 async move |_this, cx| {
254 build_buffer_diff(base_text, &buffer, language_registry, cx).await
255 }
256 });
257
258 let update_diff = cx.spawn(async move |this, cx| {
259 let buffer_diff = buffer_diff.await?;
260 this.update(cx, |this, cx| {
261 this.multibuffer().update(cx, |multibuffer, cx| {
262 let path_key = PathKey::for_buffer(&buffer, cx);
263 multibuffer.clear(cx);
264 multibuffer.set_excerpts_for_path(
265 path_key,
266 buffer,
267 ranges,
268 editor::DEFAULT_MULTIBUFFER_CONTEXT,
269 cx,
270 );
271 multibuffer.add_diff(buffer_diff.clone(), cx);
272 });
273
274 cx.notify();
275 })
276 });
277
278 FinalizedDiff {
279 path,
280 multibuffer: self.multibuffer.clone(),
281 _update_diff: update_diff,
282 }
283 }
284
285 fn update_visible_ranges(&mut self, cx: &mut Context<Diff>) {
286 let ranges = self.excerpt_ranges(cx);
287 self.multibuffer.update(cx, |multibuffer, cx| {
288 multibuffer.set_excerpts_for_path(
289 PathKey::for_buffer(&self.buffer, cx),
290 self.buffer.clone(),
291 ranges,
292 editor::DEFAULT_MULTIBUFFER_CONTEXT,
293 cx,
294 );
295 let end = multibuffer.len(cx);
296 Some(multibuffer.snapshot(cx).offset_to_point(end).row + 1)
297 });
298 cx.notify();
299 }
300
301 fn excerpt_ranges(&self, cx: &App) -> Vec<Range<Point>> {
302 let buffer = self.buffer.read(cx);
303 let diff = self.diff.read(cx);
304 let mut ranges = diff
305 .hunks_intersecting_range(Anchor::MIN..Anchor::MAX, &buffer, cx)
306 .map(|diff_hunk| diff_hunk.buffer_range.to_point(&buffer))
307 .collect::<Vec<_>>();
308 ranges.extend(
309 self.revealed_ranges
310 .iter()
311 .map(|range| range.to_point(&buffer)),
312 );
313 ranges.sort_unstable_by_key(|range| (range.start, Reverse(range.end)));
314
315 // Merge adjacent ranges
316 let mut ranges = ranges.into_iter().peekable();
317 let mut merged_ranges = Vec::new();
318 while let Some(mut range) = ranges.next() {
319 while let Some(next_range) = ranges.peek() {
320 if range.end >= next_range.start {
321 range.end = range.end.max(next_range.end);
322 ranges.next();
323 } else {
324 break;
325 }
326 }
327
328 merged_ranges.push(range);
329 }
330 merged_ranges
331 }
332}
333
334pub struct FinalizedDiff {
335 path: PathBuf,
336 multibuffer: Entity<MultiBuffer>,
337 _update_diff: Task<Result<()>>,
338}
339
340async fn build_buffer_diff(
341 old_text: Arc<String>,
342 buffer: &Entity<Buffer>,
343 language_registry: Option<Arc<LanguageRegistry>>,
344 cx: &mut AsyncApp,
345) -> Result<Entity<BufferDiff>> {
346 let buffer = cx.update(|cx| buffer.read(cx).snapshot())?;
347
348 let old_text_rope = cx
349 .background_spawn({
350 let old_text = old_text.clone();
351 async move { Rope::from(old_text.as_str()) }
352 })
353 .await;
354 let base_buffer = cx
355 .update(|cx| {
356 Buffer::build_snapshot(
357 old_text_rope,
358 buffer.language().cloned(),
359 language_registry,
360 cx,
361 )
362 })?
363 .await;
364
365 let diff_snapshot = cx
366 .update(|cx| {
367 BufferDiffSnapshot::new_with_base_buffer(
368 buffer.text.clone(),
369 Some(old_text),
370 base_buffer,
371 cx,
372 )
373 })?
374 .await;
375
376 let secondary_diff = cx.new(|cx| {
377 let mut diff = BufferDiff::new(&buffer, cx);
378 diff.set_snapshot(diff_snapshot.clone(), &buffer, cx);
379 diff
380 })?;
381
382 cx.new(|cx| {
383 let mut diff = BufferDiff::new(&buffer.text, cx);
384 diff.set_snapshot(diff_snapshot, &buffer, cx);
385 diff.set_secondary_diff(secondary_diff);
386 diff
387 })
388}