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