Use new multibuffer excerpts in find-all-references and friends (#27876)

Conrad Irwin and Kirill Bulatov created

Release Notes:

- N/A

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>

Change summary

crates/assistant2/src/assistant_diff.rs       |  4 
crates/editor/src/editor.rs                   | 36 +++++----
crates/editor/src/editor_tests.rs             |  2 
crates/git_ui/src/commit_view.rs              |  4 
crates/git_ui/src/project_diff.rs             |  8 +-
crates/multi_buffer/src/multi_buffer.rs       | 68 +++++---------------
crates/multi_buffer/src/multi_buffer_tests.rs | 71 ++------------------
7 files changed, 54 insertions(+), 139 deletions(-)

Detailed changes

crates/assistant2/src/assistant_diff.rs 🔗

@@ -135,7 +135,7 @@ impl AssistantDiff {
                 continue;
             };
 
-            let path_key = PathKey::namespaced("", file.full_path(cx).into());
+            let path_key = PathKey::namespaced(0, file.full_path(cx).into());
             paths_to_delete.remove(&path_key);
 
             let snapshot = buffer.read(cx).snapshot();
@@ -152,7 +152,7 @@ impl AssistantDiff {
             let (was_empty, is_excerpt_newly_added) =
                 self.multibuffer.update(cx, |multibuffer, cx| {
                     let was_empty = multibuffer.is_empty();
-                    let is_excerpt_newly_added = multibuffer.set_excerpts_for_path(
+                    let (_, is_excerpt_newly_added) = multibuffer.set_excerpts_for_path(
                         path_key.clone(),
                         buffer.clone(),
                         diff_hunk_ranges,

crates/editor/src/editor.rs 🔗

@@ -147,7 +147,7 @@ pub use multi_buffer::{
 };
 use multi_buffer::{
     ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
-    MultiOrSingleBufferOffsetRange, ToOffsetUtf16,
+    MultiOrSingleBufferOffsetRange, PathKey, ToOffsetUtf16,
 };
 use parking_lot::Mutex;
 use project::{
@@ -5035,17 +5035,19 @@ impl Editor {
         let excerpt_buffer = cx.new(|cx| {
             let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
             for (buffer_handle, transaction) in &entries {
-                let buffer = buffer_handle.read(cx);
-                ranges_to_highlight.extend(
-                    multibuffer.push_excerpts_with_context_lines(
-                        buffer_handle.clone(),
-                        buffer
-                            .edited_ranges_for_transaction::<usize>(transaction)
-                            .collect(),
-                        DEFAULT_MULTIBUFFER_CONTEXT,
-                        cx,
-                    ),
+                let edited_ranges = buffer_handle
+                    .read(cx)
+                    .edited_ranges_for_transaction::<Point>(transaction)
+                    .collect::<Vec<_>>();
+                let (ranges, _) = multibuffer.set_excerpts_for_path(
+                    PathKey::for_buffer(buffer_handle, cx),
+                    buffer_handle.clone(),
+                    edited_ranges,
+                    DEFAULT_MULTIBUFFER_CONTEXT,
+                    cx,
                 );
+
+                ranges_to_highlight.extend(ranges);
             }
             multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
             multibuffer
@@ -13531,7 +13533,7 @@ impl Editor {
         // If there are multiple definitions, open them in a multibuffer
         locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
         let mut locations = locations.into_iter().peekable();
-        let mut ranges = Vec::new();
+        let mut ranges: Vec<Range<Anchor>> = Vec::new();
         let capability = workspace.project().read(cx).capability();
 
         let excerpt_buffer = cx.new(|cx| {
@@ -13539,12 +13541,12 @@ impl Editor {
             while let Some(location) = locations.next() {
                 let buffer = location.buffer.read(cx);
                 let mut ranges_for_buffer = Vec::new();
-                let range = location.range.to_offset(buffer);
+                let range = location.range.to_point(buffer);
                 ranges_for_buffer.push(range.clone());
 
                 while let Some(next_location) = locations.peek() {
                     if next_location.buffer == location.buffer {
-                        ranges_for_buffer.push(next_location.range.to_offset(buffer));
+                        ranges_for_buffer.push(next_location.range.to_point(buffer));
                         locations.next();
                     } else {
                         break;
@@ -13552,12 +13554,14 @@ impl Editor {
                 }
 
                 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
-                ranges.extend(multibuffer.push_excerpts_with_context_lines(
+                let (new_ranges, _) = multibuffer.set_excerpts_for_path(
+                    PathKey::for_buffer(&location.buffer, cx),
                     location.buffer.clone(),
                     ranges_for_buffer,
                     DEFAULT_MULTIBUFFER_CONTEXT,
                     cx,
-                ))
+                );
+                ranges.extend(new_ranges)
             }
 
             multibuffer.with_title(title)

crates/editor/src/editor_tests.rs 🔗

@@ -15824,7 +15824,7 @@ async fn test_display_diff_hunks(cx: &mut TestAppContext) {
         for buffer in &buffers {
             let snapshot = buffer.read(cx).snapshot();
             multibuffer.set_excerpts_for_path(
-                PathKey::namespaced("", buffer.read(cx).file().unwrap().path().clone()),
+                PathKey::namespaced(0, buffer.read(cx).file().unwrap().path().clone()),
                 buffer.clone(),
                 vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
                 DEFAULT_MULTIBUFFER_CONTEXT,

crates/git_ui/src/commit_view.rs 🔗

@@ -44,8 +44,8 @@ struct CommitMetadataFile {
     worktree_id: WorktreeId,
 }
 
-const COMMIT_METADATA_NAMESPACE: &'static str = "0";
-const FILE_NAMESPACE: &'static str = "1";
+const COMMIT_METADATA_NAMESPACE: u32 = 0;
+const FILE_NAMESPACE: u32 = 1;
 
 impl CommitView {
     pub fn open(

crates/git_ui/src/project_diff.rs 🔗

@@ -61,9 +61,9 @@ struct DiffBuffer {
     file_status: FileStatus,
 }
 
-const CONFLICT_NAMESPACE: &'static str = "0";
-const TRACKED_NAMESPACE: &'static str = "1";
-const NEW_NAMESPACE: &'static str = "2";
+const CONFLICT_NAMESPACE: u32 = 0;
+const TRACKED_NAMESPACE: u32 = 1;
+const NEW_NAMESPACE: u32 = 2;
 
 impl ProjectDiff {
     pub(crate) fn register(workspace: &mut Workspace, cx: &mut Context<Workspace>) {
@@ -404,7 +404,7 @@ impl ProjectDiff {
 
         let (was_empty, is_excerpt_newly_added) = self.multibuffer.update(cx, |multibuffer, cx| {
             let was_empty = multibuffer.is_empty();
-            let is_newly_added = multibuffer.set_excerpts_for_path(
+            let (_, is_newly_added) = multibuffer.set_excerpts_for_path(
                 path_key.clone(),
                 buffer,
                 diff_hunk_ranges,

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -38,7 +38,7 @@ use std::{
     iter::{self, FromIterator},
     mem,
     ops::{Range, RangeBounds, Sub},
-    path::Path,
+    path::{Path, PathBuf},
     rc::Rc,
     str,
     sync::Arc,
@@ -167,15 +167,23 @@ impl MultiBufferDiffHunk {
 
 #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
 pub struct PathKey {
-    namespace: &'static str,
+    namespace: u32,
     path: Arc<Path>,
 }
 
 impl PathKey {
-    pub fn namespaced(namespace: &'static str, path: Arc<Path>) -> Self {
+    pub fn namespaced(namespace: u32, path: Arc<Path>) -> Self {
         Self { namespace, path }
     }
 
+    pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
+        if let Some(file) = buffer.read(cx).file() {
+            Self::namespaced(1, Arc::from(file.full_path(cx)))
+        } else {
+            Self::namespaced(0, Arc::from(PathBuf::from(buffer.entity_id().to_string())))
+        }
+    }
+
     pub fn path(&self) -> &Arc<Path> {
         &self.path
     }
@@ -1448,45 +1456,6 @@ impl MultiBuffer {
         self.insert_excerpts_after(ExcerptId::max(), buffer, ranges, cx)
     }
 
-    pub fn push_excerpts_with_context_lines<O>(
-        &mut self,
-        buffer: Entity<Buffer>,
-        ranges: Vec<Range<O>>,
-        context_line_count: u32,
-        cx: &mut Context<Self>,
-    ) -> Vec<Range<Anchor>>
-    where
-        O: text::ToPoint + text::ToOffset,
-    {
-        let buffer_id = buffer.read(cx).remote_id();
-        let buffer_snapshot = buffer.read(cx).snapshot();
-        let (excerpt_ranges, range_counts) =
-            build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
-
-        let excerpt_ids = self.push_excerpts(buffer, excerpt_ranges, cx);
-
-        let mut anchor_ranges = Vec::new();
-        let mut ranges = ranges.into_iter();
-        for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.into_iter()) {
-            anchor_ranges.extend(ranges.by_ref().take(range_count).map(|range| {
-                let start = Anchor {
-                    buffer_id: Some(buffer_id),
-                    excerpt_id,
-                    text_anchor: buffer_snapshot.anchor_after(range.start),
-                    diff_base_anchor: None,
-                };
-                let end = Anchor {
-                    buffer_id: Some(buffer_id),
-                    excerpt_id,
-                    text_anchor: buffer_snapshot.anchor_after(range.end),
-                    diff_base_anchor: None,
-                };
-                start..end
-            }))
-        }
-        anchor_ranges
-    }
-
     pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
         let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
         let snapshot = self.snapshot(cx);
@@ -1589,17 +1558,16 @@ impl MultiBuffer {
         &mut self,
         path: PathKey,
         buffer: Entity<Buffer>,
-        ranges: Vec<Range<Point>>,
+        ranges: impl IntoIterator<Item = Range<Point>>,
         context_line_count: u32,
         cx: &mut Context<Self>,
-    ) -> bool {
+    ) -> (Vec<Range<Anchor>>, bool) {
         let buffer_snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
 
         let excerpt_ranges = ranges.into_iter().map(|range| {
-            let start = range
-                .start
-                .saturating_sub(Point::new(context_line_count, 0));
-            let end_row = (range.end.row + 2).min(buffer_snapshot.max_point().row);
+            let start_row = range.start.row.saturating_sub(context_line_count);
+            let start = Point::new(start_row, 0);
+            let end_row = (range.end.row + context_line_count).min(buffer_snapshot.max_point().row);
             let end = Point::new(end_row, buffer_snapshot.line_len(end_row));
             ExcerptRange {
                 context: start..end,
@@ -1607,9 +1575,7 @@ impl MultiBuffer {
             }
         });
 
-        let (_, is_new) =
-            self.set_excerpt_ranges_for_path(path, buffer, excerpt_ranges.collect(), cx);
-        is_new
+        self.set_excerpt_ranges_for_path(path, buffer, excerpt_ranges.collect(), cx)
     }
 
     /// Sets excerpts, returns `true` if at least one new excerpt was added.

crates/multi_buffer/src/multi_buffer_tests.rs 🔗

@@ -710,8 +710,9 @@ fn test_expand_excerpts(cx: &mut App) {
     let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 
     multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.push_excerpts_with_context_lines(
-            buffer.clone(),
+        multibuffer.set_excerpts_for_path(
+            PathKey::for_buffer(&buffer, cx),
+            buffer,
             vec![
                 // Note that in this test, this first excerpt
                 // does not contain a new line
@@ -765,7 +766,6 @@ fn test_expand_excerpts(cx: &mut App) {
             "ccc\n", //
             "ddd\n", //
             "eee\n", //
-            "fff\n", // End of excerpt
             "fff\n", //
             "ggg\n", //
             "hhh\n", //
@@ -780,59 +780,6 @@ fn test_expand_excerpts(cx: &mut App) {
     );
 }
 
-#[gpui::test]
-fn test_push_excerpts_with_context_lines(cx: &mut App) {
-    let buffer = cx.new(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
-    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
-    let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
-        multibuffer.push_excerpts_with_context_lines(
-            buffer.clone(),
-            vec![
-                // Note that in this test, this first excerpt
-                // does contain a new line
-                Point::new(3, 2)..Point::new(4, 2),
-                Point::new(7, 1)..Point::new(7, 3),
-                Point::new(15, 0)..Point::new(15, 0),
-            ],
-            2,
-            cx,
-        )
-    });
-
-    let snapshot = multibuffer.read(cx).snapshot(cx);
-    assert_eq!(
-        snapshot.text(),
-        concat!(
-            "bbb\n", // Preserve newlines
-            "ccc\n", //
-            "ddd\n", //
-            "eee\n", //
-            "fff\n", //
-            "ggg\n", //
-            "hhh\n", //
-            "iii\n", //
-            "jjj\n", //
-            "nnn\n", //
-            "ooo\n", //
-            "ppp\n", //
-            "qqq\n", //
-            "rrr",   //
-        )
-    );
-
-    assert_eq!(
-        anchor_ranges
-            .iter()
-            .map(|range| range.to_point(&snapshot))
-            .collect::<Vec<_>>(),
-        vec![
-            Point::new(2, 2)..Point::new(3, 2),
-            Point::new(6, 1)..Point::new(6, 3),
-            Point::new(11, 0)..Point::new(11, 0)
-        ]
-    );
-}
-
 #[gpui::test(iterations = 100)]
 async fn test_push_multiple_excerpts_with_context_lines(cx: &mut TestAppContext) {
     let buffer_1 = cx.new(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
@@ -1513,7 +1460,7 @@ fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) {
             cx,
         )
     });
-    let path1: PathKey = PathKey::namespaced("0", Path::new("/").into());
+    let path1: PathKey = PathKey::namespaced(0, Path::new("/").into());
 
     let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
     multibuffer.update(cx, |multibuffer, cx| {
@@ -1539,13 +1486,11 @@ fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) {
             one
             two
             two.five
-            three
             -----
             four
             five
             six
             seven
-            eight
             -----
             nine
             ten
@@ -1561,8 +1506,8 @@ fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) {
             path1.clone(),
             buf1.clone(),
             vec![
-                Point::row_range(0..2),
-                Point::row_range(5..6),
+                Point::row_range(0..3),
+                Point::row_range(5..7),
                 Point::row_range(10..11),
             ],
             1,
@@ -1611,7 +1556,7 @@ fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) {
             cx,
         )
     });
-    let path1: PathKey = PathKey::namespaced("0", Path::new("/").into());
+    let path1: PathKey = PathKey::namespaced(0, Path::new("/").into());
     let buf2 = cx.new(|cx| {
         Buffer::local(
             indoc! {
@@ -1630,7 +1575,7 @@ fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) {
             cx,
         )
     });
-    let path2 = PathKey::namespaced("x", Path::new("/").into());
+    let path2 = PathKey::namespaced(1, Path::new("/").into());
 
     let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
     multibuffer.update(cx, |multibuffer, cx| {