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