From 9bc4697a338614a9fc7452e5d6c8e1279715cc4a Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Tue, 1 Apr 2025 15:42:32 -0600 Subject: [PATCH] Use new multibuffer excerpts in find-all-references and friends (#27876) Release Notes: - N/A --------- Co-authored-by: Kirill Bulatov --- 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(-) diff --git a/crates/assistant2/src/assistant_diff.rs b/crates/assistant2/src/assistant_diff.rs index 3227866339..d000ed10e1 100644 --- a/crates/assistant2/src/assistant_diff.rs +++ b/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, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b08f1dd1cf..550b005250 100644 --- a/crates/editor/src/editor.rs +++ b/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::(transaction) - .collect(), - DEFAULT_MULTIBUFFER_CONTEXT, - cx, - ), + let edited_ranges = buffer_handle + .read(cx) + .edited_ranges_for_transaction::(transaction) + .collect::>(); + 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> = 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) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 827809b51c..61870d0fb8 100644 --- a/crates/editor/src/editor_tests.rs +++ b/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, diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index b677bb8e8a..1dde3ca2b7 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/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( diff --git a/crates/git_ui/src/project_diff.rs b/crates/git_ui/src/project_diff.rs index 2be1aeea9f..697e2e4277 100644 --- a/crates/git_ui/src/project_diff.rs +++ b/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) { @@ -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, diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index a928e1bf72..ccd454ef13 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/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, } impl PathKey { - pub fn namespaced(namespace: &'static str, path: Arc) -> Self { + pub fn namespaced(namespace: u32, path: Arc) -> Self { Self { namespace, path } } + pub fn for_buffer(buffer: &Entity, 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 { &self.path } @@ -1448,45 +1456,6 @@ impl MultiBuffer { self.insert_excerpts_after(ExcerptId::max(), buffer, ranges, cx) } - pub fn push_excerpts_with_context_lines( - &mut self, - buffer: Entity, - ranges: Vec>, - context_line_count: u32, - cx: &mut Context, - ) -> Vec> - 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 { 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, - ranges: Vec>, + ranges: impl IntoIterator>, context_line_count: u32, cx: &mut Context, - ) -> bool { + ) -> (Vec>, 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. diff --git a/crates/multi_buffer/src/multi_buffer_tests.rs b/crates/multi_buffer/src/multi_buffer_tests.rs index d1371b3678..0acba2984a 100644 --- a/crates/multi_buffer/src/multi_buffer_tests.rs +++ b/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![ - 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| {