diff.rs

  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}