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