fill in bufferdiff parts

Cole Miller created

Change summary

crates/buffer_diff/src/buffer_diff.rs   | 141 +++++++++++++++++++-------
crates/editor/src/split.rs              |   3 
crates/multi_buffer/src/multi_buffer.rs | 108 ++++++++++++--------
3 files changed, 171 insertions(+), 81 deletions(-)

Detailed changes

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -27,12 +27,21 @@ pub struct BufferDiff {
     secondary_diff: Option<Entity<BufferDiff>>,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone)]
 pub struct BufferDiffSnapshot {
     inner: BufferDiffInner,
     secondary_diff: Option<Box<BufferDiffSnapshot>>,
 }
 
+impl std::fmt::Debug for BufferDiffSnapshot {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("BufferDiffSnapshot")
+            .field("inner", &self.inner)
+            .field("secondary_diff", &self.secondary_diff)
+            .finish()
+    }
+}
+
 #[derive(Clone)]
 struct BufferDiffInner {
     hunks: SumTree<InternalDiffHunk>,
@@ -338,9 +347,16 @@ impl BufferDiffSnapshot {
     pub fn hunks_intersecting_base_text_range<'a>(
         &'a self,
         range: Range<usize>,
+        main_buffer: &'a text::BufferSnapshot,
     ) -> impl 'a + Iterator<Item = DiffHunk> {
-        // FIXME
-        std::iter::empty()
+        let unstaged_counterpart = self.secondary_diff.as_ref().map(|diff| &diff.inner);
+        let filter = move |summary: &DiffHunkSummary| {
+            let before_start = summary.diff_base_byte_range.end < range.start;
+            let after_end = summary.diff_base_byte_range.start > range.end;
+            !before_start && !after_end
+        };
+        self.inner
+            .hunks_intersecting_range_impl(filter, main_buffer, unstaged_counterpart)
     }
 
     pub fn base_text(&self) -> &language::BufferSnapshot {
@@ -623,15 +639,22 @@ impl BufferDiffInner {
         secondary: Option<&'a Self>,
     ) -> impl 'a + Iterator<Item = DiffHunk> {
         let range = range.to_offset(buffer);
+        let filter = move |summary: &DiffHunkSummary| {
+            let summary_range = summary.buffer_range.to_offset(buffer);
+            let before_start = summary_range.end < range.start;
+            let after_end = summary_range.start > range.end;
+            !before_start && !after_end
+        };
+        self.hunks_intersecting_range_impl(filter, buffer, secondary)
+    }
 
-        let mut cursor = self
-            .hunks
-            .filter::<_, DiffHunkSummary>(buffer, move |summary| {
-                let summary_range = summary.buffer_range.to_offset(buffer);
-                let before_start = summary_range.end < range.start;
-                let after_end = summary_range.start > range.end;
-                !before_start && !after_end
-            });
+    fn hunks_intersecting_range_impl<'a>(
+        &'a self,
+        filter: impl 'a + Fn(&DiffHunkSummary) -> bool,
+        buffer: &'a text::BufferSnapshot,
+        secondary: Option<&'a Self>,
+    ) -> impl 'a + Iterator<Item = DiffHunk> {
+        let mut cursor = self.hunks.filter::<_, DiffHunkSummary>(buffer, filter);
 
         let anchor_iter = iter::from_fn(move || {
             cursor.next();
@@ -781,13 +804,19 @@ impl BufferDiffInner {
         })
     }
 
-    fn compare(&self, old: &Self, new_snapshot: &text::BufferSnapshot) -> Option<Range<Anchor>> {
+    fn compare(
+        &self,
+        old: &Self,
+        new_snapshot: &text::BufferSnapshot,
+    ) -> (Option<Range<Anchor>>, Option<Range<usize>>) {
         let mut new_cursor = self.hunks.cursor::<()>(new_snapshot);
         let mut old_cursor = old.hunks.cursor::<()>(new_snapshot);
         old_cursor.next();
         new_cursor.next();
         let mut start = None;
         let mut end = None;
+        let mut base_text_start = None;
+        let mut base_text_end = None;
 
         loop {
             match (new_cursor.item(), old_cursor.item()) {
@@ -799,12 +828,15 @@ impl BufferDiffInner {
                     {
                         Ordering::Less => {
                             start.get_or_insert(new_hunk.buffer_range.start);
+                            base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
                             end.replace(new_hunk.buffer_range.end);
+                            base_text_end.get_or_insert(new_hunk.diff_base_byte_range.end);
                             new_cursor.next();
                         }
                         Ordering::Equal => {
                             if new_hunk != old_hunk {
                                 start.get_or_insert(new_hunk.buffer_range.start);
+                                base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
                                 if old_hunk
                                     .buffer_range
                                     .end
@@ -812,8 +844,10 @@ impl BufferDiffInner {
                                     .is_ge()
                                 {
                                     end.replace(old_hunk.buffer_range.end);
+                                    base_text_end.replace(old_hunk.diff_base_byte_range.end);
                                 } else {
                                     end.replace(new_hunk.buffer_range.end);
+                                    base_text_end.replace(new_hunk.diff_base_byte_range.end);
                                 }
                             }
 
@@ -822,26 +856,37 @@ impl BufferDiffInner {
                         }
                         Ordering::Greater => {
                             start.get_or_insert(old_hunk.buffer_range.start);
+                            base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start);
                             end.replace(old_hunk.buffer_range.end);
+                            base_text_end.replace(old_hunk.diff_base_byte_range.end);
                             old_cursor.next();
                         }
                     }
                 }
                 (Some(new_hunk), None) => {
                     start.get_or_insert(new_hunk.buffer_range.start);
+                    base_text_start.get_or_insert(new_hunk.diff_base_byte_range.start);
                     end.replace(new_hunk.buffer_range.end);
+                    base_text_end.replace(new_hunk.diff_base_byte_range.end);
                     new_cursor.next();
                 }
                 (None, Some(old_hunk)) => {
                     start.get_or_insert(old_hunk.buffer_range.start);
+                    base_text_start.get_or_insert(old_hunk.diff_base_byte_range.start);
                     end.replace(old_hunk.buffer_range.end);
+                    base_text_end.replace(old_hunk.diff_base_byte_range.end);
                     old_cursor.next();
                 }
                 (None, None) => break,
             }
         }
 
-        start.zip(end).map(|(start, end)| start..end)
+        (
+            start.zip(end).map(|(start, end)| start..end),
+            base_text_start
+                .zip(base_text_end)
+                .map(|(start, end)| start..end),
+        )
     }
 }
 
@@ -1135,7 +1180,7 @@ impl BufferDiff {
             });
             cx.emit(BufferDiffEvent::DiffChanged {
                 changed_range: Some(Anchor::min_max_range_for_buffer(self.buffer_id)),
-                base_text_changed_range: todo!(),
+                base_text_changed_range: Some(0..self.base_text().len()),
             });
         }
     }
@@ -1161,9 +1206,11 @@ impl BufferDiff {
         ));
         if let Some((first, last)) = hunks.first().zip(hunks.last()) {
             let changed_range = first.buffer_range.start..last.buffer_range.end;
+            let base_text_changed_range =
+                first.diff_base_byte_range.start..last.diff_base_byte_range.end;
             cx.emit(BufferDiffEvent::DiffChanged {
                 changed_range: Some(changed_range),
-                base_text_changed_range: todo!(),
+                base_text_changed_range: Some(base_text_changed_range),
             });
         }
         new_index_text
@@ -1174,18 +1221,19 @@ impl BufferDiff {
         range: Range<Anchor>,
         buffer: &text::BufferSnapshot,
         cx: &App,
-    ) -> Option<Range<Anchor>> {
-        let start = self
+    ) -> (Option<Range<Anchor>>, Option<Range<usize>>) {
+        let first_hunk = self
             .hunks_intersecting_range(range.clone(), buffer, cx)
-            .next()?
-            .buffer_range
-            .start;
-        let end = self
-            .hunks_intersecting_range_rev(range, buffer)
-            .next()?
-            .buffer_range
-            .end;
-        Some(start..end)
+            .next();
+        let last_hunk = self.hunks_intersecting_range_rev(range, buffer).next();
+        let range = first_hunk
+            .as_ref()
+            .zip(last_hunk.as_ref())
+            .map(|(first, last)| first.buffer_range.start..last.buffer_range.end);
+        let base_text_range = first_hunk
+            .zip(last_hunk)
+            .map(|(first, last)| first.diff_base_byte_range.start..last.diff_base_byte_range.end);
+        (range, base_text_range)
     }
 
     pub async fn update_diff(
@@ -1247,9 +1295,9 @@ impl BufferDiff {
 
         let state = &mut self.inner;
         let new_state = new_snapshot.inner;
-        let (base_text_changed, mut changed_range) =
+        let (base_text_changed, (mut changed_range, mut base_text_changed_range)) =
             match (state.base_text_exists, new_state.base_text_exists) {
-                (false, false) => (true, None),
+                (false, false) => (true, (None, None)),
                 (true, true)
                     if state.base_text.remote_id() == new_state.base_text.remote_id()
                         && state.base_text.syntax_update_count()
@@ -1259,12 +1307,15 @@ impl BufferDiff {
                 }
                 _ => (
                     true,
-                    Some(text::Anchor::min_max_range_for_buffer(self.buffer_id)),
+                    (
+                        Some(text::Anchor::min_max_range_for_buffer(self.buffer_id)),
+                        Some(0..new_state.base_text.len()),
+                    ),
                 ),
             };
 
         if let Some(secondary_changed_range) = secondary_diff_change
-            && let Some(secondary_hunk_range) =
+            && let (Some(secondary_hunk_range), Some(secondary_base_range)) =
                 self.range_to_hunk_range(secondary_changed_range, buffer, cx)
         {
             if let Some(range) = &mut changed_range {
@@ -1273,6 +1324,13 @@ impl BufferDiff {
             } else {
                 changed_range = Some(secondary_hunk_range);
             }
+
+            if let Some(base_text_range) = &mut base_text_changed_range {
+                base_text_range.start = secondary_base_range.start.min(base_text_range.start);
+                base_text_range.end = secondary_base_range.end.max(base_text_range.end);
+            } else {
+                base_text_changed_range = Some(secondary_base_range);
+            }
         }
 
         let state = &mut self.inner;
@@ -1288,13 +1346,22 @@ impl BufferDiff {
                 } else {
                     changed_range = Some(first.buffer_range.start..last.buffer_range.end);
                 }
+
+                if let Some(base_text_range) = &mut base_text_changed_range {
+                    base_text_range.start =
+                        base_text_range.start.min(first.diff_base_byte_range.start);
+                    base_text_range.end = base_text_range.end.max(last.diff_base_byte_range.end);
+                } else {
+                    base_text_changed_range =
+                        Some(first.diff_base_byte_range.start..last.diff_base_byte_range.end);
+                }
             }
             state.pending_hunks = SumTree::new(buffer);
         }
 
         cx.emit(BufferDiffEvent::DiffChanged {
             changed_range: changed_range.clone(),
-            base_text_changed_range: todo!(),
+            base_text_changed_range,
         });
         changed_range
     }
@@ -2163,7 +2230,7 @@ mod tests {
 
         let empty_diff = cx.update(|cx| BufferDiffSnapshot::empty(&buffer, cx));
         let diff_1 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
-        let range = diff_1.inner.compare(&empty_diff.inner, &buffer).unwrap();
+        let range = diff_1.inner.compare(&empty_diff.inner, &buffer).0.unwrap();
         assert_eq!(range.to_point(&buffer), Point::new(0, 0)..Point::new(8, 0));
 
         // Edit does not affect the diff.
@@ -2181,7 +2248,7 @@ mod tests {
             .unindent(),
         );
         let diff_2 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
-        assert_eq!(None, diff_2.inner.compare(&diff_1.inner, &buffer));
+        assert_eq!(None, diff_2.inner.compare(&diff_1.inner, &buffer).0);
 
         // Edit turns a deletion hunk into a modification.
         buffer.edit_via_marked_text(
@@ -2198,7 +2265,7 @@ mod tests {
             .unindent(),
         );
         let diff_3 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
-        let range = diff_3.inner.compare(&diff_2.inner, &buffer).unwrap();
+        let range = diff_3.inner.compare(&diff_2.inner, &buffer).0.unwrap();
         assert_eq!(range.to_point(&buffer), Point::new(1, 0)..Point::new(2, 0));
 
         // Edit turns a modification hunk into a deletion.
@@ -2215,7 +2282,7 @@ mod tests {
             .unindent(),
         );
         let diff_4 = BufferDiffSnapshot::new_sync(buffer.clone(), base_text.clone(), cx);
-        let range = diff_4.inner.compare(&diff_3.inner, &buffer).unwrap();
+        let range = diff_4.inner.compare(&diff_3.inner, &buffer).0.unwrap();
         assert_eq!(range.to_point(&buffer), Point::new(3, 4)..Point::new(4, 0));
 
         // Edit introduces a new insertion hunk.
@@ -2233,7 +2300,7 @@ mod tests {
             .unindent(),
         );
         let diff_5 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text.clone(), cx);
-        let range = diff_5.inner.compare(&diff_4.inner, &buffer).unwrap();
+        let range = diff_5.inner.compare(&diff_4.inner, &buffer).0.unwrap();
         assert_eq!(range.to_point(&buffer), Point::new(3, 0)..Point::new(4, 0));
 
         // Edit removes a hunk.
@@ -2251,7 +2318,7 @@ mod tests {
             .unindent(),
         );
         let diff_6 = BufferDiffSnapshot::new_sync(buffer.snapshot(), base_text, cx);
-        let range = diff_6.inner.compare(&diff_5.inner, &buffer).unwrap();
+        let range = diff_6.inner.compare(&diff_5.inner, &buffer).0.unwrap();
         assert_eq!(range.to_point(&buffer), Point::new(7, 0)..Point::new(8, 0));
     }
 

crates/editor/src/split.rs 🔗

@@ -8,6 +8,7 @@ use language::{Buffer, Capability, LanguageRegistry};
 use multi_buffer::{Anchor, ExcerptRange, MultiBuffer, PathKey};
 use project::Project;
 use rope::Point;
+use text::OffsetRangeExt as _;
 use ui::{
     App, Context, InteractiveElement as _, IntoElement as _, ParentElement as _, Render,
     Styled as _, Window, div,
@@ -242,6 +243,8 @@ impl SplittableEditor {
         }
     }
 
+    // FIXME need add_diff management in here too
+
     pub fn set_excerpts_for_path(
         &mut self,
         path: PathKey,

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -15,7 +15,7 @@ use buffer_diff::{
 };
 use clock::ReplicaId;
 use collections::{BTreeMap, Bound, HashMap, HashSet};
-use gpui::{App, Context, Entity, EntityId, EventEmitter};
+use gpui::{App, Context, Entity, EntityId, EventEmitter, WeakEntity};
 use itertools::Itertools;
 use language::{
     AutoindentMode, BracketMatch, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability,
@@ -514,7 +514,7 @@ struct DiffState {
     ///
     /// The [`DiffState`]s in a [`MultiBuffer`] must either be all inverted, or
     // all not inverted.
-    is_inverted: bool,
+    main_buffer: Option<WeakEntity<Buffer>>,
     _subscription: gpui::Subscription,
 }
 
@@ -522,7 +522,11 @@ impl DiffState {
     fn snapshot(&self, cx: &App) -> DiffStateSnapshot {
         DiffStateSnapshot {
             diff: self.diff.read(cx).snapshot(cx),
-            is_inverted: self.is_inverted,
+            main_buffer: self.main_buffer.as_ref().and_then(|main_buffer| {
+                main_buffer
+                    .read_with(cx, |main_buffer, _| main_buffer.snapshot())
+                    .ok()
+            }),
         }
     }
 }
@@ -530,7 +534,7 @@ impl DiffState {
 #[derive(Clone)]
 struct DiffStateSnapshot {
     diff: BufferDiffSnapshot,
-    is_inverted: bool,
+    main_buffer: Option<BufferSnapshot>,
 }
 
 impl std::ops::Deref for DiffStateSnapshot {
@@ -558,37 +562,43 @@ impl DiffState {
                 _ => {}
             }),
             diff,
-            is_inverted: false,
+            main_buffer: None,
         }
     }
 
     fn new_inverted(
         diff: Entity<BufferDiff>,
         base_text_buffer_id: BufferId,
+        main_buffer: Entity<Buffer>,
         cx: &mut Context<MultiBuffer>,
     ) -> Self {
+        let main_buffer = main_buffer.downgrade();
         DiffState {
-            _subscription: cx.subscribe(&diff, move |this, diff, event, cx| match event {
-                BufferDiffEvent::DiffChanged {
-                    changed_range: _,
-                    base_text_changed_range,
-                } => {
-                    if let Some(base_text_changed_range) = base_text_changed_range.clone() {
-                        this.inverted_buffer_diff_changed(
-                            diff,
-                            base_text_changed_range,
-                            base_text_buffer_id,
-                            cx,
-                        )
+            _subscription: cx.subscribe(&diff, {
+                let main_buffer = main_buffer.clone();
+                move |this, diff, event, cx| match event {
+                    BufferDiffEvent::DiffChanged {
+                        changed_range: _,
+                        base_text_changed_range,
+                    } => {
+                        if let Some(base_text_changed_range) = base_text_changed_range.clone() {
+                            this.inverted_buffer_diff_changed(
+                                diff,
+                                base_text_changed_range,
+                                base_text_buffer_id,
+                                main_buffer.clone(),
+                                cx,
+                            )
+                        }
+                        cx.emit(Event::BufferDiffChanged);
                     }
-                    cx.emit(Event::BufferDiffChanged);
+                    // FIXME
+                    BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
+                    _ => {}
                 }
-                // FIXME
-                BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
-                _ => {}
             }),
             diff,
-            is_inverted: true,
+            main_buffer: Some(main_buffer),
         }
     }
 }
@@ -2267,7 +2277,7 @@ impl MultiBuffer {
         let buffer_id = diff.buffer_id;
         let diff = DiffStateSnapshot {
             diff: diff.snapshot(cx),
-            is_inverted: false,
+            main_buffer: None,
         };
         self.snapshot.get_mut().diffs.insert(buffer_id, diff);
     }
@@ -2276,12 +2286,15 @@ impl MultiBuffer {
         &mut self,
         base_text_buffer_id: BufferId,
         diff: Entity<BufferDiff>,
+        main_buffer: WeakEntity<Buffer>,
         cx: &mut Context<Self>,
     ) {
         let diff = diff.read(cx);
         let diff = DiffStateSnapshot {
             diff: diff.snapshot(cx),
-            is_inverted: true,
+            main_buffer: main_buffer
+                .update(cx, |main_buffer, _| main_buffer.snapshot())
+                .ok(),
         };
         self.snapshot
             .get_mut()
@@ -2309,7 +2322,7 @@ impl MultiBuffer {
 
         let new_diff = DiffStateSnapshot {
             diff: diff.snapshot(cx),
-            is_inverted: false,
+            main_buffer: None,
         };
         let mut snapshot = self.snapshot.get_mut();
         let base_text_changed = snapshot
@@ -2371,15 +2384,9 @@ impl MultiBuffer {
         diff: Entity<BufferDiff>,
         diff_change_range: Range<usize>,
         base_text_buffer_id: BufferId,
+        main_buffer: WeakEntity<Buffer>,
         cx: &mut Context<Self>,
     ) {
-        // FIXME move this to the level of the splittableeditor
-        // if base_text_changed && let Some(buffer_state) = self.buffers.get(&base_text_buffer_id) {
-        //     buffer_state.buffer.update(cx, |buffer, cx| {
-        //         // FIXME use the rope directly
-        //         buffer.set_text(diff.read(cx).base_text().text(), cx);
-        //     });
-        // }
         self.sync_mut(cx);
 
         let diff = diff.read(cx);
@@ -2390,7 +2397,9 @@ impl MultiBuffer {
 
         let new_diff = DiffStateSnapshot {
             diff: diff.snapshot(cx),
-            is_inverted: true,
+            main_buffer: main_buffer
+                .update(cx, |main_buffer, _| main_buffer.snapshot())
+                .ok(),
         };
         let mut snapshot = self.snapshot.get_mut();
         let base_text_changed = snapshot
@@ -2601,7 +2610,7 @@ impl MultiBuffer {
     }
 
     pub fn add_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
-        debug_assert!(self.diffs.values().all(|diff| !diff.is_inverted));
+        debug_assert!(self.diffs.values().all(|diff| diff.main_buffer.is_none()));
 
         let buffer_id = diff.read(cx).buffer_id;
         self.buffer_diff_changed(
@@ -2617,15 +2626,22 @@ impl MultiBuffer {
         &mut self,
         base_text_buffer_id: BufferId,
         diff: Entity<BufferDiff>,
+        main_buffer: Entity<Buffer>,
         cx: &mut Context<Self>,
     ) {
-        debug_assert!(self.diffs.values().all(|diff| diff.is_inverted));
+        debug_assert!(self.diffs.values().all(|diff| diff.main_buffer.is_some()));
 
         let diff_change_range = 0..diff.read(cx).base_text().len();
-        self.inverted_buffer_diff_changed(diff.clone(), diff_change_range, base_text_buffer_id, cx);
+        self.inverted_buffer_diff_changed(
+            diff.clone(),
+            diff_change_range,
+            base_text_buffer_id,
+            main_buffer.downgrade(),
+            cx,
+        );
         self.diffs.insert(
             base_text_buffer_id,
-            DiffState::new_inverted(diff, base_text_buffer_id, cx),
+            DiffState::new_inverted(diff, base_text_buffer_id, main_buffer, cx),
         );
     }
 
@@ -3300,10 +3316,11 @@ impl MultiBuffer {
                     excerpt_buffer_start + edit.new.end.saturating_sub(excerpt_start);
                 let edit_buffer_end = edit_buffer_end.min(excerpt_buffer_end);
 
-                if diff.is_inverted {
-                    for hunk in
-                        diff.hunks_intersecting_base_text_range(edit_buffer_start..edit_buffer_end)
-                    {
+                if let Some(main_buffer) = &diff.main_buffer {
+                    for hunk in diff.hunks_intersecting_base_text_range(
+                        edit_buffer_start..edit_buffer_end,
+                        main_buffer,
+                    ) {
                         let hunk_buffer_range = hunk.diff_base_byte_range.clone();
                         if hunk_buffer_range.start < excerpt_buffer_start {
                             log::trace!("skipping hunk that starts before excerpt");
@@ -3838,12 +3855,15 @@ impl MultiBufferSnapshot {
         self.lift_buffer_metadata(query_range.clone(), move |buffer, buffer_range| {
             let diff = self.diffs.get(&buffer.remote_id())?;
             let iter: Box<dyn Iterator<Item = (DiffHunk, &BufferSnapshot, bool)>> =
-                if diff.is_inverted {
+                if let Some(main_buffer) = &diff.main_buffer {
                     let buffer_start = buffer.point_to_offset(buffer_range.start);
                     let buffer_end = buffer.point_to_offset(buffer_range.end);
                     Box::new(
-                        diff.hunks_intersecting_base_text_range(buffer_start..buffer_end)
-                            .map(move |hunk| (hunk, buffer, true)),
+                        diff.hunks_intersecting_base_text_range(
+                            buffer_start..buffer_end,
+                            main_buffer,
+                        )
+                        .map(move |hunk| (hunk, buffer, true)),
                     )
                 } else {
                     let buffer_start = buffer.anchor_before(buffer_range.start);