From 75b507d38a50cd80234857beaad41a67809a76fb Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Sat, 25 Jan 2025 02:45:09 -0500 Subject: [PATCH] Open selections in multi buffer (#23644) Closes https://github.com/zed-industries/zed/issues/5126 Release Notes: - Added an `editor: open selections in multibuffer` command. --- crates/editor/src/actions.rs | 1 + crates/editor/src/editor.rs | 100 ++++++++++++++++++++++++++++++----- crates/editor/src/element.rs | 1 + 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index e1995078a2..13f9d6f231 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -318,6 +318,7 @@ gpui::actions!( OpenProposedChangesEditor, OpenDocs, OpenPermalinkToLine, + OpenSelectionsInMultibuffer, OpenUrl, Outdent, AutoIndent, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3b59ea51f9..34dd5ca299 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1031,6 +1031,11 @@ enum JumpData { }, } +pub enum MultibufferSelectionMode { + First, + All, +} + impl Editor { pub fn single_line(cx: &mut ViewContext) -> Self { let buffer = cx.new_model(|cx| Buffer::local("", cx)); @@ -9836,7 +9841,14 @@ impl Editor { }; let opened = workspace .update(&mut cx, |workspace, cx| { - Self::open_locations_in_multibuffer(workspace, locations, title, split, cx) + Self::open_locations_in_multibuffer( + workspace, + locations, + title, + split, + MultibufferSelectionMode::First, + cx, + ) }) .ok(); @@ -9971,7 +9983,14 @@ impl Editor { ) }) .unwrap(); - Self::open_locations_in_multibuffer(workspace, locations, title, false, cx); + Self::open_locations_in_multibuffer( + workspace, + locations, + title, + false, + MultibufferSelectionMode::First, + cx, + ); Navigated::Yes }) })) @@ -9983,12 +10002,13 @@ impl Editor { mut locations: Vec, title: String, split: bool, + multibuffer_selection_mode: MultibufferSelectionMode, cx: &mut ViewContext, ) { // 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_to_highlight = Vec::new(); + let mut ranges = Vec::new(); let capability = workspace.project().read(cx).capability(); let excerpt_buffer = cx.new_model(|cx| { @@ -10009,7 +10029,7 @@ impl Editor { } ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end))); - ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines( + ranges.extend(multibuffer.push_excerpts_with_context_lines( location.buffer.clone(), ranges_for_buffer, DEFAULT_MULTIBUFFER_CONTEXT, @@ -10024,17 +10044,27 @@ impl Editor { Editor::for_multibuffer(excerpt_buffer, Some(workspace.project().clone()), true, cx) }); editor.update(cx, |editor, cx| { - if let Some(first_range) = ranges_to_highlight.first() { - editor.change_selections(None, cx, |selections| { - selections.clear_disjoint(); - selections.select_anchor_ranges(std::iter::once(first_range.clone())); - }); + match multibuffer_selection_mode { + MultibufferSelectionMode::First => { + if let Some(first_range) = ranges.first() { + editor.change_selections(None, cx, |selections| { + selections.clear_disjoint(); + selections.select_anchor_ranges(std::iter::once(first_range.clone())); + }); + } + editor.highlight_background::( + &ranges, + |theme| theme.editor_highlighted_line_background, + cx, + ); + } + MultibufferSelectionMode::All => { + editor.change_selections(None, cx, |selections| { + selections.clear_disjoint(); + selections.select_anchor_ranges(ranges); + }); + } } - editor.highlight_background::( - &ranges_to_highlight, - |theme| theme.editor_highlighted_line_background, - cx, - ); editor.register_buffers_with_language_servers(cx); }); @@ -11967,6 +11997,48 @@ impl Editor { }); } + pub fn open_selections_in_multibuffer( + &mut self, + _: &OpenSelectionsInMultibuffer, + cx: &mut ViewContext, + ) { + let multibuffer = self.buffer.read(cx); + + let Some(buffer) = multibuffer.as_singleton() else { + return; + }; + + let Some(workspace) = self.workspace() else { + return; + }; + + let locations = self + .selections + .disjoint_anchors() + .iter() + .map(|range| Location { + buffer: buffer.clone(), + range: range.start.text_anchor..range.end.text_anchor, + }) + .collect::>(); + + let title = multibuffer.title(cx).to_string(); + + cx.spawn(|_, mut cx| async move { + workspace.update(&mut cx, |workspace, cx| { + Self::open_locations_in_multibuffer( + workspace, + locations, + format!("Selections for '{title}'"), + false, + MultibufferSelectionMode::All, + cx, + ); + }) + }) + .detach(); + } + /// Adds a row highlight for the given range. If a row has multiple highlights, the /// last highlight added will be used. /// diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d582b9e8a6..c27daf2aee 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -477,6 +477,7 @@ impl EditorElement { register_action(view, cx, Editor::spawn_nearest_task); register_action(view, cx, Editor::insert_uuid_v4); register_action(view, cx, Editor::insert_uuid_v7); + register_action(view, cx, Editor::open_selections_in_multibuffer); } fn register_key_listeners(&self, cx: &mut WindowContext, layout: &EditorLayout) {