wip

Cole Miller created

Change summary

crates/buffer_diff/src/buffer_diff.rs         |   2 
crates/editor/src/split.rs                    | 460 +++++++-------------
crates/multi_buffer/src/multi_buffer.rs       |   2 
crates/multi_buffer/src/multi_buffer_tests.rs |   8 
crates/multi_buffer/src/path_key.rs           |   4 
5 files changed, 174 insertions(+), 302 deletions(-)

Detailed changes

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -1400,7 +1400,7 @@ impl BufferDiff {
     }
 
     #[cfg(any(test, feature = "test-support"))]
-    pub fn recalculate_diff_sync(&mut self, buffer: text::BufferSnapshot, cx: &mut Context<Self>) {
+    pub fn recalculate_diff_sync(&mut self, buffer: &text::BufferSnapshot, cx: &mut Context<Self>) {
         let language = self.base_text(cx).language().cloned();
         let base_text = self.base_text_string(cx).map(|s| s.as_str().into());
         let fut = self.update_diff(buffer.clone(), base_text, false, language, cx);

crates/editor/src/split.rs 🔗

@@ -39,6 +39,7 @@ struct SplitDiff;
 struct UnsplitDiff;
 
 pub struct SplittableEditor {
+    primary_multibuffer: Entity<MultiBuffer>,
     primary_editor: Entity<Editor>,
     secondary: Option<SecondaryEditor>,
     panes: PaneGroup,
@@ -47,6 +48,7 @@ pub struct SplittableEditor {
 }
 
 struct SecondaryEditor {
+    multibuffer: Entity<MultiBuffer>,
     editor: Entity<Editor>,
     pane: Entity<Pane>,
     has_latest_selection: bool,
@@ -69,14 +71,20 @@ impl SplittableEditor {
     }
 
     pub fn new_unsplit(
-        buffer: Entity<MultiBuffer>,
+        primary_multibuffer: Entity<MultiBuffer>,
         project: Entity<Project>,
         workspace: Entity<Workspace>,
         window: &mut Window,
         cx: &mut Context<Self>,
     ) -> Self {
-        let primary_editor =
-            cx.new(|cx| Editor::for_multibuffer(buffer, Some(project.clone()), window, cx));
+        let primary_editor = cx.new(|cx| {
+            Editor::for_multibuffer(
+                primary_multibuffer.clone(),
+                Some(project.clone()),
+                window,
+                cx,
+            )
+        });
         let pane = cx.new(|cx| {
             let mut pane = Pane::new(
                 workspace.downgrade(),
@@ -121,6 +129,7 @@ impl SplittableEditor {
         });
         Self {
             primary_editor,
+            primary_multibuffer,
             secondary: None,
             panes,
             workspace: workspace.downgrade(),
@@ -140,14 +149,18 @@ impl SplittableEditor {
         };
         let project = workspace.read(cx).project().clone();
 
+        let secondary_multibuffer = cx.new(|cx| {
+            let mut multibuffer = MultiBuffer::new(Capability::ReadOnly);
+            multibuffer.set_all_diff_hunks_expanded(cx);
+            multibuffer
+        });
         let secondary_editor = cx.new(|cx| {
-            let multibuffer = cx.new(|cx| {
-                let mut multibuffer = MultiBuffer::new(Capability::ReadOnly);
-                multibuffer.set_all_diff_hunks_expanded(cx);
-                multibuffer
-            });
-            let mut editor =
-                Editor::for_multibuffer(multibuffer, Some(project.clone()), window, cx);
+            let mut editor = Editor::for_multibuffer(
+                secondary_multibuffer.clone(),
+                Some(project.clone()),
+                window,
+                cx,
+            );
             editor.number_deleted_lines = true;
             editor
         });
@@ -187,6 +200,7 @@ impl SplittableEditor {
             ];
         let mut secondary = SecondaryEditor {
             editor: secondary_editor,
+            multibuffer: secondary_multibuffer,
             pane: secondary_pane.clone(),
             has_latest_selection: false,
             _subscriptions: subscriptions,
@@ -273,61 +287,104 @@ impl SplittableEditor {
         })
     }
 
-    fn expand_primary_excerpts(
+    /// Expands excerpts in both sides.
+    ///
+    /// While the left multibuffer does have separate excerpts with separate
+    /// IDs, this is an implementation detail. We do not expose the left excerpt
+    /// IDs in the public API of [`SplittableEditor`].
+    pub fn expand_excerpts(
         &mut self,
         excerpt_ids: impl Iterator<Item = ExcerptId> + Clone,
         lines: u32,
         direction: ExpandExcerptDirection,
         cx: &mut Context<Self>,
     ) {
-        self.update_primary_multibuffer(cx, |multibuffer, cx| {
+        self.primary_multibuffer.update(cx, |multibuffer, cx| {
             multibuffer.expand_excerpts(excerpt_ids.clone(), lines, direction, cx);
         });
-        let paths: Vec<PathKey> = excerpt_ids
+        let paths: Vec<(ExcerptId, PathKey)> = excerpt_ids
             .flat_map(|excerpt_id| {
-                self.primary_multibuffer(cx)
+                let path = self
+                    .primary_multibuffer
+                    .read(cx)
                     .path_for_excerpt(excerpt_id)
-                    .cloned()
+                    .cloned()?;
+                Some((excerpt_id, path))
             })
             .collect();
 
-        if let Some(secondary) = &self.secondary {
-            self.update_primary_multibuffer(cx, |multibuffer, cx| {
-                let snapshot = primary_multibuffer.snapshot(cx);
-                for path in paths {
-                    let buffer = snapshot.buffer_for_excerpt(excerpt_id).unwrap();
-                    let diff = primary_multibuffer.diff_for(buffer.remote_id()).unwrap();
-                    secondary.sync_path_excerpts(path, multibuffer, diff, cx);
-                }
+        if let Some(secondary) = &mut self.secondary {
+            self.primary_editor.update(cx, |editor, cx| {
+                editor.buffer().update(cx, |multibuffer, cx| {
+                    let snapshot = multibuffer.snapshot(cx);
+                    for (excerpt_id, path) in paths {
+                        let buffer = snapshot.buffer_for_excerpt(excerpt_id).unwrap();
+                        let diff = multibuffer.diff_for(buffer.remote_id()).unwrap();
+                        secondary.sync_path_excerpts(path, multibuffer, diff, cx);
+                    }
+                })
             })
         }
     }
 
-    fn primary_multibuffer<'a>(&'a self, cx: &'a Context<Self>) -> &'a MultiBuffer {
-        self.primary_editor.read(cx).buffer.read(cx)
-    }
-
-    fn update_primary_multibuffer<R>(
-        &mut self,
-        cx: &mut Context<Self>,
-        f: impl FnOnce(&mut MultiBuffer, &mut Context<MultiBuffer>) -> R,
-    ) -> R {
-        self.primary_editor
-            .update(cx, |editor, cx| editor.buffer().update(cx, f))
+    pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
+        self.primary_multibuffer.update(cx, |buffer, cx| {
+            buffer.remove_excerpts_for_path(path.clone(), cx)
+        });
+        if let Some(secondary) = &self.secondary {
+            secondary
+                .multibuffer
+                .update(cx, |buffer, cx| buffer.remove_excerpts_for_path(path, cx))
+        }
     }
+}
 
-    #[cfg(test)]
+#[cfg(test)]
+impl SplittableEditor {
     fn check_invariants(&self, cx: &App) {
-        todo!()
+        let Some(secondary) = &self.secondary else {
+            return;
+        };
+
+        let primary_excerpts = self.primary_multibuffer.read(cx).excerpt_ids();
+        let secondary_excerpts = secondary.multibuffer.read(cx).excerpt_ids();
+        assert_eq!(primary_excerpts.len(), secondary_excerpts.len());
+
+        // self.primary_multibuffer.read(cx).check_invariants(cx);
+        // secondary.multibuffer.read(cx).check_invariants(cx);
+        // Assertions:...
+        //
+        // left.display_lines().filter(is_unmodified) == right.display_lines().filter(is_unmodified)
+        //
+        // left excerpts and right excerpts bijectivity
+        //
+        //
+
+        // let primary_buffer_text = self
+        //     .primary_multibuffer
+        //     .read(cx)
+        //     .text_summary_for_range(Anchor::min()..Anchor::max());
+        // let secondary_buffer_text = secondary
+        //     .multibuffer
+        //     .read(cx)
+        //     .text_summary_for_range(Anchor::min()..Anchor::max());
+        // let primary_buffer_base_text = self
+        //     .primary_multibuffer
+        //     .read(cx)
+        //     .base_text_summary_for_range(Anchor::min()..Anchor::max());
+        // let secondary_buffer_base_text = secondary
+        //     .multibuffer
+        //     .read(cx)
+        //     .base_text_summary_for_range(Anchor::min()..Anchor::max());
     }
 
-    #[cfg(test)]
     fn randomly_edit_excerpts(
         &mut self,
         rng: &mut impl rand::Rng,
         mutation_count: usize,
         cx: &mut Context<Self>,
     ) {
+        use collections::HashSet;
         use rand::prelude::*;
         use std::env;
         use util::RandomCharIter;
@@ -336,19 +393,21 @@ impl SplittableEditor {
             .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable"))
             .unwrap_or(5);
 
-        let excerpt_ids = self.primary_multibuffer(cx).excerpt_ids();
+        let paths = self
+            .primary_multibuffer
+            .read(cx)
+            .paths()
+            .collect::<Vec<_>>();
+        let excerpt_ids = self.primary_multibuffer.read(cx).excerpt_ids();
 
-        let mut buffers = Vec::new();
         for _ in 0..mutation_count {
             if rng.random_bool(0.05) {
                 log::info!("Clearing multi-buffer");
-                self.update_primary_multibuffer(cx, |multibuffer, cx| {
+                self.primary_multibuffer.update(cx, |multibuffer, cx| {
                     multibuffer.clear(cx);
                 });
                 continue;
             } else if rng.random_bool(0.1) && !excerpt_ids.is_empty() {
-                use collections::HashSet;
-
                 let mut excerpts = HashSet::default();
                 for _ in 0..rng.random_range(0..excerpt_ids.len()) {
                     excerpts.extend(excerpt_ids.choose(rng).copied());
@@ -358,8 +417,7 @@ impl SplittableEditor {
 
                 log::info!("Expanding excerpts {excerpts:?} by {line_count} lines");
 
-                // FIXME we need an expand_excerpts API on the splittable editor that does a sync
-                self.expand_primary_excerpts(
+                self.expand_excerpts(
                     excerpts.iter().cloned(),
                     line_count,
                     ExpandExcerptDirection::UpAndDown,
@@ -369,58 +427,50 @@ impl SplittableEditor {
             }
 
             if excerpt_ids.is_empty() || (rng.random() && excerpt_ids.len() < max_excerpts) {
-                let buffer_handle = if rng.random() || self.buffers.is_empty() {
-                    let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
-                    buffers.push(cx.new(|cx| Buffer::local(text, cx)));
-                    let buffer = buffers.last().unwrap().read(cx);
+                let existing_buffers = self.primary_multibuffer.read(cx).all_buffers();
+                let buffer = if rng.random() || existing_buffers.is_empty() {
+                    let len = rng.random_range(0..500);
+                    let text = RandomCharIter::new(&mut *rng).take(len).collect::<String>();
+                    let buffer = cx.new(|cx| Buffer::local(text, cx));
                     log::info!(
                         "Creating new buffer {} with text: {:?}",
-                        buffer.remote_id(),
-                        buffer.text()
+                        buffer.read(cx).remote_id(),
+                        buffer.read(cx).text()
                     );
-                    buffers.last().unwrap().clone()
+                    buffer
                 } else {
-                    self.buffers.values().choose(rng).unwrap().buffer.clone()
+                    existing_buffers.iter().choose(rng).unwrap().clone()
                 };
 
-                let buffer = buffer_handle.read(cx);
-                let buffer_text = buffer.text();
-                let ranges = (0..rng.random_range(0..5))
-                    .map(|_| {
-                        let end_ix =
-                            buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Right);
-                        let start_ix = buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
-                        ExcerptRange::new(start_ix..end_ix)
-                    })
-                    .collect::<Vec<_>>();
-                log::info!(
-                    "Inserting excerpts from buffer {} and ranges {:?}: {:?}",
-                    buffer_handle.read(cx).remote_id(),
-                    ranges.iter().map(|r| &r.context).collect::<Vec<_>>(),
-                    ranges
-                        .iter()
-                        .map(|r| &buffer_text[r.context.clone()])
+                let buffer_snapshot = buffer.read(cx).snapshot();
+                let diff = cx.new(|cx| BufferDiff::new_unchanged(&buffer_snapshot, cx));
+                // Create some initial diff hunks.
+                buffer.update(cx, |buffer, cx| {
+                    buffer.randomly_edit(rng, 2, cx);
+                });
+                let buffer_snapshot = buffer.read(cx).text_snapshot();
+                let ranges = diff.update(cx, |diff, cx| {
+                    diff.recalculate_diff_sync(&buffer_snapshot, cx);
+                    diff.snapshot(cx)
+                        .hunks(&buffer_snapshot)
+                        .map(|hunk| hunk.range.clone())
                         .collect::<Vec<_>>()
-                );
-
-                let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx);
-                log::info!("Inserted with ids: {:?}", excerpt_id);
+                });
+                let path = PathKey::for_buffer(&buffer, cx);
+                self.set_excerpts_for_path(path, buffer, ranges, 2, diff, cx);
             } else {
-                let remove_count = rng.random_range(1..=excerpt_ids.len());
-                let mut excerpts_to_remove = excerpt_ids
+                let remove_count = rng.random_range(1..=paths.len());
+                let paths_to_remove = paths
                     .choose_multiple(rng, remove_count)
                     .cloned()
                     .collect::<Vec<_>>();
-                let snapshot = self.snapshot.borrow();
-                excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &snapshot));
-                drop(snapshot);
-                log::info!("Removing excerpts {:?}", excerpts_to_remove);
-                self.remove_excerpts(excerpts_to_remove, cx);
+                for path in paths_to_remove {
+                    self.remove_excerpts_for_path(path, cx);
+                }
             }
         }
     }
 
-    #[cfg(test)]
     fn randomly_mutate(
         &mut self,
         rng: &mut impl rand::Rng,
@@ -442,12 +492,31 @@ impl SplittableEditor {
                     }
                 });
             } else {
-                self.update_primary_multibuffer(cx, |multibuffer, cx| {
+                self.primary_multibuffer.update(cx, |multibuffer, cx| {
                     multibuffer.randomly_edit(rng, mutation_count, cx);
                 });
             }
-        } else {
+        } else if rng.random() {
             self.randomly_edit_excerpts(rng, mutation_count, cx);
+        } else {
+            for buffer in self.primary_multibuffer.read(cx).all_buffers() {
+                let diff = self
+                    .primary_multibuffer
+                    .read(cx)
+                    .diff_for(buffer.read(cx).remote_id())
+                    .unwrap();
+                let buffer_snapshot = buffer.read(cx).text_snapshot();
+                diff.update(cx, |diff, cx| {
+                    diff.recalculate_diff_sync(&buffer_snapshot, cx);
+                });
+                // TODO(split-diff) might be a good idea to try to separate the diff recalculation from the excerpt recalculation
+                let diff_snapshot = diff.read(cx).snapshot(cx);
+                let ranges = diff_snapshot
+                    .hunks(&buffer_snapshot)
+                    .map(|hunk| hunk.range.clone());
+                let path = PathKey::for_buffer(&buffer, cx);
+                self.set_excerpts_for_path(path, buffer, ranges, 2, diff, cx);
+            }
         }
 
         self.check_invariants(cx);
@@ -516,16 +585,15 @@ impl SecondaryEditor {
                 let point_range_to_base_text_point_range = |range: Range<Point>| {
                     let start_row =
                         diff_snapshot.row_to_base_text_row(range.start.row, main_buffer);
-                    let start_column = 0;
                     let end_row = diff_snapshot.row_to_base_text_row(range.end.row, main_buffer);
                     let end_column = diff_snapshot.base_text().line_len(end_row);
-                    Point::new(start_row, start_column)..Point::new(end_row, end_column)
+                    Point::new(start_row, 0)..Point::new(end_row, end_column)
                 };
                 let primary = excerpt_range.primary.to_point(main_buffer);
                 let context = excerpt_range.context.to_point(main_buffer);
                 ExcerptRange {
-                    primary: point_range_to_base_text_point_range(primary),
-                    context: point_range_to_base_text_point_range(context),
+                    primary: point_range_to_base_text_point_range(dbg!(primary)),
+                    context: point_range_to_base_text_point_range(dbg!(context)),
                 }
             })
             .collect();
@@ -556,21 +624,25 @@ mod tests {
     use language::{Buffer, Capability};
     use multi_buffer::MultiBuffer;
     use project::Project;
-    use rand::rngs::StdRng;
+    use rand::{Rng, rngs::StdRng};
     use settings::SettingsStore;
     use ui::VisualContext as _;
     use workspace::Workspace;
 
     use crate::SplittableEditor;
 
-    #[gpui::test]
-    async fn test_basic_excerpts(mut rng: StdRng, cx: &mut gpui::TestAppContext) {
+    fn init_test(cx: &mut gpui::TestAppContext) {
         cx.update(|cx| {
             let store = SettingsStore::test(cx);
             cx.set_global(store);
             theme::init(theme::LoadThemes::JustBase, cx);
             crate::init(cx);
         });
+    }
+
+    #[gpui::test]
+    async fn test_basic_excerpts(mut rng: StdRng, cx: &mut gpui::TestAppContext) {
+        init_test(cx);
         let base_text = indoc! {"
             hello
         "};
@@ -589,9 +661,9 @@ mod tests {
             SplittableEditor::new_unsplit(multibuffer, project, workspace, window, cx)
         });
 
-        let mutation_count = rng.gen_range(0..100);
+        let mutation_count = rng.random_range(0..100);
         editor.update(cx, |editor, cx| {
-            editor.randomly_mutate(rng, mutation_count, cx);
+            editor.randomly_mutate(&mut rng, mutation_count, cx);
         })
 
         // for _ in 0..random() {
@@ -605,208 +677,4 @@ mod tests {
 
         // editor.read(cx).primary_editor().read(cx).display_map.read(cx)
     }
-
-    // MultiB
-
-    // FIXME restore these tests in some form
-    // #[gpui::test]
-    // async fn test_filtered_editor_pair(cx: &mut gpui::TestAppContext) {
-    //     init_test(cx, |_| {});
-    //     let mut leader_cx = EditorTestContext::new(cx).await;
-
-    //     let diff_base = indoc!(
-    //         r#"
-    //         one
-    //         two
-    //         three
-    //         four
-    //         five
-    //         six
-    //         "#
-    //     );
-
-    //     let initial_state = indoc!(
-    //         r#"
-    //         ˇone
-    //         two
-    //         THREE
-    //         four
-    //         five
-    //         six
-    //         "#
-    //     );
-
-    //     leader_cx.set_state(initial_state);
-
-    //     leader_cx.set_head_text(&diff_base);
-    //     leader_cx.run_until_parked();
-
-    //     let follower = leader_cx.update_multibuffer(|leader, cx| {
-    //         leader.set_filter_mode(Some(MultiBufferFilterMode::KeepInsertions));
-    //         leader.set_all_diff_hunks_expanded(cx);
-    //         leader.get_or_create_follower(cx)
-    //     });
-    //     follower.update(cx, |follower, cx| {
-    //         follower.set_filter_mode(Some(MultiBufferFilterMode::KeepDeletions));
-    //         follower.set_all_diff_hunks_expanded(cx);
-    //     });
-
-    //     let follower_editor =
-    //         leader_cx.new_window_entity(|window, cx| build_editor(follower, window, cx));
-    //     // leader_cx.window.focus(&follower_editor.focus_handle(cx));
-
-    //     let mut follower_cx = EditorTestContext::for_editor_in(follower_editor, &mut leader_cx).await;
-    //     cx.run_until_parked();
-
-    //     leader_cx.assert_editor_state(initial_state);
-    //     follower_cx.assert_editor_state(indoc! {
-    //         r#"
-    //         ˇone
-    //         two
-    //         three
-    //         four
-    //         five
-    //         six
-    //         "#
-    //     });
-
-    //     follower_cx.editor(|editor, _window, cx| {
-    //         assert!(editor.read_only(cx));
-    //     });
-
-    //     leader_cx.update_editor(|editor, _window, cx| {
-    //         editor.edit([(Point::new(4, 0)..Point::new(5, 0), "FIVE\n")], cx);
-    //     });
-    //     cx.run_until_parked();
-
-    //     leader_cx.assert_editor_state(indoc! {
-    //         r#"
-    //         ˇone
-    //         two
-    //         THREE
-    //         four
-    //         FIVE
-    //         six
-    //         "#
-    //     });
-
-    //     follower_cx.assert_editor_state(indoc! {
-    //         r#"
-    //         ˇone
-    //         two
-    //         three
-    //         four
-    //         five
-    //         six
-    //         "#
-    //     });
-
-    //     leader_cx.update_editor(|editor, _window, cx| {
-    //         editor.edit([(Point::new(6, 0)..Point::new(6, 0), "SEVEN")], cx);
-    //     });
-    //     cx.run_until_parked();
-
-    //     leader_cx.assert_editor_state(indoc! {
-    //         r#"
-    //         ˇone
-    //         two
-    //         THREE
-    //         four
-    //         FIVE
-    //         six
-    //         SEVEN"#
-    //     });
-
-    //     follower_cx.assert_editor_state(indoc! {
-    //         r#"
-    //         ˇone
-    //         two
-    //         three
-    //         four
-    //         five
-    //         six
-    //         "#
-    //     });
-
-    //     leader_cx.update_editor(|editor, window, cx| {
-    //         editor.move_down(&MoveDown, window, cx);
-    //         editor.refresh_selected_text_highlights(true, window, cx);
-    //     });
-    //     leader_cx.run_until_parked();
-    // }
-
-    // #[gpui::test]
-    // async fn test_filtered_editor_pair_complex(cx: &mut gpui::TestAppContext) {
-    //     init_test(cx, |_| {});
-    //     let base_text = "base\n";
-    //     let buffer_text = "buffer\n";
-
-    //     let buffer1 = cx.new(|cx| Buffer::local(buffer_text, cx));
-    //     let diff1 = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer1, cx));
-
-    //     let extra_buffer_1 = cx.new(|cx| Buffer::local("dummy text 1\n", cx));
-    //     let extra_diff_1 = cx.new(|cx| BufferDiff::new_with_base_text("", &extra_buffer_1, cx));
-    //     let extra_buffer_2 = cx.new(|cx| Buffer::local("dummy text 2\n", cx));
-    //     let extra_diff_2 = cx.new(|cx| BufferDiff::new_with_base_text("", &extra_buffer_2, cx));
-
-    //     let leader = cx.new(|cx| {
-    //         let mut leader = MultiBuffer::new(Capability::ReadWrite);
-    //         leader.set_all_diff_hunks_expanded(cx);
-    //         leader.set_filter_mode(Some(MultiBufferFilterMode::KeepInsertions));
-    //         leader
-    //     });
-    //     let follower = leader.update(cx, |leader, cx| leader.get_or_create_follower(cx));
-    //     follower.update(cx, |follower, _| {
-    //         follower.set_filter_mode(Some(MultiBufferFilterMode::KeepDeletions));
-    //     });
-
-    //     leader.update(cx, |leader, cx| {
-    //         leader.insert_excerpts_after(
-    //             ExcerptId::min(),
-    //             extra_buffer_2.clone(),
-    //             vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
-    //             cx,
-    //         );
-    //         leader.add_diff(extra_diff_2.clone(), cx);
-
-    //         leader.insert_excerpts_after(
-    //             ExcerptId::min(),
-    //             extra_buffer_1.clone(),
-    //             vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
-    //             cx,
-    //         );
-    //         leader.add_diff(extra_diff_1.clone(), cx);
-
-    //         leader.insert_excerpts_after(
-    //             ExcerptId::min(),
-    //             buffer1.clone(),
-    //             vec![ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
-    //             cx,
-    //         );
-    //         leader.add_diff(diff1.clone(), cx);
-    //     });
-
-    //     cx.run_until_parked();
-    //     let mut cx = cx.add_empty_window();
-
-    //     let leader_editor = cx
-    //         .new_window_entity(|window, cx| Editor::for_multibuffer(leader.clone(), None, window, cx));
-    //     let follower_editor = cx.new_window_entity(|window, cx| {
-    //         Editor::for_multibuffer(follower.clone(), None, window, cx)
-    //     });
-
-    //     let mut leader_cx = EditorTestContext::for_editor_in(leader_editor.clone(), &mut cx).await;
-    //     leader_cx.assert_editor_state(indoc! {"
-    //        ˇbuffer
-
-    //        dummy text 1
-
-    //        dummy text 2
-    //     "});
-    //     let mut follower_cx = EditorTestContext::for_editor_in(follower_editor.clone(), &mut cx).await;
-    //     follower_cx.assert_editor_state(indoc! {"
-    //         ˇbase
-
-    //     "});
-    // }
 }

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -3708,7 +3708,7 @@ impl MultiBuffer {
         self.check_invariants(cx);
     }
 
-    pub fn check_invariants(&self, cx: &App) {
+    fn check_invariants(&self, cx: &App) {
         self.read(cx).check_invariants();
     }
 }

crates/multi_buffer/src/multi_buffer_tests.rs 🔗

@@ -994,7 +994,7 @@ async fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
     buffer.update(cx, |buffer, cx| {
         buffer.edit([(0..0, "a\nb\nc")], None, cx);
         diff.update(cx, |diff, cx| {
-            diff.recalculate_diff_sync(buffer.snapshot().text, cx);
+            diff.recalculate_diff_sync(&buffer.text_snapshot(), cx);
         });
         assert_eq!(buffer.text(), "a\nb\nc")
     });
@@ -1006,7 +1006,7 @@ async fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
     buffer.update(cx, |buffer, cx| {
         buffer.undo(cx);
         diff.update(cx, |diff, cx| {
-            diff.recalculate_diff_sync(buffer.snapshot().text, cx);
+            diff.recalculate_diff_sync(&buffer.text_snapshot(), cx);
         });
         assert_eq!(buffer.text(), "")
     });
@@ -1507,7 +1507,7 @@ async fn test_basic_diff_hunks(cx: &mut TestAppContext) {
 
     // Recalculate the diff, changing the first diff hunk.
     diff.update(cx, |diff, cx| {
-        diff.recalculate_diff_sync(buffer.read(cx).text_snapshot(), cx);
+        diff.recalculate_diff_sync(&buffer.read(cx).text_snapshot(), cx);
     });
     cx.run_until_parked();
     assert_new_snapshot(
@@ -2898,7 +2898,7 @@ async fn test_random_multibuffer_impl(
                                     "recalculating diff for buffer {:?}",
                                     snapshot.remote_id(),
                                 );
-                                diff.recalculate_diff_sync(snapshot.text, cx);
+                                diff.recalculate_diff_sync(&snapshot.text, cx);
                             },
                         );
                     }

crates/multi_buffer/src/path_key.rs 🔗

@@ -46,6 +46,10 @@ impl MultiBuffer {
         self.excerpts_by_path.keys().cloned()
     }
 
+    pub fn path_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<&PathKey> {

+        self.paths_by_excerpt.get(&excerpt_id)

+    }

+

     pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
         if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
             self.remove_excerpts(to_remove, cx)