Finish refactoring of how editors are opened

Antonio Scandurra created

Change summary

crates/diagnostics/src/diagnostics.rs |   2 
crates/editor/src/editor.rs           |  41 ++--
crates/editor/src/items.rs            |  28 ---
crates/file_finder/src/file_finder.rs |   6 
crates/gpui/src/app.rs                |   6 
crates/search/src/project_search.rs   |   4 
crates/server/src/rpc.rs              |   8 
crates/workspace/src/pane.rs          |  16 -
crates/workspace/src/workspace.rs     | 236 ++++++----------------------
crates/zed/src/main.rs                |   4 
crates/zed/src/test.rs                |   4 
crates/zed/src/zed.rs                 |   1 
12 files changed, 96 insertions(+), 260 deletions(-)

Detailed changes

crates/diagnostics/src/diagnostics.rs 🔗

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

crates/editor/src/editor.rs 🔗

@@ -57,7 +57,7 @@ pub use sum_tree::Bias;
 use text::rope::TextDimension;
 use theme::DiagnosticStyle;
 use util::{post_inc, ResultExt, TryFutureExt};
-use workspace::{settings, ItemNavHistory, PathOpener, Settings, Workspace};
+use workspace::{settings, ItemNavHistory, Settings, Workspace};
 
 const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
 const MAX_LINE_LEN: usize = 1024;
@@ -141,8 +141,7 @@ pub enum Direction {
     Next,
 }
 
-pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpener>>) {
-    path_openers.push(Box::new(items::BufferOpener));
+pub fn init(cx: &mut MutableAppContext) {
     cx.add_bindings(vec![
         Binding::new("escape", Cancel, Some("Editor")),
         Binding::new("backspace", Backspace, Some("Editor")),
@@ -340,6 +339,11 @@ pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpene
     cx.add_async_action(Editor::rename);
     cx.add_async_action(Editor::confirm_rename);
     cx.add_async_action(Editor::find_all_references);
+
+    workspace::register_editor_builder(cx, |project, buffer, cx| {
+        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+        Editor::for_buffer(buffer, Some(project), cx)
+    });
 }
 
 trait SelectionExt {
@@ -842,18 +846,17 @@ impl Editor {
     ) -> ViewHandle<Editor> {
         let project = workspace.project().clone();
 
-        if let Some(project_entry) = project::File::from_dyn(buffer.read(cx).file())
+        if let Some(item) = project::File::from_dyn(buffer.read(cx).file())
             .and_then(|file| file.project_entry_id(cx))
+            .and_then(|entry_id| workspace.item_for_entry(entry_id, cx))
+            .and_then(|item| item.downcast())
         {
-            return workspace
-                .open_editor(project_entry, cx)
-                .downcast::<Editor>()
-                .unwrap();
+            return item;
         }
 
         let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
         let editor = cx.add_view(|cx| Editor::for_buffer(multibuffer, Some(project.clone()), cx));
-        workspace.open_item(Box::new(editor.clone()), cx);
+        workspace.add_item(Box::new(editor.clone()), cx);
         editor
     }
 
@@ -966,7 +969,7 @@ impl Editor {
             .log_err()
         {
             let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-            workspace.open_item(
+            workspace.add_item(
                 Box::new(
                     cx.add_view(|cx| Editor::for_buffer(multibuffer, Some(project.clone()), cx)),
                 ),
@@ -2376,16 +2379,12 @@ impl Editor {
 
         workspace.update(&mut cx, |workspace, cx| {
             let project = workspace.project().clone();
-            let editor = workspace.open_item(
-                Box::new(cx.add_view(|cx| Editor::for_buffer(excerpt_buffer, Some(project), cx))),
-                cx,
-            );
-            if let Some(editor) = editor.act_as::<Self>(cx) {
-                editor.update(cx, |editor, cx| {
-                    let color = editor.style(cx).highlighted_line_background;
-                    editor.highlight_background::<Self>(ranges_to_highlight, color, cx);
-                });
-            }
+            let editor = cx.add_view(|cx| Editor::for_buffer(excerpt_buffer, Some(project), cx));
+            workspace.add_item(Box::new(editor.clone()), cx);
+            editor.update(cx, |editor, cx| {
+                let color = editor.style(cx).highlighted_line_background;
+                editor.highlight_background::<Self>(ranges_to_highlight, color, cx);
+            });
         });
 
         Ok(())
@@ -4402,7 +4401,7 @@ impl Editor {
                     let color = editor.style(cx).highlighted_line_background;
                     editor.highlight_background::<Self>(ranges_to_highlight, color, cx);
                 });
-                workspace.open_item(Box::new(editor), cx);
+                workspace.add_item(Box::new(editor), cx);
             });
 
             Ok(())

crates/editor/src/items.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{Autoscroll, Editor, Event, MultiBuffer, NavigationData, ToOffset, ToPoint as _};
 use anyhow::Result;
 use gpui::{
-    elements::*, AppContext, Entity, ModelContext, ModelHandle, RenderContext, Subscription, Task,
-    View, ViewContext, ViewHandle, WeakModelHandle,
+    elements::*, AppContext, Entity, ModelHandle, RenderContext, Subscription, Task, View,
+    ViewContext, ViewHandle, WeakModelHandle,
 };
 use language::{Bias, Buffer, Diagnostic, File as _};
 use project::{File, Project, ProjectPath};
@@ -10,9 +10,7 @@ use std::fmt::Write;
 use std::path::PathBuf;
 use text::{Point, Selection};
 use util::ResultExt;
-use workspace::{ItemNavHistory, ItemView, ItemViewHandle, PathOpener, Settings, StatusItemView};
-
-pub struct BufferOpener;
+use workspace::{ItemNavHistory, ItemView, ItemViewHandle, Settings, StatusItemView};
 
 #[derive(Clone)]
 pub struct BufferItemHandle(pub ModelHandle<Buffer>);
@@ -26,26 +24,6 @@ pub struct MultiBufferItemHandle(pub ModelHandle<MultiBuffer>);
 #[derive(Clone)]
 struct WeakMultiBufferItemHandle(WeakModelHandle<MultiBuffer>);
 
-impl PathOpener for BufferOpener {
-    fn open(
-        &self,
-        project: &mut Project,
-        project_path: ProjectPath,
-        window_id: usize,
-        cx: &mut ModelContext<Project>,
-    ) -> Option<Task<Result<Box<dyn ItemViewHandle>>>> {
-        let buffer = project.open_buffer_for_path(project_path, cx);
-        Some(cx.spawn(|project, mut cx| async move {
-            let buffer = buffer.await?;
-            let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
-            let editor = cx.add_view(window_id, |cx| {
-                Editor::for_buffer(multibuffer, Some(project), cx)
-            });
-            Ok(Box::new(editor) as Box<dyn ItemViewHandle>)
-        }))
-    }
-}
-
 impl ItemView for Editor {
     fn navigate(&mut self, data: Box<dyn std::any::Any>, cx: &mut ViewContext<Self>) {
         if let Some(data) = data.downcast_ref::<NavigationData>() {

crates/file_finder/src/file_finder.rs 🔗

@@ -409,14 +409,12 @@ mod tests {
 
     #[gpui::test]
     async fn test_matching_paths(cx: &mut gpui::TestAppContext) {
-        let mut path_openers = Vec::new();
         cx.update(|cx| {
             super::init(cx);
-            editor::init(cx, &mut path_openers);
+            editor::init(cx);
         });
 
-        let mut params = cx.update(WorkspaceParams::test);
-        params.path_openers = Arc::from(path_openers);
+        let params = cx.update(WorkspaceParams::test);
         params
             .fs
             .as_fake()

crates/gpui/src/app.rs 🔗

@@ -3473,6 +3473,12 @@ impl<T> PartialEq<WeakViewHandle<T>> for ViewHandle<T> {
     }
 }
 
+impl<T> PartialEq<ViewHandle<T>> for WeakViewHandle<T> {
+    fn eq(&self, other: &ViewHandle<T>) -> bool {
+        self.window_id == other.window_id && self.view_id == other.view_id
+    }
+}
+
 impl<T> Eq for ViewHandle<T> {}
 
 impl<T> Debug for ViewHandle<T> {

crates/search/src/project_search.rs 🔗

@@ -391,7 +391,7 @@ impl ProjectSearchView {
             workspace.activate_item(&existing, cx);
         } else {
             let model = cx.add_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
-            workspace.open_item(
+            workspace.add_item(
                 Box::new(cx.add_view(|cx| ProjectSearchView::new(model, cx))),
                 cx,
             );
@@ -429,7 +429,7 @@ impl ProjectSearchView {
                     model.search(new_query, cx);
                     model
                 });
-                workspace.open_item(
+                workspace.add_item(
                     Box::new(cx.add_view(|cx| ProjectSearchView::new(model, cx))),
                     cx,
                 );

crates/server/src/rpc.rs 🔗

@@ -3201,8 +3201,7 @@ mod tests {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
-        let mut path_openers_b = Vec::new();
-        cx_b.update(|cx| editor::init(cx, &mut path_openers_b));
+        cx_b.update(|cx| editor::init(cx));
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
@@ -3271,7 +3270,6 @@ mod tests {
         params.client = client_b.client.clone();
         params.user_store = client_b.user_store.clone();
         params.project = project_b;
-        params.path_openers = path_openers_b.into();
 
         let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(&params, cx));
         let editor_b = workspace_b
@@ -3437,8 +3435,7 @@ mod tests {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::test());
         let fs = FakeFs::new(cx_a.background());
-        let mut path_openers_b = Vec::new();
-        cx_b.update(|cx| editor::init(cx, &mut path_openers_b));
+        cx_b.update(|cx| editor::init(cx));
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
@@ -3507,7 +3504,6 @@ mod tests {
         params.client = client_b.client.clone();
         params.user_store = client_b.user_store.clone();
         params.project = project_b;
-        params.path_openers = path_openers_b.into();
 
         let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(&params, cx));
         let editor_b = workspace_b

crates/workspace/src/pane.rs 🔗

@@ -253,12 +253,12 @@ impl Pane {
             let pane = pane.downgrade();
             let task = workspace.load_path(project_path, cx);
             cx.spawn(|workspace, mut cx| async move {
-                let item = task.await;
+                let task = task.await;
                 if let Some(pane) = pane.upgrade(&cx) {
-                    if let Some(item) = item.log_err() {
+                    if let Some((project_entry_id, build_item)) = task.log_err() {
                         pane.update(&mut cx, |pane, cx| {
                             pane.nav_history.borrow_mut().set_mode(mode);
-                            pane.open_item(item, cx);
+                            let item = pane.open_item(project_entry_id, cx, build_item);
                             pane.nav_history
                                 .borrow_mut()
                                 .set_mode(NavigationMode::Normal);
@@ -280,7 +280,7 @@ impl Pane {
         }
     }
 
-    pub(crate) fn open_editor(
+    pub(crate) fn open_item(
         &mut self,
         project_entry_id: ProjectEntryId,
         cx: &mut ViewContext<Self>,
@@ -299,14 +299,6 @@ impl Pane {
         item_view
     }
 
-    pub fn open_item(
-        &mut self,
-        item_view_to_open: Box<dyn ItemViewHandle>,
-        cx: &mut ViewContext<Self>,
-    ) {
-        self.add_item(None, item_view_to_open.boxed_clone(), cx);
-    }
-
     pub(crate) fn add_item(
         &mut self,
         project_entry_id: Option<ProjectEntryId>,

crates/workspace/src/workspace.rs 🔗

@@ -9,7 +9,6 @@ mod status_bar;
 use anyhow::{anyhow, Result};
 use client::{Authenticate, ChannelList, Client, User, UserStore};
 use clock::ReplicaId;
-use futures::TryFutureExt;
 use gpui::{
     action,
     color::Color,
@@ -18,9 +17,9 @@ use gpui::{
     json::{self, to_string_pretty, ToJson},
     keymap::Binding,
     platform::{CursorStyle, WindowOptions},
-    AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, ModelContext, ModelHandle,
-    MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext,
-    ViewHandle, WeakViewHandle,
+    AnyViewHandle, AppContext, ClipboardItem, Entity, ImageData, ModelHandle, MutableAppContext,
+    PathPromptOptions, PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle,
+    WeakViewHandle,
 };
 use language::{Buffer, LanguageRegistry};
 use log::error;
@@ -42,7 +41,7 @@ use std::{
 };
 use theme::{Theme, ThemeRegistry};
 
-pub type BuildEditor = Box<
+pub type BuildEditor = Arc<
     dyn Fn(
         usize,
         ModelHandle<Project>,
@@ -110,7 +109,7 @@ where
     V: ItemView,
     F: 'static + Fn(ModelHandle<Project>, ModelHandle<Buffer>, &mut ViewContext<V>) -> V,
 {
-    cx.add_app_state::<BuildEditor>(Box::new(|window_id, project, model, cx| {
+    cx.add_app_state::<BuildEditor>(Arc::new(move |window_id, project, model, cx| {
         Box::new(cx.add_view(window_id, |cx| build_editor(project, model, cx)))
     }));
 }
@@ -122,7 +121,6 @@ pub struct AppState {
     pub user_store: ModelHandle<client::UserStore>,
     pub fs: Arc<dyn fs::Fs>,
     pub channel_list: ModelHandle<client::ChannelList>,
-    pub path_openers: Arc<[Box<dyn PathOpener>]>,
     pub build_window_options: &'static dyn Fn() -> WindowOptions<'static>,
     pub build_workspace: &'static dyn Fn(
         ModelHandle<Project>,
@@ -143,16 +141,6 @@ pub struct JoinProjectParams {
     pub app_state: Arc<AppState>,
 }
 
-pub trait PathOpener {
-    fn open(
-        &self,
-        project: &mut Project,
-        path: ProjectPath,
-        window_id: usize,
-        cx: &mut ModelContext<Project>,
-    ) -> Option<Task<Result<Box<dyn ItemViewHandle>>>>;
-}
-
 pub trait ItemView: View {
     fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
     fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) {}
@@ -378,7 +366,6 @@ pub struct WorkspaceParams {
     pub languages: Arc<LanguageRegistry>,
     pub user_store: ModelHandle<UserStore>,
     pub channel_list: ModelHandle<ChannelList>,
-    pub path_openers: Arc<[Box<dyn PathOpener>]>,
 }
 
 impl WorkspaceParams {
@@ -409,7 +396,6 @@ impl WorkspaceParams {
             fs,
             languages,
             user_store,
-            path_openers: Arc::from([]),
         }
     }
 
@@ -428,7 +414,6 @@ impl WorkspaceParams {
             languages: app_state.languages.clone(),
             user_store: app_state.user_store.clone(),
             channel_list: app_state.channel_list.clone(),
-            path_openers: app_state.path_openers.clone(),
         }
     }
 }
@@ -446,7 +431,6 @@ pub struct Workspace {
     active_pane: ViewHandle<Pane>,
     status_bar: ViewHandle<StatusBar>,
     project: ModelHandle<Project>,
-    path_openers: Arc<[Box<dyn PathOpener>]>,
     // items: BTreeMap<Reverse<usize>, Box<dyn WeakItemHandle>>,
     _observe_current_user: Task<()>,
 }
@@ -510,7 +494,6 @@ impl Workspace {
             left_sidebar: Sidebar::new(Side::Left),
             right_sidebar: Sidebar::new(Side::Right),
             project: params.project.clone(),
-            path_openers: params.path_openers.clone(),
             _observe_current_user,
         }
     }
@@ -665,76 +648,14 @@ impl Workspace {
         }
     }
 
-    pub fn open_path(
-        &mut self,
-        path: ProjectPath,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<Box<dyn ItemViewHandle>, Arc<anyhow::Error>>> {
-        let project_entry = self.project.read(cx).entry_for_path(&path, cx);
-
-        let existing_entry = self
-            .active_pane()
-            .update(cx, |pane, cx| pane.activate_project_entry(project_entry));
-
-        cx.spawn(|this, cx| {
-            if let Some(existing_entry) = existing_entry {
-                return Ok(existing_entry);
-            }
-
-            let load_task = this
-                .update(&mut cx, |this, cx| {
-                    this.load_project_entry(project_entry, cx)
-                })
-                .await;
-        });
-
-        let load_task = self.load_path(path, cx);
-        let pane = self.active_pane().clone().downgrade();
-        cx.as_mut().spawn(|mut cx| async move {
-            let item = load_task.await?;
-            let pane = pane
-                .upgrade(&cx)
-                .ok_or_else(|| anyhow!("could not upgrade pane reference"))?;
-            Ok(pane.update(&mut cx, |pane, cx| pane.open_editor(item, cx)))
-        })
-    }
-
-    pub fn load_path(
-        &mut self,
-        path: ProjectPath,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<Box<dyn ItemViewHandle>>> {
-        if let Some(project_entry) = self.project.read(cx).entry_for_path(&path, cx) {
-            self.load_project_entry(project_entry, cx)
-        } else {
-            Task::ready(Err(anyhow!("no such file {:?}", path)))
-        }
-    }
-
-    pub fn load_project_entry(
-        &mut self,
-        project_entry: ProjectEntryId,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<Box<dyn ItemViewHandle>>> {
-        if let Some(existing_item) = self
-            .panes
+    pub fn item_for_entry(
+        &self,
+        entry_id: ProjectEntryId,
+        cx: &AppContext,
+    ) -> Option<Box<dyn ItemViewHandle>> {
+        self.panes()
             .iter()
-            .find_map(|pane| pane.read(cx).item_for_entry(project_entry))
-        {
-            return Task::ready(Ok(existing_item));
-        }
-
-        let project_path = path.clone();
-        let path_openers = self.path_openers.clone();
-        let window_id = cx.window_id();
-        self.project.update(cx, |project, cx| {
-            for opener in path_openers.iter() {
-                if let Some(task) = opener.open(project, project_path.clone(), window_id, cx) {
-                    return task;
-                }
-            }
-            Task::ready(Err(anyhow!("no opener found for path {:?}", project_path)))
-        })
+            .find_map(|pane| pane.read(cx).item_for_entry(entry_id))
     }
 
     pub fn item_of_type<T: ItemView>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
@@ -871,107 +792,58 @@ impl Workspace {
         pane
     }
 
-    pub fn open_item(&mut self, item_view: Box<dyn ItemViewHandle>, cx: &mut ViewContext<Self>) {
+    pub fn add_item(&mut self, item_view: Box<dyn ItemViewHandle>, cx: &mut ViewContext<Self>) {
         self.active_pane()
-            .update(cx, |pane, cx| pane.open_item(item_view, cx))
+            .update(cx, |pane, cx| pane.add_item(None, item_view, cx))
     }
 
-    pub fn open_editor(
+    pub fn open_path(
         &mut self,
-        project_entry: ProjectEntryId,
+        path: ProjectPath,
         cx: &mut ViewContext<Self>,
     ) -> Task<Result<Box<dyn ItemViewHandle>, Arc<anyhow::Error>>> {
-        let pane = self.active_pane().clone();
-        let project = self.project().clone();
-        let buffer = project.update(cx, |project, cx| {
-            project.open_buffer_for_entry(project_entry, cx)
-        });
-
-        cx.spawn(|this, cx| async move {
-            let buffer = buffer.await?;
-            let editor = this.update(&mut cx, |this, cx| {
-                let window_id = cx.window_id();
+        let pane = self.active_pane().downgrade();
+        let task = self.load_path(path, cx);
+        cx.spawn(|this, mut cx| async move {
+            let (project_entry_id, build_editor) = task.await?;
+            let pane = pane
+                .upgrade(&cx)
+                .ok_or_else(|| anyhow!("pane was closed"))?;
+            this.update(&mut cx, |_, cx| {
                 pane.update(cx, |pane, cx| {
-                    pane.open_editor(project_entry, cx, |cx| {
-                        cx.app_state::<BuildEditor>()(window_id, project, buffer, cx)
-                    })
+                    Ok(pane.open_item(project_entry_id, cx, build_editor))
                 })
-            });
-            Ok(editor)
+            })
         })
     }
 
-    //     pub fn open_path(
-    //     &mut self,
-    //     path: ProjectPath,
-    //     cx: &mut ViewContext<Self>,
-    // ) -> Task<Result<Box<dyn ItemViewHandle>, Arc<anyhow::Error>>> {
-    //     let project_entry = self.project.read(cx).entry_for_path(&path, cx);
-
-    //     let existing_entry = self
-    //         .active_pane()
-    //         .update(cx, |pane, cx| pane.activate_project_entry(project_entry));
-
-    //     cx.spawn(|this, cx| {
-    //         if let Some(existing_entry) = existing_entry {
-    //             return Ok(existing_entry);
-    //         }
-
-    //         let load_task = this
-    //             .update(&mut cx, |this, cx| {
-    //                 this.load_project_entry(project_entry, cx)
-    //             })
-    //             .await;
-    //     });
-
-    //     let load_task = self.load_path(path, cx);
-    //     let pane = self.active_pane().clone().downgrade();
-    //     cx.as_mut().spawn(|mut cx| async move {
-    //         let item = load_task.await?;
-    //         let pane = pane
-    //             .upgrade(&cx)
-    //             .ok_or_else(|| anyhow!("could not upgrade pane reference"))?;
-    //         Ok(pane.update(&mut cx, |pane, cx| pane.open_editor(item, cx)))
-    //     })
-    // }
-
-    // pub fn load_path(
-    //     &mut self,
-    //     path: ProjectPath,
-    //     cx: &mut ViewContext<Self>,
-    // ) -> Task<Result<Box<dyn ItemViewHandle>>> {
-    //     if let Some(project_entry) = self.project.read(cx).entry_for_path(&path, cx) {
-    //         self.load_project_entry(project_entry, cx)
-    //     } else {
-    //         Task::ready(Err(anyhow!("no such file {:?}", path)))
-    //     }
-    // }
-
-    // pub fn load_project_entry(
-    //     &mut self,
-    //     project_entry: ProjectEntryId,
-    //     cx: &mut ViewContext<Self>,
-    // ) -> Task<Result<Box<dyn ItemViewHandle>>> {
-    //     if let Some(existing_item) = self
-    //         .panes
-    //         .iter()
-    //         .find_map(|pane| pane.read(cx).item_for_entry(project_entry))
-    //     {
-    //         return Task::ready(Ok(existing_item));
-    //     }
-
-    //     let project_path = path.clone();
-    //     let path_openers = self.path_openers.clone();
-    //     let window_id = cx.window_id();
-    //     self.project.update(cx, |project, cx| {
-    //         for opener in path_openers.iter() {
-    //             if let Some(task) = opener.open(project, project_path.clone(), window_id, cx) {
-    //                 return task;
-    //             }
-    //         }
-    //         Task::ready(Err(anyhow!("no opener found for path {:?}", project_path)))
-    //     })
-    // }
+    pub(crate) fn load_path(
+        &mut self,
+        path: ProjectPath,
+        cx: &mut ViewContext<Self>,
+    ) -> Task<
+        Result<(
+            ProjectEntryId,
+            impl 'static + FnOnce(&mut MutableAppContext) -> Box<dyn ItemViewHandle>,
+        )>,
+    > {
+        let project = self.project().clone();
+        let buffer = project.update(cx, |project, cx| project.open_buffer_for_path(path, cx));
+        cx.spawn(|this, mut cx| async move {
+            let buffer = buffer.await?;
+            let project_entry_id = buffer.read_with(&cx, |buffer, cx| {
+                project::File::from_dyn(buffer.file())
+                    .and_then(|file| file.project_entry_id(cx))
+                    .ok_or_else(|| anyhow!("buffer has no entry"))
+            })?;
+            let (window_id, build_editor) = this.update(&mut cx, |_, cx| {
+                (cx.window_id(), cx.app_state::<BuildEditor>().clone())
+            });
+            let build_editor =
+                move |cx: &mut MutableAppContext| build_editor(window_id, project, buffer, cx);
+            Ok((project_entry_id, build_editor))
+        })
+    }
 
     pub fn activate_item(&mut self, item: &dyn ItemViewHandle, cx: &mut ViewContext<Self>) -> bool {
         let result = self.panes.iter().find_map(|pane| {
@@ -1046,7 +918,7 @@ impl Workspace {
             let project_entry_id = pane.read(cx).project_entry_id_for_item(item.as_ref());
             if let Some(clone) = item.clone_on_split(cx.as_mut()) {
                 new_pane.update(cx, |new_pane, cx| {
-                    new_pane.open_item(project_entry_id, clone, cx);
+                    new_pane.add_item(project_entry_id, clone, cx);
                 });
             }
         }

crates/zed/src/main.rs 🔗

@@ -61,7 +61,6 @@ fn main() {
     app.run(move |cx| {
         let http = http::client();
         let client = client::Client::new(http.clone());
-        let mut path_openers = Vec::new();
         let mut languages = language::build_language_registry(login_shell_env_loaded);
         let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
         let channel_list =
@@ -71,7 +70,7 @@ fn main() {
         client::Channel::init(&client);
         client::init(client.clone(), cx);
         workspace::init(cx);
-        editor::init(cx, &mut path_openers);
+        editor::init(cx);
         go_to_line::init(cx);
         file_finder::init(cx);
         chat_panel::init(cx);
@@ -120,7 +119,6 @@ fn main() {
             client,
             user_store,
             fs,
-            path_openers: Arc::from(path_openers),
             build_window_options: &build_window_options,
             build_workspace: &build_workspace,
         });

crates/zed/src/test.rs 🔗

@@ -17,8 +17,7 @@ fn init_logger() {
 
 pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
     let settings = Settings::test(cx);
-    let mut path_openers = Vec::new();
-    editor::init(cx, &mut path_openers);
+    editor::init(cx);
     cx.add_app_state(settings);
     let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
     let http = FakeHttpClient::with_404_response();
@@ -40,7 +39,6 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
         client,
         user_store,
         fs: FakeFs::new(cx.background().clone()),
-        path_openers: Arc::from(path_openers),
         build_window_options: &build_window_options,
         build_workspace: &build_workspace,
     })

crates/zed/src/zed.rs 🔗

@@ -111,7 +111,6 @@ pub fn build_workspace(
         languages: app_state.languages.clone(),
         user_store: app_state.user_store.clone(),
         channel_list: app_state.channel_list.clone(),
-        path_openers: app_state.path_openers.clone(),
     };
     let mut workspace = Workspace::new(&workspace_params, cx);
     let project = workspace.project().clone();