Fix panic when editor::OpenSelectionsInMultibuffer
only has pending selection (#32842)
On the panics dashboard, saw this panic of `There must be at least one selection` in `open_locations_in_multibuffer`. Only seems to have happened once in the past month. Fix is to include the pending selection. Since `selections.all()` cannot provide anchor selections, added `selections.all_anchors()` which only really does any work if there is a pending selection. Also fixes a corner case in jump-to-definitions where if the definition is `HoverLink::InlayHint` and the `compute_target_location` fails for all definitions it could potentially also trigger this case (and return `Navigated::Yes` instead of `Navigated::No` Release Notes: - N/A
This commit is contained in:
parent
0e794fa0ac
commit
2f3acb6185
2 changed files with 60 additions and 25 deletions
|
@ -15130,16 +15130,21 @@ impl Editor {
|
|||
})
|
||||
.context("location tasks preparation")?;
|
||||
|
||||
let locations = future::join_all(location_tasks)
|
||||
let locations: Vec<Location> = future::join_all(location_tasks)
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|location| location.transpose())
|
||||
.collect::<Result<_>>()
|
||||
.context("location tasks")?;
|
||||
|
||||
if locations.is_empty() {
|
||||
return Ok(Navigated::No);
|
||||
}
|
||||
|
||||
let Some(workspace) = workspace else {
|
||||
return Ok(Navigated::No);
|
||||
};
|
||||
|
||||
let opened = workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
Self::open_locations_in_multibuffer(
|
||||
|
@ -15303,6 +15308,11 @@ impl Editor {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) {
|
||||
if locations.is_empty() {
|
||||
log::error!("bug: open_locations_in_multibuffer called with empty list of locations");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
@ -18289,18 +18299,18 @@ impl Editor {
|
|||
return;
|
||||
};
|
||||
|
||||
let title = multibuffer.title(cx).to_string();
|
||||
|
||||
let locations = self
|
||||
.selections
|
||||
.disjoint_anchors()
|
||||
.iter()
|
||||
.map(|range| Location {
|
||||
.all_anchors(cx)
|
||||
.into_iter()
|
||||
.map(|selection| Location {
|
||||
buffer: buffer.clone(),
|
||||
range: range.start.text_anchor..range.end.text_anchor,
|
||||
range: selection.start.text_anchor..selection.end.text_anchor,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let title = multibuffer.title(cx).to_string();
|
||||
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
workspace.update_in(cx, |workspace, window, cx| {
|
||||
Self::open_locations_in_multibuffer(
|
||||
|
|
|
@ -81,9 +81,9 @@ impl SelectionsCollection {
|
|||
count
|
||||
}
|
||||
|
||||
/// The non-pending, non-overlapping selections. There could still be a pending
|
||||
/// selection that overlaps these if the mouse is being dragged, etc. Returned as
|
||||
/// selections over Anchors.
|
||||
/// The non-pending, non-overlapping selections. There could be a pending selection that
|
||||
/// overlaps these if the mouse is being dragged, etc. This could also be empty if there is a
|
||||
/// pending selection. Returned as selections over Anchors.
|
||||
pub fn disjoint_anchors(&self) -> Arc<[Selection<Anchor>]> {
|
||||
self.disjoint.clone()
|
||||
}
|
||||
|
@ -94,6 +94,20 @@ impl SelectionsCollection {
|
|||
(0..disjoint.len()).map(move |ix| disjoint[ix].range())
|
||||
}
|
||||
|
||||
/// Non-overlapping selections using anchors, including the pending selection.
|
||||
pub fn all_anchors(&self, cx: &mut App) -> Arc<[Selection<Anchor>]> {
|
||||
if self.pending.is_none() {
|
||||
self.disjoint_anchors()
|
||||
} else {
|
||||
let all_offset_selections = self.all::<usize>(cx);
|
||||
let buffer = self.buffer(cx);
|
||||
all_offset_selections
|
||||
.into_iter()
|
||||
.map(|selection| selection_to_anchor_selection(selection, &buffer))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending_anchor(&self) -> Option<Selection<Anchor>> {
|
||||
self.pending
|
||||
.as_ref()
|
||||
|
@ -534,21 +548,11 @@ impl<'a> MutableSelectionsCollection<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
self.collection.disjoint = Arc::from_iter(selections.into_iter().map(|selection| {
|
||||
let end_bias = if selection.end > selection.start {
|
||||
Bias::Left
|
||||
} else {
|
||||
Bias::Right
|
||||
};
|
||||
Selection {
|
||||
id: selection.id,
|
||||
start: buffer.anchor_after(selection.start),
|
||||
end: buffer.anchor_at(selection.end, end_bias),
|
||||
reversed: selection.reversed,
|
||||
goal: selection.goal,
|
||||
}
|
||||
}));
|
||||
|
||||
self.collection.disjoint = Arc::from_iter(
|
||||
selections
|
||||
.into_iter()
|
||||
.map(|selection| selection_to_anchor_selection(selection, &buffer)),
|
||||
);
|
||||
self.collection.pending = None;
|
||||
self.selections_changed = true;
|
||||
}
|
||||
|
@ -880,6 +884,27 @@ impl DerefMut for MutableSelectionsCollection<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn selection_to_anchor_selection<T>(
|
||||
selection: Selection<T>,
|
||||
buffer: &MultiBufferSnapshot,
|
||||
) -> Selection<Anchor>
|
||||
where
|
||||
T: ToOffset + Ord,
|
||||
{
|
||||
let end_bias = if selection.end > selection.start {
|
||||
Bias::Left
|
||||
} else {
|
||||
Bias::Right
|
||||
};
|
||||
Selection {
|
||||
id: selection.id,
|
||||
start: buffer.anchor_after(selection.start),
|
||||
end: buffer.anchor_at(selection.end, end_bias),
|
||||
reversed: selection.reversed,
|
||||
goal: selection.goal,
|
||||
}
|
||||
}
|
||||
|
||||
// Panics if passed selections are not in order
|
||||
fn resolve_selections_display<'a>(
|
||||
selections: impl 'a + IntoIterator<Item = &'a Selection<Anchor>>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue