checkpoint

Cole Miller and cameron created

Co-authored-by: cameron <cameron.studdstreet@gmail.com>

Change summary

crates/buffer_diff/src/buffer_diff.rs   |  1 
crates/multi_buffer/src/multi_buffer.rs | 83 +++++++++++++++++---------
crates/multi_buffer/src/path_key.rs     | 75 +++++++++++++++++++++++
3 files changed, 127 insertions(+), 32 deletions(-)

Detailed changes

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -36,7 +36,6 @@ pub struct BufferDiffSnapshot {
 #[derive(Clone)]
 struct BufferDiffInner {
     hunks: SumTree<InternalDiffHunk>,
-    // Used for making staging mo
     pending_hunks: SumTree<PendingHunk>,
     base_text: language::BufferSnapshot,
     base_text_exists: bool,

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -87,6 +87,8 @@ pub struct MultiBuffer {
     /// The writing capability of the multi-buffer.
     capability: Capability,
     buffer_changed_since_sync: Rc<Cell<bool>>,
+    follower: Option<Entity<MultiBuffer>>,
+    base_text_buffers_by_main_buffer_id: HashMap<BufferId, Entity<Buffer>>,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -506,20 +508,14 @@ struct BufferState {
 
 struct DiffState {
     diff: Entity<BufferDiff>,
-    base_text_buffer_id: Option<BufferId>,
+    is_inverted: bool,
     _subscription: gpui::Subscription,
 }
 
 #[derive(Clone)]
 struct DiffStateSnapshot {
     diff: BufferDiffSnapshot,
-    base_text_buffer_id: Option<BufferId>,
-}
-
-impl DiffStateSnapshot {
-    fn is_inverted(&self) -> bool {
-        self.base_text_buffer_id.is_some()
-    }
+    is_inverted: bool,
 }
 
 // FIXME
@@ -548,13 +544,13 @@ impl DiffState {
                 _ => {}
             }),
             diff,
-            base_text_buffer_id: None,
+            is_inverted: false,
         }
     }
 
     fn new_inverted(
         diff: Entity<BufferDiff>,
-        base_text_buffer_id: BufferId,
+        base_text_buffer: Entity<Buffer>,
         cx: &mut Context<MultiBuffer>,
     ) -> Self {
         DiffState {
@@ -567,7 +563,7 @@ impl DiffState {
                         this.inverted_buffer_diff_changed(
                             diff,
                             base_text_changed_range,
-                            base_text_buffer_id,
+                            base_text_buffer.clone(),
                             cx,
                         )
                     }
@@ -578,7 +574,7 @@ impl DiffState {
                 _ => {}
             }),
             diff,
-            base_text_buffer_id: Some(base_text_buffer_id),
+            is_inverted: true,
         }
     }
 
@@ -607,12 +603,24 @@ pub struct MultiBufferSnapshot {
     show_headers: bool,
 }
 
+// follower: None
+// - BufferContent(Some)
+// - BufferContent(None)
+// - DeletedHunk
+//
+// follower: Some
+// - BufferContent(Some)
+// - BufferContent(None)
+
 #[derive(Debug, Clone)]
 enum DiffTransform {
+    // RealText
     BufferContent {
         summary: MBTextSummary,
+        // modified_hunk_info
         inserted_hunk_info: Option<DiffTransformHunkInfo>,
     },
+    // ExpandedHunkText
     DeletedHunk {
         summary: TextSummary,
         buffer_id: BufferId,
@@ -2322,27 +2330,25 @@ impl MultiBuffer {
         });
     }
 
+    // FIXME should take main_text_buffer_id right?
     fn inverted_buffer_diff_changed(
         &mut self,
         diff: Entity<BufferDiff>,
         diff_change_range: Range<usize>,
-        base_text_buffer_id: BufferId,
+        base_text_buffer: Entity<Buffer>,
         cx: &mut Context<Self>,
     ) {
-        let diff = diff.read(cx);
-        let new_diff = diff.snapshot(cx);
+        let base_text_buffer_id = base_text_buffer.read(cx).remote_id();
         let snapshot = self.snapshot.get_mut();
+        let new_diff = diff.read(cx).snapshot(cx);
         let base_text_changed = snapshot
             .diffs
             .get(&base_text_buffer_id)
             .is_none_or(|old_diff| !new_diff.base_texts_eq(old_diff));
         if base_text_changed {
-            let Some(base_text_buffer) = self.buffers.get(&base_text_buffer_id) else {
-                return;
-            };
-            base_text_buffer.buffer.update(cx, |buffer, cx| {
-                // FIXME take the rope directly
-                buffer.set_text(new_diff.base_text().text(), cx);
+            base_text_buffer.update(cx, |buffer, cx| {
+                // FIXME use the rope directly
+                buffer.set_text(diff.read(cx).base_text().text(), cx);
             });
         }
         self.sync_mut(cx);
@@ -2352,9 +2358,13 @@ impl MultiBuffer {
         };
         self.buffer_changed_since_sync.replace(true);
         let mut snapshot = self.snapshot.get_mut();
-        snapshot
-            .diffs
-            .insert_or_replace(base_text_buffer_id, new_diff);
+        snapshot.diffs.insert_or_replace(
+            base_text_buffer_id,
+            DiffStateSnapshot {
+                diff: new_diff,
+                is_inverted: true,
+            },
+        );
 
         let mut excerpt_edits = Vec::new();
         for locator in &buffer_state.excerpts {
@@ -2556,11 +2566,27 @@ impl MultiBuffer {
             text::Anchor::min_max_range_for_buffer(buffer_id),
             cx,
         );
-        self.diffs.insert(buffer_id, DiffState::new(diff, cx));
+        self.diffs
+            .insert(buffer_id, DiffState::new(diff.clone(), cx));
+
+        if let Some(follower) = &self.follower {
+            follower.update(cx, |follower, cx| {
+                let diff_change_range = 0..diff.read(cx).base_text().len();
+                let base_text_buffer: Entity<Buffer> = create_base_text_buffer(&diff);
+                follower.inverted_buffer_diff_changed(
+                    diff,
+                    diff_change_range,
+                    base_text_buffer.clone(),
+                    cx,
+                );
+                follower.diffs.insert(
+                    base_text_buffer.read(cx).remote_id(),
+                    DiffState::new_inverted(diff, base_text_buffer, cx),
+                );
+            });
+        }
     }
 
-    // FIXME add_inverted_diff
-
     pub fn diff_for(&self, buffer_id: BufferId) -> Option<Entity<BufferDiff>> {
         self.diffs.get(&buffer_id).map(|state| state.diff.clone())
     }
@@ -3226,7 +3252,7 @@ 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() {
+                if diff.is_inverted {
                     for hunk in
                         diff.hunks_intersecting_base_text_range(edit_buffer_start..edit_buffer_end)
                     {
@@ -3245,6 +3271,7 @@ impl MultiBuffer {
                             hunk_excerpt_start,
                             *end_of_current_insert,
                         );
+                        // FIXME record that the status for this region should be "deleted"
                         if !hunk_buffer_range.is_empty() {
                             let hunk_info = DiffTransformHunkInfo {
                                 excerpt_id: excerpt.id,

crates/multi_buffer/src/path_key.rs 🔗

@@ -6,7 +6,7 @@ use itertools::Itertools;
 use language::{Buffer, BufferSnapshot};
 use rope::Point;
 use text::{Bias, BufferId, OffsetRangeExt, locator::Locator};
-use util::{post_inc, rel_path::RelPath};

+use util::{debug_panic, post_inc, rel_path::RelPath};

 
 use crate::{
     Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, build_excerpt_ranges,
@@ -68,6 +68,26 @@ impl MultiBuffer {
         self.excerpts_by_path.keys()
     }
 
+    

+    // need:

+    // MultiBuffer::add_inverted_diff

+    // 

+    // SplittableEditor will handle:

+    // - creating diff base buffers

+    // - calling add_diff on one side and add_inverted_diff on the other side

+    // - calling set_excerpts_for_path on both sides (using the diff base buffers for the LHS)

+    //   - and translating excerpt ranges for the LHS

+    // - we have to make very sure that at all times, the sequence of excerpts on the two sides is the same

+    

+    // let b = Buffer::new(text);

+    // let mb = MutliBuffer::new([b1, b2, b3], ...);

+    // mb.attach_diff_hunks(...);

+    // let mb2 = mb.inverted();

+    //

+    // fn inverted(&self) -> Self {

+    //

+    // }

+

     /// Sets excerpts, returns `true` if at least one new excerpt was added.
     pub fn set_excerpts_for_path(
         &mut self,
@@ -153,6 +173,7 @@ impl MultiBuffer {
         }
     }
 
+    // FIXME need to sync excerpt removal to the follower

     pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
         self.remove_excerpts(
             self.excerpts_for_buffer(buffer, cx)
@@ -273,6 +294,14 @@ impl MultiBuffer {
         (result, added_a_new_excerpt)
     }
 
+    // SplittableEditor (SplitEditor | Editor)

+    //   - lhs: Editor

+    //     - mb: MultiBuffer

+    //   - rhs: Editor

+    //     - mb: MultiBuffer

+    //

+    // editor.rhs.mb.follower = Some(editor.lhs.mb)

+    // editor.lhs.mb.has_inverted_diffs = true

     fn update_path_excerpts(
         &mut self,
         path: PathKey,
@@ -293,7 +322,7 @@ impl MultiBuffer {
             .get(&path)
             .cloned()
             .unwrap_or_default();
-        let mut new_iter = new.into_iter().peekable();

+        let mut new_iter = new.iter().cloned().peekable();

         let mut existing_iter = existing.into_iter().peekable();
 
         let mut excerpt_ids = Vec::new();
@@ -427,7 +456,47 @@ impl MultiBuffer {
             let snapshot = &*self.snapshot.get_mut();
             let mut excerpt_ids: Vec<_> = excerpt_ids.iter().dedup().cloned().collect();
             excerpt_ids.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
-            self.excerpts_by_path.insert(path, excerpt_ids);

+            self.excerpts_by_path.insert(path.clone(), excerpt_ids);

+        }

+

+        if let Some(follower) = &self.follower {

+            if let Some(diff) = snapshot.diffs.get(&buffer_snapshot.remote_id()) {

+                follower.update(cx, |follower, cx| {

+                    let Some(base_text_buffer) = follower

+                        .base_text_buffers_by_main_buffer_id

+                        .get(&buffer_snapshot.remote_id())

+                        .cloned()

+                    else {

+                        return;

+                    };

+                    let new = new

+                        .into_iter()

+                        .map(|range| {

+                            let point_to_base_text_point = |point: Point| {

+                                let row = diff.row_to_base_text_row(point.row, buffer_snapshot);

+                                let column = diff.base_text().line_len(row);

+                                Point::new(row, column)

+                            };

+                            ExcerptRange {

+                                primary: point_to_base_text_point(range.primary.start)

+                                    ..point_to_base_text_point(range.primary.end),

+                                context: point_to_base_text_point(range.context.start)

+                                    ..point_to_base_text_point(range.context.end),

+                            }

+                        })

+                        .collect();

+                    let base_text_buffer_snapshot = base_text_buffer.read(cx).snapshot();

+                    follower.update_path_excerpts(

+                        path,

+                        base_text_buffer,

+                        &base_text_buffer_snapshot,

+                        new,

+                        cx,

+                    );

+                });

+            } else {

+                // FIXME

+            }

         }
 
         (excerpt_ids, added_a_new_excerpt)