editor: Deduplicate locations in navigate_to_hover_links (#38707)
Closes https://github.com/zed-industries/zed/issues/6730#issuecomment-3320933701 That way if multiple servers are running while reporting the same results we prevent opening multi buffers for single entries. Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
+65
-49
@@ -177,7 +177,7 @@ use std::{
|
||||
borrow::Cow,
|
||||
cell::{OnceCell, RefCell},
|
||||
cmp::{self, Ordering, Reverse},
|
||||
iter::Peekable,
|
||||
iter::{self, Peekable},
|
||||
mem,
|
||||
num::NonZeroU32,
|
||||
ops::{ControlFlow, Deref, DerefMut, Not, Range, RangeInclusive},
|
||||
@@ -16401,15 +16401,30 @@ impl Editor {
|
||||
|
||||
let workspace = self.workspace();
|
||||
|
||||
cx.spawn_in(window, async move |editor, acx| {
|
||||
let mut locations: Vec<Location> = future::join_all(definitions)
|
||||
cx.spawn_in(window, async move |editor, cx| {
|
||||
let locations: Vec<Location> = future::join_all(definitions)
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|location| location.transpose())
|
||||
.collect::<Result<_>>()
|
||||
.context("location tasks")?;
|
||||
let mut locations = cx.update(|_, cx| {
|
||||
locations
|
||||
.into_iter()
|
||||
.map(|location| {
|
||||
let buffer = location.buffer.read(cx);
|
||||
(location.buffer, location.range.to_point(buffer))
|
||||
})
|
||||
.into_group_map()
|
||||
})?;
|
||||
let mut num_locations = 0;
|
||||
for ranges in locations.values_mut() {
|
||||
ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
|
||||
ranges.dedup();
|
||||
num_locations += ranges.len();
|
||||
}
|
||||
|
||||
if locations.len() > 1 {
|
||||
if num_locations > 1 {
|
||||
let Some(workspace) = workspace else {
|
||||
return Ok(Navigated::No);
|
||||
};
|
||||
@@ -16421,14 +16436,14 @@ impl Editor {
|
||||
Some(GotoDefinitionKind::Type) => "Types",
|
||||
};
|
||||
let title = editor
|
||||
.update_in(acx, |_, _, cx| {
|
||||
.update_in(cx, |_, _, cx| {
|
||||
let target = locations
|
||||
.iter()
|
||||
.map(|location| {
|
||||
location
|
||||
.buffer
|
||||
.flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
|
||||
.map(|(buffer, location)| {
|
||||
buffer
|
||||
.read(cx)
|
||||
.text_for_range(location.range.clone())
|
||||
.text_for_range(location.clone())
|
||||
.collect::<String>()
|
||||
})
|
||||
.filter(|text| !text.contains('\n'))
|
||||
@@ -16444,7 +16459,7 @@ impl Editor {
|
||||
.context("buffer title")?;
|
||||
|
||||
let opened = workspace
|
||||
.update_in(acx, |workspace, window, cx| {
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
Self::open_locations_in_multibuffer(
|
||||
workspace,
|
||||
locations,
|
||||
@@ -16458,11 +16473,11 @@ impl Editor {
|
||||
.is_ok();
|
||||
|
||||
anyhow::Ok(Navigated::from_bool(opened))
|
||||
} else if locations.is_empty() {
|
||||
} else if num_locations == 0 {
|
||||
// If there is one url or file, open it directly
|
||||
match first_url_or_file {
|
||||
Some(Either::Left(url)) => {
|
||||
acx.update(|_, cx| cx.open_url(&url))?;
|
||||
cx.update(|_, cx| cx.open_url(&url))?;
|
||||
Ok(Navigated::Yes)
|
||||
}
|
||||
Some(Either::Right(path)) => {
|
||||
@@ -16471,7 +16486,7 @@ impl Editor {
|
||||
};
|
||||
|
||||
workspace
|
||||
.update_in(acx, |workspace, window, cx| {
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
workspace.open_resolved_path(path, window, cx)
|
||||
})?
|
||||
.await?;
|
||||
@@ -16484,14 +16499,16 @@ impl Editor {
|
||||
return Ok(Navigated::No);
|
||||
};
|
||||
|
||||
let target = locations.pop().unwrap();
|
||||
editor.update_in(acx, |editor, window, cx| {
|
||||
let range = target.range.to_point(target.buffer.read(cx));
|
||||
let (target_buffer, target_ranges) = locations.into_iter().next().unwrap();
|
||||
let target_range = target_ranges.first().unwrap().clone();
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
let range = target_range.to_point(target_buffer.read(cx));
|
||||
let range = editor.range_for_match(&range);
|
||||
let range = collapse_multiline_range(range);
|
||||
|
||||
if !split
|
||||
&& Some(&target.buffer) == editor.buffer.read(cx).as_singleton().as_ref()
|
||||
&& Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
|
||||
{
|
||||
editor.go_to_singleton_buffer_range(range, window, cx);
|
||||
} else {
|
||||
@@ -16507,7 +16524,7 @@ impl Editor {
|
||||
|
||||
workspace.open_project_item(
|
||||
pane,
|
||||
target.buffer.clone(),
|
||||
target_buffer.clone(),
|
||||
true,
|
||||
true,
|
||||
window,
|
||||
@@ -16617,18 +16634,31 @@ impl Editor {
|
||||
let Some(locations) = references.await? else {
|
||||
return anyhow::Ok(Navigated::No);
|
||||
};
|
||||
let mut locations = cx.update(|_, cx| {
|
||||
locations
|
||||
.into_iter()
|
||||
.map(|location| {
|
||||
let buffer = location.buffer.read(cx);
|
||||
(location.buffer, location.range.to_point(buffer))
|
||||
})
|
||||
.into_group_map()
|
||||
})?;
|
||||
if locations.is_empty() {
|
||||
return anyhow::Ok(Navigated::No);
|
||||
}
|
||||
for ranges in locations.values_mut() {
|
||||
ranges.sort_by_key(|range| (range.start, Reverse(range.end)));
|
||||
ranges.dedup();
|
||||
}
|
||||
|
||||
workspace.update_in(cx, |workspace, window, cx| {
|
||||
let target = locations
|
||||
.iter()
|
||||
.map(|location| {
|
||||
location
|
||||
.buffer
|
||||
.flat_map(|(k, v)| iter::repeat(k.clone()).zip(v))
|
||||
.map(|(buffer, location)| {
|
||||
buffer
|
||||
.read(cx)
|
||||
.text_for_range(location.range.clone())
|
||||
.text_for_range(location.clone())
|
||||
.collect::<String>()
|
||||
})
|
||||
.filter(|text| !text.contains('\n'))
|
||||
@@ -16657,7 +16687,7 @@ impl Editor {
|
||||
/// Opens a multibuffer with the given project locations in it
|
||||
pub fn open_locations_in_multibuffer(
|
||||
workspace: &mut Workspace,
|
||||
mut locations: Vec<Location>,
|
||||
locations: std::collections::HashMap<Entity<Buffer>, Vec<Range<Point>>>,
|
||||
title: String,
|
||||
split: bool,
|
||||
multibuffer_selection_mode: MultibufferSelectionMode,
|
||||
@@ -16669,11 +16699,8 @@ impl Editor {
|
||||
return;
|
||||
}
|
||||
|
||||
locations.sort_by_key(|location| location.buffer.read(cx).remote_id());
|
||||
|
||||
let mut locations = locations.into_iter().peekable();
|
||||
let mut ranges: Vec<Range<Anchor>> = Vec::new();
|
||||
let capability = workspace.project().read(cx).capability();
|
||||
let mut ranges = <Vec<Range<Anchor>>>::new();
|
||||
|
||||
// a key to find existing multibuffer editors with the same set of locations
|
||||
// to prevent us from opening more and more multibuffer tabs for searches and the like
|
||||
@@ -16681,26 +16708,12 @@ impl Editor {
|
||||
let excerpt_buffer = cx.new(|cx| {
|
||||
let key = &mut key.1;
|
||||
let mut multibuffer = MultiBuffer::new(capability);
|
||||
while let Some(location) = locations.next() {
|
||||
let buffer = location.buffer.read(cx);
|
||||
let mut ranges_for_buffer = Vec::new();
|
||||
let range = location.range.to_point(buffer);
|
||||
ranges_for_buffer.push(range.clone());
|
||||
|
||||
while let Some(next_location) =
|
||||
locations.next_if(|next_location| next_location.buffer == location.buffer)
|
||||
{
|
||||
ranges_for_buffer.push(next_location.range.to_point(buffer));
|
||||
}
|
||||
|
||||
for (buffer, mut ranges_for_buffer) in locations {
|
||||
ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
|
||||
key.push((
|
||||
location.buffer.read(cx).remote_id(),
|
||||
ranges_for_buffer.clone(),
|
||||
));
|
||||
key.push((buffer.read(cx).remote_id(), ranges_for_buffer.clone()));
|
||||
let (new_ranges, _) = multibuffer.set_excerpts_for_path(
|
||||
PathKey::for_buffer(&location.buffer, cx),
|
||||
location.buffer.clone(),
|
||||
PathKey::for_buffer(&buffer, cx),
|
||||
buffer.clone(),
|
||||
ranges_for_buffer,
|
||||
multibuffer_context_lines(cx),
|
||||
cx,
|
||||
@@ -19782,11 +19795,14 @@ impl Editor {
|
||||
.selections
|
||||
.all_anchors(cx)
|
||||
.iter()
|
||||
.map(|selection| Location {
|
||||
buffer: buffer.clone(),
|
||||
range: selection.start.text_anchor..selection.end.text_anchor,
|
||||
.map(|selection| {
|
||||
(
|
||||
buffer.clone(),
|
||||
(selection.start.text_anchor..selection.end.text_anchor)
|
||||
.to_point(buffer.read(cx)),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.into_group_map();
|
||||
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
workspace.update_in(cx, |workspace, window, cx| {
|
||||
|
||||
Reference in New Issue
Block a user