Add OpenExcerptsSplit (#8574)

Conrad Irwin created

I would like to keep diagnostics open on one side, and process them on
the other.


Release Notes:

- Added `editor::OpenExcerptsSplit` (bound to `cmd-k enter`) to open the
selected excerpts in the adjacent pane
- vim: Added `ctrl-w d`, `ctrl-w shift-d` and `ctrl-w space` for
`editor::GoTo{,Type}Definition` and `editor::OpenExcerptsSplit`

Change summary

assets/keymaps/default-linux.json                   |  1 
assets/keymaps/default-macos.json                   |  1 
assets/keymaps/vim.json                             |  7 +
crates/activity_indicator/src/activity_indicator.rs |  2 
crates/auto_update/src/auto_update.rs               |  2 
crates/collab/src/tests/following_tests.rs          |  2 
crates/command_palette/src/command_palette.rs       |  2 
crates/diagnostics/src/diagnostics.rs               |  2 
crates/editor/src/actions.rs                        |  1 
crates/editor/src/editor.rs                         | 34 +++-
crates/editor/src/element.rs                        |  1 
crates/editor/src/rust_analyzer_ext.rs              |  2 
crates/extensions_ui/src/extensions_ui.rs           |  2 
crates/language_tools/src/lsp_log.rs                |  2 
crates/project_symbols/src/project_symbols.rs       |  9 
crates/search/src/project_search.rs                 |  6 
crates/terminal_view/src/terminal_view.rs           |  2 
crates/welcome/src/welcome.rs                       |  2 
crates/workspace/src/workspace.rs                   | 94 +++++++-------
crates/zed/src/zed.rs                               |  6 
20 files changed, 100 insertions(+), 80 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -467,6 +467,7 @@
     "context": "Editor && mode == full",
     "bindings": {
       "alt-enter": "editor::OpenExcerpts",
+      "ctrl-k enter": "editor::OpenExcerptsSplit",
       "ctrl-f8": "editor::GoToHunk",
       "ctrl-shift-f8": "editor::GoToPrevHunk",
       "ctrl-enter": "assistant::InlineAssist"

assets/keymaps/default-macos.json 🔗

@@ -507,6 +507,7 @@
     "context": "Editor && mode == full",
     "bindings": {
       "alt-enter": "editor::OpenExcerpts",
+      "cmd-k enter": "editor::OpenExcerptsSplit",
       "cmd-f8": "editor::GoToHunk",
       "cmd-shift-f8": "editor::GoToPrevHunk",
       "ctrl-enter": "assistant::InlineAssist"

assets/keymaps/vim.json 🔗

@@ -288,6 +288,13 @@
       "ctrl-w ctrl-o": "workspace::CloseInactiveTabsAndPanes",
       "ctrl-w n": ["workspace::NewFileInDirection", "Up"],
       "ctrl-w ctrl-n": ["workspace::NewFileInDirection", "Up"],
+
+      "ctrl-w d": "editorGoToDefinitionSplit",
+      "ctrl-w g d": "editorGoToDefinitionSplit",
+      "ctrl-w shift-d": "editorGoToTypeDefinitionSplit",
+      "ctrl-w g shift-d": "editorGoToTypeDefinitionSplit",
+      "ctrl-w space": "editor::OpenExcerptsSplit",
+      "ctrl-w g space": "editor::OpenExcerptsSplit",
       "-": "pane::RevealInProjectPanel"
     }
   },

crates/auto_update/src/auto_update.rs 🔗

@@ -242,7 +242,7 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Wo
                                 Some(tab_description),
                                 cx,
                             );
-                            workspace.add_item(Box::new(view.clone()), cx);
+                            workspace.add_item_to_active_pane(Box::new(view.clone()), cx);
                             cx.notify();
                         })
                         .log_err();

crates/collab/src/tests/following_tests.rs 🔗

@@ -310,7 +310,7 @@ async fn test_basic_following(
     let multibuffer_editor_a = workspace_a.update(cx_a, |workspace, cx| {
         let editor =
             cx.new_view(|cx| Editor::for_multibuffer(multibuffer_a, Some(project_a.clone()), cx));
-        workspace.add_item(Box::new(editor.clone()), cx);
+        workspace.add_item_to_active_pane(Box::new(editor.clone()), cx);
         editor
     });
     executor.run_until_parked();

crates/command_palette/src/command_palette.rs 🔗

@@ -468,7 +468,7 @@ mod tests {
         });
 
         workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(editor.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(editor.clone()), cx);
             editor.update(cx, |editor, cx| editor.focus(cx))
         });
 

crates/diagnostics/src/diagnostics.rs 🔗

@@ -202,7 +202,7 @@ impl ProjectDiagnosticsEditor {
             let diagnostics = cx.new_view(|cx| {
                 ProjectDiagnosticsEditor::new(workspace.project().clone(), workspace_handle, cx)
             });
-            workspace.add_item(Box::new(diagnostics), cx);
+            workspace.add_item_to_active_pane(Box::new(diagnostics), cx);
         }
     }
 

crates/editor/src/actions.rs 🔗

@@ -197,6 +197,7 @@ gpui::actions!(
         NewlineBelow,
         NextScreen,
         OpenExcerpts,
+        OpenExcerptsSplit,
         OpenPermalinkToLine,
         Outdent,
         PageDown,

crates/editor/src/editor.rs 🔗

@@ -1642,7 +1642,7 @@ impl Editor {
             .update(cx, |project, cx| project.create_buffer("", None, cx))
             .log_err()
         {
-            workspace.add_item(
+            workspace.add_item_to_active_pane(
                 Box::new(cx.new_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
                 cx,
             );
@@ -3639,7 +3639,7 @@ impl Editor {
             let project = workspace.project().clone();
             let editor =
                 cx.new_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
-            workspace.add_item(Box::new(editor.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(editor.clone()), cx);
             editor.update(cx, |editor, cx| {
                 editor.highlight_background::<Self>(
                     ranges_to_highlight,
@@ -7506,11 +7506,13 @@ impl Editor {
                             cx.window_context().defer(move |cx| {
                                 let target_editor: View<Self> =
                                     workspace.update(cx, |workspace, cx| {
-                                        if split {
-                                            workspace.split_project_item(target.buffer.clone(), cx)
+                                        let pane = if split {
+                                            workspace.adjacent_pane(cx)
                                         } else {
-                                            workspace.open_project_item(target.buffer.clone(), cx)
-                                        }
+                                            workspace.active_pane().clone()
+                                        };
+
+                                        workspace.open_project_item(pane, target.buffer.clone(), cx)
                                     });
                                 target_editor.update(cx, |target_editor, cx| {
                                     // When selecting a definition in a different buffer, disable the nav history
@@ -7747,7 +7749,7 @@ impl Editor {
         if split {
             workspace.split_item(SplitDirection::Right, Box::new(editor), cx);
         } else {
-            workspace.add_item(Box::new(editor), cx);
+            workspace.add_item_to_active_pane(Box::new(editor), cx);
         }
     }
 
@@ -9067,7 +9069,15 @@ impl Editor {
         self.searchable
     }
 
+    fn open_excerpts_in_split(&mut self, _: &OpenExcerptsSplit, cx: &mut ViewContext<Self>) {
+        self.open_excerpts_common(true, cx)
+    }
+
     fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
+        self.open_excerpts_common(false, cx)
+    }
+
+    fn open_excerpts_common(&mut self, split: bool, cx: &mut ViewContext<Self>) {
         let buffer = self.buffer.read(cx);
         if buffer.is_singleton() {
             cx.propagate();
@@ -9094,18 +9104,20 @@ impl Editor {
             }
         }
 
-        self.push_to_nav_history(self.selections.newest_anchor().head(), None, cx);
-
         // We defer the pane interaction because we ourselves are a workspace item
         // and activating a new item causes the pane to call a method on us reentrantly,
         // which panics if we're on the stack.
         cx.window_context().defer(move |cx| {
             workspace.update(cx, |workspace, cx| {
-                let pane = workspace.active_pane().clone();
+                let pane = if split {
+                    workspace.adjacent_pane(cx)
+                } else {
+                    workspace.active_pane().clone()
+                };
                 pane.update(cx, |pane, _| pane.disable_history());
 
                 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
-                    let editor = workspace.open_project_item::<Self>(buffer, cx);
+                    let editor = workspace.open_project_item::<Self>(pane.clone(), buffer, cx);
                     editor.update(cx, |editor, cx| {
                         editor.change_selections(Some(Autoscroll::newest()), cx, |s| {
                             s.select_ranges(ranges);

crates/editor/src/element.rs 🔗

@@ -273,6 +273,7 @@ impl EditorElement {
         register_action(view, cx, Editor::show_completions);
         register_action(view, cx, Editor::toggle_code_actions);
         register_action(view, cx, Editor::open_excerpts);
+        register_action(view, cx, Editor::open_excerpts_in_split);
         register_action(view, cx, Editor::toggle_soft_wrap);
         register_action(view, cx, Editor::toggle_inlay_hints);
         register_action(view, cx, hover_popover::hover);

crates/editor/src/rust_analyzer_ext.rs 🔗

@@ -105,7 +105,7 @@ pub fn expand_macro_recursively(
             let buffer = cx.new_model(|cx| {
                 MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
             });
-            workspace.add_item(
+            workspace.add_item_to_active_pane(
                 Box::new(cx.new_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
                 cx,
             );

crates/extensions_ui/src/extensions_ui.rs 🔗

@@ -24,7 +24,7 @@ pub fn init(cx: &mut AppContext) {
     cx.observe_new_views(move |workspace: &mut Workspace, _cx| {
         workspace.register_action(move |workspace, _: &Extensions, cx| {
             let extensions_page = ExtensionsPage::new(workspace, cx);
-            workspace.add_item(Box::new(extensions_page), cx)
+            workspace.add_item_to_active_pane(Box::new(extensions_page), cx)
         });
     })
     .detach();

crates/language_tools/src/lsp_log.rs 🔗

@@ -93,7 +93,7 @@ pub fn init(cx: &mut AppContext) {
         workspace.register_action(move |workspace, _: &OpenLanguageServerLogs, cx| {
             let project = workspace.project().read(cx);
             if project.is_local() {
-                workspace.add_item(
+                workspace.add_item_to_active_pane(
                     Box::new(cx.new_view(|cx| {
                         LspLogView::new(workspace.project().clone(), log_store.clone(), cx)
                     })),

crates/project_symbols/src/project_symbols.rs 🔗

@@ -127,13 +127,14 @@ impl PickerDelegate for ProjectSymbolsDelegate {
                     let position = buffer
                         .read(cx)
                         .clip_point_utf16(symbol.range.start, Bias::Left);
-
-                    let editor = if secondary {
-                        workspace.split_project_item::<Editor>(buffer, cx)
+                    let pane = if secondary {
+                        workspace.adjacent_pane(cx)
                     } else {
-                        workspace.open_project_item::<Editor>(buffer, cx)
+                        workspace.active_pane().clone()
                     };
 
+                    let editor = workspace.open_project_item::<Editor>(pane, buffer, cx);
+
                     editor.update(cx, |editor, cx| {
                         editor.change_selections(Some(Autoscroll::center()), cx, |s| {
                             s.select_ranges([position..position])

crates/search/src/project_search.rs 🔗

@@ -999,7 +999,7 @@ impl ProjectSearchView {
 
         let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
         let search = cx.new_view(|cx| ProjectSearchView::new(model, cx, None));
-        workspace.add_item(Box::new(search.clone()), cx);
+        workspace.add_item_to_active_pane(Box::new(search.clone()), cx);
         search.update(cx, |search, cx| {
             search
                 .included_files_editor
@@ -1048,7 +1048,7 @@ impl ProjectSearchView {
                     model.search(new_query, cx);
                     model
                 });
-                workspace.add_item(
+                workspace.add_item_to_active_pane(
                     Box::new(cx.new_view(|cx| ProjectSearchView::new(model, cx, None))),
                     cx,
                 );
@@ -1098,7 +1098,7 @@ impl ProjectSearchView {
             let model = cx.new_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
             let view = cx.new_view(|cx| ProjectSearchView::new(model, cx, settings));
 
-            workspace.add_item(Box::new(view.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(view.clone()), cx);
             view
         };
 

crates/welcome/src/welcome.rs 🔗

@@ -28,7 +28,7 @@ pub fn init(cx: &mut AppContext) {
     cx.observe_new_views(|workspace: &mut Workspace, _cx| {
         workspace.register_action(|workspace, _: &Welcome, cx| {
             let welcome_page = WelcomePage::new(workspace, cx);
-            workspace.add_item(Box::new(welcome_page), cx)
+            workspace.add_item_to_active_pane(Box::new(welcome_page), cx)
         });
     })
     .detach();

crates/workspace/src/workspace.rs 🔗

@@ -1885,15 +1885,23 @@ impl Workspace {
         }
     }
 
-    pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut WindowContext) {
+    pub fn add_item_to_active_pane(&mut self, item: Box<dyn ItemHandle>, cx: &mut WindowContext) {
+        self.add_item(self.active_pane.clone(), item, cx)
+    }
+
+    pub fn add_item(
+        &mut self,
+        pane: View<Pane>,
+        item: Box<dyn ItemHandle>,
+        cx: &mut WindowContext,
+    ) {
         if let Some(text) = item.telemetry_event_text(cx) {
             self.client()
                 .telemetry()
                 .report_app_event(format!("{}: open", text));
         }
 
-        self.active_pane
-            .update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
+        pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
     }
 
     pub fn split_item(
@@ -1903,9 +1911,7 @@ impl Workspace {
         cx: &mut ViewContext<Self>,
     ) {
         let new_pane = self.split_pane(self.active_pane.clone(), split_direction, cx);
-        new_pane.update(cx, move |new_pane, cx| {
-            new_pane.add_item(item, true, true, None, cx)
-        })
+        self.add_item(new_pane, item, cx);
     }
 
     pub fn open_abs_path(
@@ -2047,6 +2053,7 @@ impl Workspace {
 
     pub fn open_project_item<T>(
         &mut self,
+        pane: View<Pane>,
         project_item: Model<T::Item>,
         cx: &mut ViewContext<Self>,
     ) -> View<T>
@@ -2057,31 +2064,7 @@ impl Workspace {
 
         let entry_id = project_item.read(cx).entry_id(cx);
         if let Some(item) = entry_id
-            .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx))
-            .and_then(|item| item.downcast())
-        {
-            self.activate_item(&item, cx);
-            return item;
-        }
-
-        let item = cx.new_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
-        self.add_item(Box::new(item.clone()), cx);
-        item
-    }
-
-    pub fn split_project_item<T>(
-        &mut self,
-        project_item: Model<T::Item>,
-        cx: &mut ViewContext<Self>,
-    ) -> View<T>
-    where
-        T: ProjectItem,
-    {
-        use project::Item as _;
-
-        let entry_id = project_item.read(cx).entry_id(cx);
-        if let Some(item) = entry_id
-            .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx))
+            .and_then(|entry_id| pane.read(cx).item_for_entry(entry_id, cx))
             .and_then(|item| item.downcast())
         {
             self.activate_item(&item, cx);
@@ -2089,7 +2072,7 @@ impl Workspace {
         }
 
         let item = cx.new_view(|cx| T::for_project_item(self.project().clone(), project_item, cx));
-        self.split_item(SplitDirection::Right, Box::new(item.clone()), cx);
+        self.add_item(pane, Box::new(item.clone()), cx);
         item
     }
 
@@ -2498,6 +2481,13 @@ impl Workspace {
         &self.active_pane
     }
 
+    pub fn adjacent_pane(&mut self, cx: &mut ViewContext<Self>) -> View<Pane> {
+        self.find_pane_in_direction(SplitDirection::Right, cx)
+            .or_else(|| self.find_pane_in_direction(SplitDirection::Left, cx))
+            .unwrap_or_else(|| self.split_pane(self.active_pane.clone(), SplitDirection::Right, cx))
+            .clone()
+    }
+
     pub fn pane_for(&self, handle: &dyn ItemHandle) -> Option<View<Pane>> {
         let weak_pane = self.panes_by_item.get(&handle.item_id())?;
         weak_pane.upgrade()
@@ -4702,7 +4692,7 @@ mod tests {
             item
         });
         workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(item1.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item1.clone()), cx);
         });
         item1.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(0)));
 
@@ -4714,7 +4704,7 @@ mod tests {
             item
         });
         workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(item2.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item2.clone()), cx);
         });
         item1.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
         item2.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
@@ -4728,7 +4718,7 @@ mod tests {
             item
         });
         workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(item3.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item3.clone()), cx);
         });
         item1.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(1)));
         item2.update(cx, |item, _| assert_eq!(item.tab_detail.get(), Some(3)));
@@ -4771,7 +4761,9 @@ mod tests {
         });
 
         // Add an item to an empty pane
-        workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item1), cx));
+        workspace.update(cx, |workspace, cx| {
+            workspace.add_item_to_active_pane(Box::new(item1), cx)
+        });
         project.update(cx, |project, cx| {
             assert_eq!(
                 project.active_entry(),
@@ -4783,7 +4775,9 @@ mod tests {
         assert_eq!(cx.window_title().as_deref(), Some("one.txt — root1"));
 
         // Add a second item to a non-empty pane
-        workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
+        workspace.update(cx, |workspace, cx| {
+            workspace.add_item_to_active_pane(Box::new(item2), cx)
+        });
         assert_eq!(cx.window_title().as_deref(), Some("two.txt — root1"));
         project.update(cx, |project, cx| {
             assert_eq!(
@@ -4836,7 +4830,9 @@ mod tests {
 
         // When there are no dirty items, there's nothing to do.
         let item1 = cx.new_view(|cx| TestItem::new(cx));
-        workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
+        workspace.update(cx, |w, cx| {
+            w.add_item_to_active_pane(Box::new(item1.clone()), cx)
+        });
         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
         assert!(task.await.unwrap());
 
@@ -4849,8 +4845,8 @@ mod tests {
                 .with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
         });
         workspace.update(cx, |w, cx| {
-            w.add_item(Box::new(item2.clone()), cx);
-            w.add_item(Box::new(item3.clone()), cx);
+            w.add_item_to_active_pane(Box::new(item2.clone()), cx);
+            w.add_item_to_active_pane(Box::new(item3.clone()), cx);
         });
         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
         cx.executor().run_until_parked();
@@ -4894,10 +4890,10 @@ mod tests {
                 .with_project_items(&[TestProjectItem::new_untitled(cx)])
         });
         let pane = workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(item1.clone()), cx);
-            workspace.add_item(Box::new(item2.clone()), cx);
-            workspace.add_item(Box::new(item3.clone()), cx);
-            workspace.add_item(Box::new(item4.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item1.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item2.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item3.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item4.clone()), cx);
             workspace.active_pane().clone()
         });
 
@@ -5019,9 +5015,9 @@ mod tests {
         //     multi-entry items:   (3, 4)
         let left_pane = workspace.update(cx, |workspace, cx| {
             let left_pane = workspace.active_pane().clone();
-            workspace.add_item(Box::new(item_2_3.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item_2_3.clone()), cx);
             for item in single_entry_items {
-                workspace.add_item(Box::new(item), cx);
+                workspace.add_item_to_active_pane(Box::new(item), cx);
             }
             left_pane.update(cx, |pane, cx| {
                 pane.activate_item(2, true, true, cx);
@@ -5092,7 +5088,7 @@ mod tests {
         });
         let item_id = item.entity_id();
         workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(item.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item.clone()), cx);
         });
 
         // Autosave on window change.
@@ -5174,7 +5170,7 @@ mod tests {
 
         // Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
         workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(item.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item.clone()), cx);
         });
         item.update(cx, |item, cx| {
             item.project_items[0].update(cx, |item, _| {
@@ -5212,7 +5208,7 @@ mod tests {
         let toolbar_notify_count = Rc::new(RefCell::new(0));
 
         workspace.update(cx, |workspace, cx| {
-            workspace.add_item(Box::new(item.clone()), cx);
+            workspace.add_item_to_active_pane(Box::new(item.clone()), cx);
             let toolbar_notification_count = toolbar_notify_count.clone();
             cx.observe(&toolbar, move |_, _, _| {
                 *toolbar_notification_count.borrow_mut() += 1

crates/zed/src/zed.rs 🔗

@@ -531,7 +531,7 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
                         let buffer = cx.new_model(|cx| {
                             MultiBuffer::singleton(buffer, cx).with_title("Log".into())
                         });
-                        workspace.add_item(
+                        workspace.add_item_to_active_pane(
                             Box::new(
                                 cx.new_view(|cx| {
                                     Editor::for_multibuffer(buffer, Some(project), cx)
@@ -747,7 +747,7 @@ fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Works
                 let buffer = cx.new_model(|cx| {
                     MultiBuffer::singleton(buffer, cx).with_title("Telemetry Log".into())
                 });
-                workspace.add_item(
+                workspace.add_item_to_active_pane(
                     Box::new(cx.new_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
                     cx,
                 );
@@ -781,7 +781,7 @@ fn open_bundled_file(
                     let buffer = cx.new_model(|cx| {
                         MultiBuffer::singleton(buffer, cx).with_title(title.into())
                     });
-                    workspace.add_item(
+                    workspace.add_item_to_active_pane(
                         Box::new(cx.new_view(|cx| {
                             Editor::for_multibuffer(buffer, Some(project.clone()), cx)
                         })),