Don't open a multibuffer when code actions's edits are contained in the current editor

Max Brunsfeld , Antonio Scandurra , and Nathan Sobo created

Co-Authored-By: Antonio Scandurra <me@as-cii.com>
Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/diagnostics/src/diagnostics.rs |  2 
crates/editor/src/editor.rs           | 29 ++++++++++++++++++++++++++
crates/editor/src/multi_buffer.rs     | 32 +++++++++++++++++++++++++---
3 files changed, 58 insertions(+), 5 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

@@ -191,7 +191,7 @@ impl ProjectDiagnosticsEditor {
 
             for selection in editor.local_selections::<usize>(cx) {
                 for (buffer, mut range) in
-                    excerpts.excerpted_buffers(selection.start..selection.end, cx)
+                    excerpts.range_to_buffer_ranges(selection.start..selection.end, cx)
                 {
                     if selection.reversed {
                         mem::swap(&mut range.start, &mut range.end);

crates/editor/src/editor.rs 🔗

@@ -2114,6 +2114,35 @@ impl Editor {
         Some(cx.spawn(|workspace, mut cx| async move {
             let project_transaction = apply_code_actions.await?;
 
+            // If the code action's edits are all contained within this editor, then
+            // avoid opening a new editor to display them.
+            let mut entries = project_transaction.0.iter();
+            if let Some((buffer, transaction)) = entries.next() {
+                if entries.next().is_none() {
+                    let excerpt = editor.read_with(&cx, |editor, cx| {
+                        editor
+                            .buffer()
+                            .read(cx)
+                            .excerpt_containing(editor.newest_anchor_selection().head(), cx)
+                    });
+                    if let Some((excerpted_buffer, excerpt_range)) = excerpt {
+                        if excerpted_buffer == *buffer {
+                            let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
+                            let excerpt_range = excerpt_range.to_offset(&snapshot);
+                            if snapshot
+                                .edited_ranges_for_transaction(transaction)
+                                .all(|range| {
+                                    excerpt_range.start <= range.start
+                                        && excerpt_range.end >= range.end
+                                })
+                            {
+                                return Ok(());
+                            }
+                        }
+                    }
+                }
+            }
+
             let mut ranges_to_highlight = Vec::new();
             let excerpt_buffer = cx.add_model(|cx| {
                 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);

crates/editor/src/multi_buffer.rs 🔗

@@ -779,12 +779,35 @@ impl MultiBuffer {
             .map_or(Vec::new(), |state| state.excerpts.clone())
     }
 
-    pub fn excerpted_buffers<'a, T: ToOffset>(
+    pub fn excerpt_containing(
+        &self,
+        position: impl ToOffset,
+        cx: &AppContext,
+    ) -> Option<(ModelHandle<Buffer>, Range<text::Anchor>)> {
+        let snapshot = self.read(cx);
+        let position = position.to_offset(&snapshot);
+
+        let mut cursor = snapshot.excerpts.cursor::<usize>();
+        cursor.seek(&position, Bias::Right, &());
+        cursor.item().map(|excerpt| {
+            (
+                self.buffers
+                    .borrow()
+                    .get(&excerpt.buffer_id)
+                    .unwrap()
+                    .buffer
+                    .clone(),
+                excerpt.range.clone(),
+            )
+        })
+    }
+
+    pub fn range_to_buffer_ranges<'a, T: ToOffset>(
         &'a self,
         range: Range<T>,
         cx: &AppContext,
     ) -> Vec<(ModelHandle<Buffer>, Range<usize>)> {
-        let snapshot = self.snapshot(cx);
+        let snapshot = self.read(cx);
         let start = range.start.to_offset(&snapshot);
         let end = range.end.to_offset(&snapshot);
 
@@ -3538,8 +3561,9 @@ mod tests {
                     start_ix..end_ix
                 );
 
-                let excerpted_buffer_ranges =
-                    multibuffer.read(cx).excerpted_buffers(start_ix..end_ix, cx);
+                let excerpted_buffer_ranges = multibuffer
+                    .read(cx)
+                    .range_to_buffer_ranges(start_ix..end_ix, cx);
                 let excerpted_buffers_text = excerpted_buffer_ranges
                     .into_iter()
                     .map(|(buffer, buffer_range)| {