Properly handle opening of file-less excerpts (#21465)

Kirill Bulatov created

Follow-up of https://github.com/zed-industries/zed/pull/20491 and
https://github.com/zed-industries/zed/pull/20469
Closes https://github.com/zed-industries/zed/issues/21369

Release Notes:

- Fixed file-less excerpts always opening instead of activating

Change summary

crates/editor/src/editor.rs       | 37 +++++++++++++++++++++++++++++++-
crates/editor/src/editor_tests.rs |  2 
2 files changed, 36 insertions(+), 3 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -12833,8 +12833,41 @@ impl Editor {
                 };
 
                 for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
-                    let editor =
-                        workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
+                    let editor = buffer
+                        .read(cx)
+                        .file()
+                        .is_none()
+                        .then(|| {
+                            // Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
+                            // so `workspace.open_project_item` will never find them, always opening a new editor.
+                            // Instead, we try to activate the existing editor in the pane first.
+                            let (editor, pane_item_index) =
+                                pane.read(cx).items().enumerate().find_map(|(i, item)| {
+                                    let editor = item.downcast::<Editor>()?;
+                                    let singleton_buffer =
+                                        editor.read(cx).buffer().read(cx).as_singleton()?;
+                                    if singleton_buffer == buffer {
+                                        Some((editor, i))
+                                    } else {
+                                        None
+                                    }
+                                })?;
+                            pane.update(cx, |pane, cx| {
+                                pane.activate_item(pane_item_index, true, true, cx)
+                            });
+                            Some(editor)
+                        })
+                        .flatten()
+                        .unwrap_or_else(|| {
+                            workspace.open_project_item::<Self>(
+                                pane.clone(),
+                                buffer,
+                                true,
+                                true,
+                                cx,
+                            )
+                        });
+
                     editor.update(cx, |editor, cx| {
                         let autoscroll = match scroll_offset {
                             Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),

crates/editor/src/editor_tests.rs 🔗

@@ -11805,7 +11805,7 @@ async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
 
     multi_buffer_editor.update(cx, |editor, cx| {
         editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-            s.select_ranges(Some(60..70))
+            s.select_ranges(Some(70..70))
         });
         editor.open_excerpts(&OpenExcerpts, cx);
     });