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
32 let new_buffer = cx.new(|cx| Buffer::local(new_text, cx));
33 let old_buffer = cx.new(|cx| Buffer::local(old_text.unwrap_or("".into()), cx));
34 let new_buffer_snapshot = new_buffer.read(cx).text_snapshot();
35 let buffer_diff = cx.new(|cx| BufferDiff::new(&new_buffer_snapshot, cx));
36
37 let task = cx.spawn({
38 let multibuffer = multibuffer.clone();
39 let path = path.clone();
40 async move |_, cx| {
41 let language = language_registry
42 .language_for_file_path(&path)
43 .await
44 .log_err();
45
46 new_buffer.update(cx, |buffer, cx| buffer.set_language(language.clone(), cx))?;
47
48 let old_buffer_snapshot = old_buffer.update(cx, |buffer, cx| {
49 buffer.set_language(language, cx);
50 buffer.snapshot()
51 })?;
52
53 buffer_diff
54 .update(cx, |diff, cx| {
55 diff.set_base_text(
56 old_buffer_snapshot,
57 Some(language_registry),
58 new_buffer_snapshot,
59 cx,
60 )
61 })?
62 .await?;
63
64 multibuffer
65 .update(cx, |multibuffer, cx| {
66 let hunk_ranges = {
67 let buffer = new_buffer.read(cx);
68 let diff = buffer_diff.read(cx);
69 diff.hunks_intersecting_range(Anchor::MIN..Anchor::MAX, buffer, cx)
70 .map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
71 .collect::<Vec<_>>()
72 };
73
74 multibuffer.set_excerpts_for_path(
75 PathKey::for_buffer(&new_buffer, cx),
76 new_buffer.clone(),
77 hunk_ranges,
78 editor::DEFAULT_MULTIBUFFER_CONTEXT,
79 cx,
80 );
81 multibuffer.add_diff(buffer_diff, cx);
82 })
83 .log_err();
84
85 anyhow::Ok(())
86 }
87 });
88
89 Self::Finalized(FinalizedDiff {
90 multibuffer,
91 path,
92 _update_diff: task,
93 })
94 }
95
96 pub fn new(buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Self {
97 let buffer_snapshot = buffer.read(cx).snapshot();
98 let base_text = buffer_snapshot.text();
99 let language_registry = buffer.read(cx).language_registry();
100 let text_snapshot = buffer.read(cx).text_snapshot();
101 let buffer_diff = cx.new(|cx| {
102 let mut diff = BufferDiff::new(&text_snapshot, cx);
103 let _ = diff.set_base_text(
104 buffer_snapshot.clone(),
105 language_registry,
106 text_snapshot,
107 cx,
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 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 { buffer, .. }) => {
162 buffer.read(cx).file().map(|file| file.path().as_ref())
163 }
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
178pub struct PendingDiff {
179 multibuffer: Entity<MultiBuffer>,
180 base_text: Arc<String>,
181 buffer: Entity<Buffer>,
182 diff: Entity<BufferDiff>,
183 revealed_ranges: Vec<Range<Anchor>>,
184 _subscription: Subscription,
185 update_diff: Task<Result<()>>,
186}
187
188impl PendingDiff {
189 pub fn update(&mut self, cx: &mut Context<Diff>) {
190 let buffer = self.buffer.clone();
191 let buffer_diff = self.diff.clone();
192 let base_text = self.base_text.clone();
193 self.update_diff = cx.spawn(async move |diff, cx| {
194 let text_snapshot = buffer.read_with(cx, |buffer, _| buffer.text_snapshot())?;
195 let diff_snapshot = BufferDiff::update_diff(
196 buffer_diff.clone(),
197 text_snapshot.clone(),
198 Some(base_text),
199 false,
200 false,
201 None,
202 None,
203 cx,
204 )
205 .await?;
206 buffer_diff.update(cx, |diff, cx| {
207 diff.set_snapshot(diff_snapshot, &text_snapshot, cx)
208 })?;
209 diff.update(cx, |diff, cx| {
210 if let Diff::Pending(diff) = diff {
211 diff.update_visible_ranges(cx);
212 }
213 })
214 });
215 }
216
217 pub fn reveal_range(&mut self, range: Range<Anchor>, cx: &mut Context<Diff>) {
218 self.revealed_ranges.push(range);
219 self.update_visible_ranges(cx);
220 }
221
222 fn finalize(&self, cx: &mut Context<Diff>) -> FinalizedDiff {
223 let ranges = self.excerpt_ranges(cx);
224 let base_text = self.base_text.clone();
225 let language_registry = self.buffer.read(cx).language_registry().clone();
226
227 let path = self
228 .buffer
229 .read(cx)
230 .file()
231 .map(|file| file.path().as_ref())
232 .unwrap_or(Path::new("untitled"))
233 .into();
234
235 // Replace the buffer in the multibuffer with the snapshot
236 let buffer = cx.new(|cx| {
237 let language = self.buffer.read(cx).language().cloned();
238 let buffer = TextBuffer::new_normalized(
239 0,
240 cx.entity_id().as_non_zero_u64().into(),
241 self.buffer.read(cx).line_ending(),
242 self.buffer.read(cx).as_rope().clone(),
243 );
244 let mut buffer = Buffer::build(buffer, None, Capability::ReadWrite);
245 buffer.set_language(language, cx);
246 buffer
247 });
248
249 let buffer_diff = cx.spawn({
250 let buffer = buffer.clone();
251 let language_registry = language_registry.clone();
252 async move |_this, cx| {
253 build_buffer_diff(base_text, &buffer, language_registry, cx).await
254 }
255 });
256
257 let update_diff = cx.spawn(async move |this, cx| {
258 let buffer_diff = buffer_diff.await?;
259 this.update(cx, |this, cx| {
260 this.multibuffer().update(cx, |multibuffer, cx| {
261 let path_key = PathKey::for_buffer(&buffer, cx);
262 multibuffer.clear(cx);
263 multibuffer.set_excerpts_for_path(
264 path_key,
265 buffer,
266 ranges,
267 editor::DEFAULT_MULTIBUFFER_CONTEXT,
268 cx,
269 );
270 multibuffer.add_diff(buffer_diff.clone(), cx);
271 });
272
273 cx.notify();
274 })
275 });
276
277 FinalizedDiff {
278 path,
279 multibuffer: self.multibuffer.clone(),
280 _update_diff: update_diff,
281 }
282 }
283
284 fn update_visible_ranges(&mut self, cx: &mut Context<Diff>) {
285 let ranges = self.excerpt_ranges(cx);
286 self.multibuffer.update(cx, |multibuffer, cx| {
287 multibuffer.set_excerpts_for_path(
288 PathKey::for_buffer(&self.buffer, cx),
289 self.buffer.clone(),
290 ranges,
291 editor::DEFAULT_MULTIBUFFER_CONTEXT,
292 cx,
293 );
294 let end = multibuffer.len(cx);
295 Some(multibuffer.snapshot(cx).offset_to_point(end).row + 1)
296 });
297 cx.notify();
298 }
299
300 fn excerpt_ranges(&self, cx: &App) -> Vec<Range<Point>> {
301 let buffer = self.buffer.read(cx);
302 let diff = self.diff.read(cx);
303 let mut ranges = diff
304 .hunks_intersecting_range(Anchor::MIN..Anchor::MAX, buffer, cx)
305 .map(|diff_hunk| diff_hunk.buffer_range.to_point(buffer))
306 .collect::<Vec<_>>();
307 ranges.extend(
308 self.revealed_ranges
309 .iter()
310 .map(|range| range.to_point(buffer)),
311 );
312 ranges.sort_unstable_by_key(|range| (range.start, Reverse(range.end)));
313
314 // Merge adjacent ranges
315 let mut ranges = ranges.into_iter().peekable();
316 let mut merged_ranges = Vec::new();
317 while let Some(mut range) = ranges.next() {
318 while let Some(next_range) = ranges.peek() {
319 if range.end >= next_range.start {
320 range.end = range.end.max(next_range.end);
321 ranges.next();
322 } else {
323 break;
324 }
325 }
326
327 merged_ranges.push(range);
328 }
329 merged_ranges
330 }
331}
332
333pub struct FinalizedDiff {
334 path: PathBuf,
335 multibuffer: Entity<MultiBuffer>,
336 _update_diff: Task<Result<()>>,
337}
338
339async fn build_buffer_diff(
340 old_text: Arc<String>,
341 buffer: &Entity<Buffer>,
342 language_registry: Option<Arc<LanguageRegistry>>,
343 cx: &mut AsyncApp,
344) -> Result<Entity<BufferDiff>> {
345 let buffer = cx.update(|cx| buffer.read(cx).snapshot())?;
346
347 let old_text_rope = cx
348 .background_spawn({
349 let old_text = old_text.clone();
350 async move { Rope::from(old_text.as_str()) }
351 })
352 .await;
353 let base_buffer = cx
354 .update(|cx| {
355 Buffer::build_snapshot(
356 old_text_rope,
357 buffer.language().cloned(),
358 language_registry,
359 cx,
360 )
361 })?
362 .await;
363
364 let diff_snapshot = cx
365 .update(|cx| {
366 BufferDiffSnapshot::new_with_base_buffer(
367 buffer.text.clone(),
368 Some(old_text),
369 base_buffer,
370 cx,
371 )
372 })?
373 .await;
374
375 let secondary_diff = cx.new(|cx| {
376 let mut diff = BufferDiff::new(&buffer, cx);
377 diff.set_snapshot(diff_snapshot.clone(), &buffer, cx);
378 diff
379 })?;
380
381 cx.new(|cx| {
382 let mut diff = BufferDiff::new(&buffer.text, cx);
383 diff.set_snapshot(diff_snapshot, &buffer, cx);
384 diff.set_secondary_diff(secondary_diff);
385 diff
386 })
387}