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 executor = cx.background_executor().clone();
365    let old_text_rope = cx
366        .background_spawn({
367            let old_text = old_text.clone();
368            let executor = executor.clone();
369            async move { Rope::from_str(old_text.as_str(), &executor) }
370        })
371        .await;
372    let base_buffer = cx
373        .update(|cx| {
374            Buffer::build_snapshot(
375                old_text_rope,
376                buffer.language().cloned(),
377                language_registry,
378                cx,
379            )
380        })?
381        .await;
382
383    let diff_snapshot = cx
384        .update(|cx| {
385            BufferDiffSnapshot::new_with_base_buffer(
386                buffer.text.clone(),
387                Some(old_text),
388                base_buffer,
389                cx,
390            )
391        })?
392        .await;
393
394    let secondary_diff = cx.new(|cx| {
395        let mut diff = BufferDiff::new(&buffer, cx);
396        diff.set_snapshot(diff_snapshot.clone(), &buffer, cx);
397        diff
398    })?;
399
400    cx.new(|cx| {
401        let mut diff = BufferDiff::new(&buffer.text, cx);
402        diff.set_snapshot(diff_snapshot, &buffer, cx);
403        diff.set_secondary_diff(secondary_diff);
404        diff
405    })
406}
407
408#[cfg(test)]
409mod tests {
410    use gpui::{AppContext as _, TestAppContext};
411    use language::Buffer;
412
413    use crate::Diff;
414
415    #[gpui::test]
416    async fn test_pending_diff(cx: &mut TestAppContext) {
417        let buffer = cx.new(|cx| Buffer::local("hello!", cx));
418        let _diff = cx.new(|cx| Diff::new(buffer.clone(), cx));
419        buffer.update(cx, |buffer, cx| {
420            buffer.set_text("HELLO!", cx);
421        });
422        cx.run_until_parked();
423    }
424}