Compare commits

...

2 Commits

Author SHA1 Message Date
Conrad Irwin
675c3441ee clipster 2025-03-13 23:24:46 -06:00
Conrad Irwin
38773a9b0b Use new path-based multibuffers in "find references" and friends 2025-03-13 22:51:29 -06:00
5 changed files with 54 additions and 124 deletions

View File

@@ -134,7 +134,7 @@ pub use multi_buffer::{
}; };
use multi_buffer::{ use multi_buffer::{
ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ExcerptInfo, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow,
MultiOrSingleBufferOffsetRange, ToOffsetUtf16, MultiOrSingleBufferOffsetRange, PathKey, ToOffsetUtf16,
}; };
use project::{ use project::{
lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle}, lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
@@ -4659,18 +4659,18 @@ impl Editor {
let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title); let mut multibuffer = MultiBuffer::new(Capability::ReadWrite).with_title(title);
for (buffer_handle, transaction) in &entries { for (buffer_handle, transaction) in &entries {
let buffer = buffer_handle.read(cx); let buffer = buffer_handle.read(cx);
ranges_to_highlight.extend( multibuffer.set_excerpts_for_path(
multibuffer.push_excerpts_with_context_lines( PathKey::for_buffer(&buffer, cx),
buffer_handle.clone(), buffer_handle.clone(),
buffer buffer
.edited_ranges_for_transaction::<usize>(transaction) .edited_ranges_for_transaction::<Point>(transaction)
.collect(), .collect(),
DEFAULT_MULTIBUFFER_CONTEXT, DEFAULT_MULTIBUFFER_CONTEXT,
cx, cx,
),
); );
} }
multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx); multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
ranges_to_highlight.extend(multibuffer.primary_excerpt_ranges(cx));
multibuffer multibuffer
})?; })?;
@@ -12328,20 +12328,20 @@ impl Editor {
// If there are multiple definitions, open them in a multibuffer // If there are multiple definitions, open them in a multibuffer
locations.sort_by_key(|location| location.buffer.read(cx).remote_id()); locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
let mut locations = locations.into_iter().peekable(); let mut locations = locations.into_iter().peekable();
let mut ranges = Vec::new();
let capability = workspace.project().read(cx).capability(); let capability = workspace.project().read(cx).capability();
let mut ranges = Vec::new();
let excerpt_buffer = cx.new(|cx| { let excerpt_buffer = cx.new(|cx| {
let mut multibuffer = MultiBuffer::new(capability); let mut multibuffer = MultiBuffer::new(capability);
while let Some(location) = locations.next() { while let Some(location) = locations.next() {
let buffer = location.buffer.read(cx); let buffer = location.buffer.read(cx);
let mut ranges_for_buffer = Vec::new(); 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()); ranges_for_buffer.push(range.clone());
while let Some(next_location) = locations.peek() { while let Some(next_location) = locations.peek() {
if next_location.buffer == location.buffer { 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(); locations.next();
} else { } else {
break; break;
@@ -12349,14 +12349,16 @@ impl Editor {
} }
ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end))); ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
ranges.extend(multibuffer.push_excerpts_with_context_lines( multibuffer.set_excerpts_for_path(
PathKey::for_buffer(&buffer, cx),
location.buffer.clone(), location.buffer.clone(),
ranges_for_buffer, ranges_for_buffer,
DEFAULT_MULTIBUFFER_CONTEXT, DEFAULT_MULTIBUFFER_CONTEXT,
cx, cx,
)) );
} }
ranges.extend(multibuffer.primary_excerpt_ranges(cx));
multibuffer.with_title(title) multibuffer.with_title(title)
}); });

View File

@@ -15648,7 +15648,7 @@ async fn test_display_diff_hunks(cx: &mut TestAppContext) {
for buffer in &buffers { for buffer in &buffers {
let snapshot = buffer.read(cx).snapshot(); let snapshot = buffer.read(cx).snapshot();
multibuffer.set_excerpts_for_path( 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(), buffer.clone(),
vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)], vec![text::Anchor::MIN.to_point(&snapshot)..text::Anchor::MAX.to_point(&snapshot)],
DEFAULT_MULTIBUFFER_CONTEXT, DEFAULT_MULTIBUFFER_CONTEXT,

View File

@@ -61,9 +61,9 @@ struct DiffBuffer {
file_status: FileStatus, file_status: FileStatus,
} }
const CONFLICT_NAMESPACE: &'static str = "0"; const CONFLICT_NAMESPACE: usize = 0;
const TRACKED_NAMESPACE: &'static str = "1"; const TRACKED_NAMESPACE: usize = 1;
const NEW_NAMESPACE: &'static str = "2"; const NEW_NAMESPACE: usize = 2;
impl ProjectDiff { impl ProjectDiff {
pub(crate) fn register( pub(crate) fn register(

View File

@@ -167,17 +167,27 @@ impl MultiBufferDiffHunk {
#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)] #[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
pub struct PathKey { pub struct PathKey {
namespace: &'static str, namespace: usize,
path: Arc<Path>, path: Arc<Path>,
} }
impl PathKey { impl PathKey {
pub fn namespaced(namespace: &'static str, path: Arc<Path>) -> Self { pub fn namespaced(namespace: usize, path: Arc<Path>) -> Self {
Self { namespace, path } Self { namespace, path }
} }
pub fn path(&self) -> &Arc<Path> { pub fn for_buffer(buffer: &Buffer, cx: &App) -> Self {
&self.path if let Some(file) = buffer.file() {
Self {
namespace: 1 + file.worktree_id(cx).to_usize(),
path: file.path().clone(),
}
} else {
Self {
namespace: 0,
path: Arc::from(Path::new(&format!("Untitled/{}", buffer.remote_id()))),
}
}
} }
} }
@@ -1442,45 +1452,6 @@ impl MultiBuffer {
self.insert_excerpts_after(ExcerptId::max(), buffer, ranges, cx) 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> { pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
let excerpt_id = self.excerpts_by_path.get(path)?.first()?; let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
let snapshot = self.snapshot(cx); let snapshot = self.snapshot(cx);
@@ -1554,7 +1525,7 @@ impl MultiBuffer {
let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new(); let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
for range in expanded_ranges { for range in expanded_ranges {
if let Some(last_range) = merged_ranges.last_mut() { if let Some(last_range) = merged_ranges.last_mut() {
if last_range.context.end >= range.context.start { if last_range.context.end + Point::new(1, 0) >= range.context.start {
last_range.context.end = range.context.end; last_range.context.end = range.context.end;
continue; continue;
} }
@@ -2015,6 +1986,19 @@ impl MultiBuffer {
excerpts excerpts
} }
pub fn primary_excerpt_ranges(&self, cx: &App) -> Vec<Range<Anchor>> {
self.read(cx)
.excerpts()
.flat_map(|(excerpt_id, buffer, range)| {
Some(Anchor::range_in_buffer(
excerpt_id,
buffer.remote_id(),
range.primary?,
))
})
.collect()
}
pub fn excerpt_ranges_for_buffer(&self, buffer_id: BufferId, cx: &App) -> Vec<Range<Point>> { pub fn excerpt_ranges_for_buffer(&self, buffer_id: BufferId, cx: &App) -> Vec<Range<Point>> {
let snapshot = self.read(cx); let snapshot = self.read(cx);
let buffers = self.buffers.borrow(); let buffers = self.buffers.borrow();

View File

@@ -737,7 +737,8 @@ fn test_expand_excerpts(cx: &mut App) {
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite)); let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
multibuffer.update(cx, |multibuffer, cx| { multibuffer.update(cx, |multibuffer, cx| {
multibuffer.push_excerpts_with_context_lines( multibuffer.set_excerpts_for_path(
PathKey::for_buffer(&buffer.read(cx), cx),
buffer.clone(), buffer.clone(),
vec![ vec![
// Note that in this test, this first excerpt // Note that in this test, this first excerpt
@@ -782,9 +783,6 @@ fn test_expand_excerpts(cx: &mut App) {
let snapshot = multibuffer.read(cx).snapshot(cx); let snapshot = multibuffer.read(cx).snapshot(cx);
// Expanding context lines causes the line containing 'fff' to appear in two different excerpts.
// We don't attempt to merge them, because removing the excerpt could create inconsistency with other layers
// that are tracking excerpt ids.
assert_eq!( assert_eq!(
snapshot.text(), snapshot.text(),
concat!( concat!(
@@ -792,7 +790,6 @@ fn test_expand_excerpts(cx: &mut App) {
"ccc\n", // "ccc\n", //
"ddd\n", // "ddd\n", //
"eee\n", // "eee\n", //
"fff\n", // End of excerpt
"fff\n", // "fff\n", //
"ggg\n", // "ggg\n", //
"hhh\n", // "hhh\n", //
@@ -807,59 +804,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)] #[gpui::test(iterations = 100)]
async fn test_push_multiple_excerpts_with_context_lines(cx: &mut TestAppContext) { 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)); let buffer_1 = cx.new(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
@@ -1587,7 +1531,7 @@ fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) {
cx, 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)); let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
multibuffer.update(cx, |multibuffer, cx| { multibuffer.update(cx, |multibuffer, cx| {
@@ -1683,7 +1627,7 @@ fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) {
cx, cx,
) )
}); });
let path1: PathKey = PathKey::namespaced("0", Path::new("/").into()); let path1: PathKey = PathKey::namespaced(0, Path::new("/").into());
let buf2 = cx.new(|cx| { let buf2 = cx.new(|cx| {
Buffer::local( Buffer::local(
indoc! { indoc! {
@@ -1702,7 +1646,7 @@ fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) {
cx, cx,
) )
}); });
let path2 = PathKey::namespaced("x", Path::new("/").into()); let path2 = PathKey::namespaced(0, Path::new("/").into());
let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite)); let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
multibuffer.update(cx, |multibuffer, cx| { multibuffer.update(cx, |multibuffer, cx| {