diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e77e33a366..25b7f24e20 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -15130,16 +15130,21 @@ impl Editor { }) .context("location tasks preparation")?; - let locations = future::join_all(location_tasks) + let locations: Vec = future::join_all(location_tasks) .await .into_iter() .filter_map(|location| location.transpose()) .collect::>() .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, ) { + 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::>(); - 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( diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 6b5203caf2..73c5f1c076 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -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]> { 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]> { + if self.pending.is_none() { + self.disjoint_anchors() + } else { + let all_offset_selections = self.all::(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> { 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( + selection: Selection, + buffer: &MultiBufferSnapshot, +) -> Selection +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>,