Rebase - Got Zed compiling and fixed a build error due to conflicting dependencies that cargo didn't catch :(

Mikayla Maki and kay@zed.dev created

Co-Authored-By: kay@zed.dev

Change summary

Cargo.lock                                         |   2 
Cargo.toml                                         |   1 
crates/auto_update/Cargo.toml                      |   1 
crates/auto_update/src/auto_update.rs              |  14 
crates/collab/src/integration_tests.rs             |  37 ++
crates/collab_ui/src/collab_ui.rs                  |   7 
crates/command_palette/src/command_palette.rs      |   5 
crates/db/Cargo.toml                               |   1 
crates/db/examples/serialize-pane.rs               |   7 
crates/db/src/items.rs                             | 216 ++++++++--------
crates/db/src/pane.rs                              |  32 +
crates/diagnostics/src/diagnostics.rs              |  10 
crates/editor/src/test/editor_lsp_test_context.rs  |  10 
crates/file_finder/src/file_finder.rs              |  30 +
crates/project_panel/src/project_panel.rs          |  20 +
crates/terminal/src/tests/terminal_test_context.rs |  11 
crates/vim/src/test/vim_test_context.rs            |  10 
crates/workspace/src/dock.rs                       |   5 
crates/workspace/src/pane.rs                       |  15 
crates/workspace/src/workspace.rs                  | 172 ++++++++----
crates/zed/src/main.rs                             |  13 
crates/zed/src/zed.rs                              | 178 +++++++-----
22 files changed, 486 insertions(+), 311 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -428,6 +428,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "client",
+ "db",
  "gpui",
  "isahc",
  "lazy_static",
@@ -1560,7 +1561,6 @@ dependencies = [
  "rusqlite_migration",
  "serde",
  "serde_rusqlite",
- "settings",
  "tempdir",
 ]
 

Cargo.toml 🔗

@@ -81,3 +81,4 @@ split-debuginfo = "unpacked"
 [profile.release]
 debug = true
 
+

crates/auto_update/Cargo.toml 🔗

@@ -8,6 +8,7 @@ path = "src/auto_update.rs"
 doctest = false
 
 [dependencies]
+db = { path = "../db" }
 client = { path = "../client" }
 gpui = { path = "../gpui" }
 menu = { path = "../menu" }

crates/auto_update/src/auto_update.rs 🔗

@@ -1,7 +1,8 @@
 mod update_notification;
 
 use anyhow::{anyhow, Context, Result};
-use client::{http::HttpClient, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
+use client::{http::HttpClient, ZED_SECRET_CLIENT_TOKEN};
+use db::Db;
 use gpui::{
     actions, platform::AppVersion, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
     MutableAppContext, Task, WeakViewHandle,
@@ -55,11 +56,16 @@ impl Entity for AutoUpdater {
     type Event = ();
 }
 
-pub fn init(db: project::Db, http_client: Arc<dyn HttpClient>, cx: &mut MutableAppContext) {
+pub fn init(
+    db: Db,
+    http_client: Arc<dyn HttpClient>,
+    server_url: String,
+    cx: &mut MutableAppContext,
+) {
     if let Some(version) = (*ZED_APP_VERSION).or_else(|| cx.platform().app_version().ok()) {
-        let server_url = ZED_SERVER_URL.to_string();
+        let server_url = server_url;
         let auto_updater = cx.add_model(|cx| {
-            let updater = AutoUpdater::new(version, db.clone(), http_client, server_url.clone());
+            let updater = AutoUpdater::new(version, db, http_client, server_url.clone());
             updater.start_polling(cx).detach();
             updater
         });

crates/collab/src/integration_tests.rs 🔗

@@ -905,8 +905,14 @@ async fn test_host_disconnect(
     let project_b = client_b.build_remote_project(project_id, cx_b).await;
     assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
 
-    let (_, workspace_b) =
-        cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
+    let (_, workspace_b) = cx_b.add_window(|cx| {
+        Workspace::new(
+            Default::default(),
+            project_b.clone(),
+            |_, _| unimplemented!(),
+            cx,
+        )
+    });
     let editor_b = workspace_b
         .update(cx_b, |workspace, cx| {
             workspace.open_path((worktree_id, "b.txt"), None, true, cx)
@@ -3701,8 +3707,14 @@ async fn test_collaborating_with_code_actions(
 
     // Join the project as client B.
     let project_b = client_b.build_remote_project(project_id, cx_b).await;
-    let (_window_b, workspace_b) =
-        cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
+    let (_window_b, workspace_b) = cx_b.add_window(|cx| {
+        Workspace::new(
+            Default::default(),
+            project_b.clone(),
+            |_, _| unimplemented!(),
+            cx,
+        )
+    });
     let editor_b = workspace_b
         .update(cx_b, |workspace, cx| {
             workspace.open_path((worktree_id, "main.rs"), None, true, cx)
@@ -3922,8 +3934,14 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
         .unwrap();
     let project_b = client_b.build_remote_project(project_id, cx_b).await;
 
-    let (_window_b, workspace_b) =
-        cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
+    let (_window_b, workspace_b) = cx_b.add_window(|cx| {
+        Workspace::new(
+            Default::default(),
+            project_b.clone(),
+            |_, _| unimplemented!(),
+            cx,
+        )
+    });
     let editor_b = workspace_b
         .update(cx_b, |workspace, cx| {
             workspace.open_path((worktree_id, "one.rs"), None, true, cx)
@@ -6054,7 +6072,12 @@ impl TestClient {
     ) -> ViewHandle<Workspace> {
         let (_, root_view) = cx.add_window(|_| EmptyView);
         cx.add_view(&root_view, |cx| {
-            Workspace::new(project.clone(), |_, _| unimplemented!(), cx)
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
         })
     }
 

crates/collab_ui/src/collab_ui.rs 🔗

@@ -51,7 +51,12 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
                 .await?;
 
                 let (_, workspace) = cx.add_window((app_state.build_window_options)(), |cx| {
-                    let mut workspace = Workspace::new(project, app_state.default_item_factory, cx);
+                    let mut workspace = Workspace::new(
+                        Default::default(),
+                        project,
+                        app_state.default_item_factory,
+                        cx,
+                    );
                     (app_state.initialize_workspace)(&mut workspace, &app_state, cx);
                     workspace
                 });

crates/command_palette/src/command_palette.rs 🔗

@@ -350,8 +350,9 @@ mod tests {
         });
 
         let project = Project::test(app_state.fs.clone(), [], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let editor = cx.add_view(&workspace, |cx| {
             let mut editor = Editor::single_line(None, cx);
             editor.set_text("abc", cx);

crates/db/Cargo.toml 🔗

@@ -13,7 +13,6 @@ test-support = []
 [dependencies]
 collections = { path = "../collections" }
 gpui = { path = "../gpui" }
-settings = { path = "../settings" }
 anyhow = "1.0.57"
 async-trait = "0.1"
 lazy_static = "1.4.0"

crates/db/examples/serialize-pane.rs 🔗

@@ -1,7 +1,6 @@
-use std::{fs::File, path::Path, thread::sleep, time::Duration};
+use std::{fs::File, path::Path};
 
-use db::pane::SerializedDockPane;
-use settings::DockAnchor;
+use db::pane::{DockAnchor, SerializedDockPane};
 
 const TEST_FILE: &'static str = "test-db.db";
 
@@ -25,7 +24,7 @@ fn main() -> anyhow::Result<()> {
         shown: true,
     });
 
-    let new_workspace = db.workspace_for_roots(&["/tmp"]);
+    let _new_workspace = db.workspace_for_roots(&["/tmp"]);
 
     db.write_to(file).ok();
 

crates/db/src/items.rs 🔗

@@ -1,69 +1,69 @@
-use std::{
-    ffi::OsStr,
-    fmt::Display,
-    hash::Hash,
-    os::unix::prelude::OsStrExt,
-    path::{Path, PathBuf},
-    sync::Arc,
-};
-
-use anyhow::Result;
-use collections::HashSet;
-use rusqlite::{named_params, params, types::FromSql};
-
-use crate::workspace::WorkspaceId;
-
-use super::Db;
-
-/// Current design makes the cut at the item level,
-///   - Maybe A little more bottom up, serialize 'Terminals' and 'Editors' directly, and then make a seperate
-///   - items table, with a kind, and an integer that acts as a key to one of these other tables
-/// This column is a foreign key to ONE OF: editors, terminals, searches
-///   -
-
-// (workspace_id, item_id)
-// kind -> ::Editor::
-
-// ->
-// At the workspace level
-// -> (Workspace_ID, item_id)
-// -> One shot, big query, load everything up:
-
-// -> SerializedWorkspace::deserialize(tx, itemKey)
-//     -> SerializedEditor::deserialize(tx, itemKey)
-
-//         ->
-// -> Workspace::new(SerializedWorkspace)
-//     -> Editor::new(serialized_workspace[???]serializedEditor)
-
-// //Pros: Keeps sql out of every body elese, makes changing it easier (e.g. for loading from a network or RocksDB)
-// //Cons: DB has to know the internals of the entire rest of the app
-
-// Workspace
-// Worktree roots
-// Pane groups
-// Dock
-// Items
-// Sidebars
-
-// Things I'm doing: finding about nullability for foreign keys
-pub(crate) const ITEMS_M_1: &str = "
-CREATE TABLE project_searches(
-    workspace_id INTEGER,
-    item_id INTEGER,
-    query TEXT,
-    PRIMARY KEY (workspace_id, item_id)
-    FOREIGN KEY(workspace_id) REFERENCES workspace_ids(workspace_id)
-) STRICT;
-
-CREATE TABLE editors(
-    workspace_id INTEGER,
-    item_id INTEGER,
-    path BLOB NOT NULL,
-    PRIMARY KEY (workspace_id, item_id)
-    FOREIGN KEY(workspace_id) REFERENCES workspace_ids(workspace_id)
-) STRICT;
-";
+// use std::{
+//     ffi::OsStr,
+//     fmt::Display,
+//     hash::Hash,
+//     os::unix::prelude::OsStrExt,
+//     path::{Path, PathBuf},
+//     sync::Arc,
+// };
+
+// use anyhow::Result;
+// use collections::HashSet;
+// use rusqlite::{named_params, params, types::FromSql};
+
+// use crate::workspace::WorkspaceId;
+
+// use super::Db;
+
+// /// Current design makes the cut at the item level,
+// ///   - Maybe A little more bottom up, serialize 'Terminals' and 'Editors' directly, and then make a seperate
+// ///   - items table, with a kind, and an integer that acts as a key to one of these other tables
+// /// This column is a foreign key to ONE OF: editors, terminals, searches
+// ///   -
+
+// // (workspace_id, item_id)
+// // kind -> ::Editor::
+
+// // ->
+// // At the workspace level
+// // -> (Workspace_ID, item_id)
+// // -> One shot, big query, load everything up:
+
+// // -> SerializedWorkspace::deserialize(tx, itemKey)
+// //     -> SerializedEditor::deserialize(tx, itemKey)
+
+// //         ->
+// // -> Workspace::new(SerializedWorkspace)
+// //     -> Editor::new(serialized_workspace[???]serializedEditor)
+
+// // //Pros: Keeps sql out of every body elese, makes changing it easier (e.g. for loading from a network or RocksDB)
+// // //Cons: DB has to know the internals of the entire rest of the app
+
+// // Workspace
+// // Worktree roots
+// // Pane groups
+// // Dock
+// // Items
+// // Sidebars
+
+// // Things I'm doing: finding about nullability for foreign keys
+// pub(crate) const ITEMS_M_1: &str = "
+// CREATE TABLE project_searches(
+//     workspace_id INTEGER,
+//     item_id INTEGER,
+//     query TEXT,
+//     PRIMARY KEY (workspace_id, item_id)
+//     FOREIGN KEY(workspace_id) REFERENCES workspace_ids(workspace_id)
+// ) STRICT;
+
+// CREATE TABLE editors(
+//     workspace_id INTEGER,
+//     item_id INTEGER,
+//     path BLOB NOT NULL,
+//     PRIMARY KEY (workspace_id, item_id)
+//     FOREIGN KEY(workspace_id) REFERENCES workspace_ids(workspace_id)
+// ) STRICT;
+// ";
 
 #[derive(Debug, PartialEq, Eq)]
 pub struct ItemId {
@@ -71,45 +71,45 @@ pub struct ItemId {
     item_id: usize,
 }
 
-enum SerializedItemKind {
-    Editor,
-    Diagnostics,
-    ProjectSearch,
-    Terminal,
-}
-
-struct SerializedItemRow {
-    kind: SerializedItemKind,
-    item_id: usize,
-    path: Option<Arc<Path>>,
-    query: Option<String>,
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum SerializedItem {
-    Editor { item_id: usize, path: Arc<Path> },
-    Diagnostics { item_id: usize },
-    ProjectSearch { item_id: usize, query: String },
-    Terminal { item_id: usize },
-}
-
-impl SerializedItem {
-    pub fn item_id(&self) -> usize {
-        match self {
-            SerializedItem::Editor { item_id, .. } => *item_id,
-            SerializedItem::Diagnostics { item_id } => *item_id,
-            SerializedItem::ProjectSearch { item_id, .. } => *item_id,
-            SerializedItem::Terminal { item_id } => *item_id,
-        }
-    }
-}
-
-impl Db {
-    pub fn get_item(&self, item_id: ItemId) -> SerializedItem {
-        unimplemented!()
-    }
-
-    pub fn save_item(&self, workspace_id: WorkspaceId, item: &SerializedItem) {}
-
-    pub fn close_item(&self, item_id: ItemId) {}
-}
+// enum SerializedItemKind {
+//     Editor,
+//     Diagnostics,
+//     ProjectSearch,
+//     Terminal,
+// }
+
+// struct SerializedItemRow {
+//     kind: SerializedItemKind,
+//     item_id: usize,
+//     path: Option<Arc<Path>>,
+//     query: Option<String>,
+// }
+
+// #[derive(Debug, PartialEq, Eq)]
+// pub enum SerializedItem {
+//     Editor { item_id: usize, path: Arc<Path> },
+//     Diagnostics { item_id: usize },
+//     ProjectSearch { item_id: usize, query: String },
+//     Terminal { item_id: usize },
+// }
+
+// impl SerializedItem {
+//     pub fn item_id(&self) -> usize {
+//         match self {
+//             SerializedItem::Editor { item_id, .. } => *item_id,
+//             SerializedItem::Diagnostics { item_id } => *item_id,
+//             SerializedItem::ProjectSearch { item_id, .. } => *item_id,
+//             SerializedItem::Terminal { item_id } => *item_id,
+//         }
+//     }
+// }
+
+// impl Db {
+//     pub fn get_item(&self, item_id: ItemId) -> SerializedItem {
+//         unimplemented!()
+//     }
+
+//     pub fn save_item(&self, workspace_id: WorkspaceId, item: &SerializedItem) {}
+
+//     pub fn close_item(&self, item_id: ItemId) {}
+// }

crates/db/src/pane.rs 🔗

@@ -1,5 +1,4 @@
 use gpui::Axis;
-use settings::DockAnchor;
 
 use crate::{items::ItemId, workspace::WorkspaceId};
 
@@ -32,7 +31,7 @@ pub struct PaneGroupId {
 }
 
 impl PaneGroupId {
-    pub(crate) fn root(workspace_id: WorkspaceId) -> Self {
+    pub fn root(workspace_id: WorkspaceId) -> Self {
         Self {
             workspace_id,
             group_id: 0,
@@ -48,7 +47,7 @@ pub struct SerializedPaneGroup {
 }
 
 impl SerializedPaneGroup {
-    pub(crate) fn empty_root(workspace_id: WorkspaceId) -> Self {
+    pub fn empty_root(workspace_id: WorkspaceId) -> Self {
         Self {
             group_id: PaneGroupId::root(workspace_id),
             axis: Default::default(),
@@ -136,6 +135,14 @@ CREATE TABLE dock_items(
 COMMIT;
 ";
 
+#[derive(Default, Debug)]
+pub enum DockAnchor {
+    #[default]
+    Bottom,
+    Right,
+    Expanded,
+}
+
 #[derive(Default, Debug)]
 pub struct SerializedDockPane {
     pub workspace: WorkspaceId,
@@ -144,7 +151,7 @@ pub struct SerializedDockPane {
 }
 
 impl Db {
-    pub(crate) fn get_pane_group(&self, pane_group_id: PaneGroupId) -> SerializedPaneGroup {
+    pub fn get_pane_group(&self, pane_group_id: PaneGroupId) -> SerializedPaneGroup {
         let axis = self.get_pane_group_axis(pane_group_id);
         let mut children: Vec<(usize, PaneGroupChild)> = Vec::new();
         for child_row in self.get_pane_group_children(pane_group_id) {
@@ -177,40 +184,39 @@ impl Db {
 
     fn get_pane_group_children(
         &self,
-        pane_group_id: PaneGroupId,
+        _pane_group_id: PaneGroupId,
     ) -> impl Iterator<Item = PaneGroupChildRow> {
         Vec::new().into_iter()
     }
 
-    fn get_pane_group_axis(&self, pane_group_id: PaneGroupId) -> Axis {
+    fn get_pane_group_axis(&self, _pane_group_id: PaneGroupId) -> Axis {
         unimplemented!();
     }
 
-    pub fn save_pane_splits(&self, center_pane_group: SerializedPaneGroup) {
+    pub fn save_pane_splits(&self, _center_pane_group: SerializedPaneGroup) {
         // Delete the center pane group for this workspace and any of its children
         // Generate new pane group IDs as we go through
         // insert them
         // Items garbage collect themselves when dropped
     }
 
-    pub(crate) fn get_pane(&self, pane_id: PaneId) -> SerializedPane {
+    pub(crate) fn get_pane(&self, _pane_id: PaneId) -> SerializedPane {
         unimplemented!();
     }
 
-    pub fn get_dock_pane(&self, workspace: WorkspaceId) -> Option<SerializedDockPane> {
+    pub fn get_dock_pane(&self, _workspace: WorkspaceId) -> Option<SerializedDockPane> {
         unimplemented!()
     }
 
-    pub fn save_dock_pane(&self, dock_pane: SerializedDockPane) {}
+    pub fn save_dock_pane(&self, _dock_pane: SerializedDockPane) {}
 }
 
 #[cfg(test)]
 mod tests {
-    use settings::DockAnchor;
 
     use crate::Db;
 
-    use super::SerializedDockPane;
+    use super::{DockAnchor, SerializedDockPane};
 
     #[test]
     fn test_basic_dock_pane() {
@@ -226,6 +232,6 @@ mod tests {
             shown: true,
         });
 
-        let new_workspace = db.workspace_for_roots(&["/tmp"]);
+        let _new_workspace = db.workspace_for_roots(&["/tmp"]);
     }
 }

crates/diagnostics/src/diagnostics.rs 🔗

@@ -781,8 +781,14 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
+        });
 
         // Create some diagnostics
         project.update(cx, |project, cx| {

crates/editor/src/test/editor_lsp_test_context.rs 🔗

@@ -63,8 +63,14 @@ impl<'a> EditorLspTestContext<'a> {
             .insert_tree("/root", json!({ "dir": { file_name: "" }}))
             .await;
 
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
+        });
         project
             .update(cx, |project, cx| {
                 project.find_or_create_local_worktree("/root", true, cx)

crates/file_finder/src/file_finder.rs 🔗

@@ -316,8 +316,9 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         cx.dispatch_action(window_id, Toggle);
 
         let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
@@ -371,8 +372,9 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let (_, finder) =
             cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
 
@@ -446,8 +448,9 @@ mod tests {
             cx,
         )
         .await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let (_, finder) =
             cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
         finder
@@ -471,8 +474,9 @@ mod tests {
             cx,
         )
         .await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let (_, finder) =
             cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
 
@@ -524,8 +528,9 @@ mod tests {
             cx,
         )
         .await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let (_, finder) =
             cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
 
@@ -563,8 +568,9 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let (_, finder) =
             cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), cx));
         finder

crates/project_panel/src/project_panel.rs 🔗

@@ -1393,8 +1393,14 @@ mod tests {
         .await;
 
         let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
+        });
         let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
         assert_eq!(
             visible_entries_as_strings(&panel, 0..50, cx),
@@ -1486,8 +1492,14 @@ mod tests {
         .await;
 
         let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
+        });
         let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx));
 
         select_path(&panel, "root1", cx);

crates/terminal/src/tests/terminal_test_context.rs 🔗

@@ -28,9 +28,14 @@ impl<'a> TerminalTestContext<'a> {
         let params = self.cx.update(AppState::test);
 
         let project = Project::test(params.fs.clone(), [], self.cx).await;
-        let (_, workspace) = self
-            .cx
-            .add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
+        let (_, workspace) = self.cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
+        });
 
         (project, workspace)
     }

crates/vim/src/test/vim_test_context.rs 🔗

@@ -41,8 +41,14 @@ impl<'a> VimTestContext<'a> {
             .insert_tree("/root", json!({ "dir": { "test.txt": "" } }))
             .await;
 
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
+        });
 
         // Setup search toolbars
         workspace.update(cx, |workspace, cx| {

crates/workspace/src/dock.rs 🔗

@@ -568,8 +568,9 @@ mod tests {
 
             cx.update(|cx| init(cx));
             let project = Project::test(fs, [], cx).await;
-            let (window_id, workspace) =
-                cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
+            let (window_id, workspace) = cx.add_window(|cx| {
+                Workspace::new(Default::default(), project, default_item_factory, cx)
+            });
 
             workspace.update(cx, |workspace, cx| {
                 let left_panel = cx.add_view(|_| TestItem::new());

crates/workspace/src/pane.rs 🔗

@@ -1645,8 +1645,9 @@ mod tests {
         let fs = FakeFs::new(cx.background());
 
         let project = Project::test(fs, None, cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
 
         // 1. Add with a destination index
@@ -1734,8 +1735,9 @@ mod tests {
         let fs = FakeFs::new(cx.background());
 
         let project = Project::test(fs, None, cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
 
         // 1. Add with a destination index
@@ -1811,8 +1813,9 @@ mod tests {
         let fs = FakeFs::new(cx.background());
 
         let project = Project::test(fs, None, cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
 
         // singleton view

crates/workspace/src/workspace.rs 🔗

@@ -1073,7 +1073,7 @@ pub enum Event {
 
 pub struct Workspace {
     weak_self: WeakViewHandle<Self>,
-    db_id: WorkspaceId,
+    _db_id: WorkspaceId,
     client: Arc<Client>,
     user_store: ModelHandle<client::UserStore>,
     remote_entity_subscription: Option<client::Subscription>,
@@ -1217,7 +1217,7 @@ impl Workspace {
         let mut this = Workspace {
             modal: None,
             weak_self: weak_handle,
-            db_id: serialized_workspace.workspace_id,
+            _db_id: serialized_workspace.workspace_id,
             center: PaneGroup::new(center_pane.clone()),
             dock,
             // When removing an item, the last element remaining in this array
@@ -1250,16 +1250,14 @@ impl Workspace {
         this
     }
 
-    fn new_local<T, F>(
-        abs_paths: &[PathBuf],
-        app_state: &Arc<AppState>,
+    fn new_local(
+        abs_paths: Vec<PathBuf>,
+        app_state: Arc<AppState>,
         cx: &mut MutableAppContext,
-        callback: F,
-    ) -> Task<T>
-    where
-        T: 'static,
-        F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
-    {
+    ) -> Task<(
+        ViewHandle<Workspace>,
+        Vec<Option<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>>>,
+    )> {
         let project_handle = Project::local(
             app_state.client.clone(),
             app_state.user_store.clone(),
@@ -1273,21 +1271,25 @@ impl Workspace {
             // Get project paths for all of the abs_paths
             let mut worktree_roots: HashSet<Arc<Path>> = Default::default();
             let mut project_paths = Vec::new();
-            for path in abs_paths {
+            for path in abs_paths.iter() {
                 if let Some((worktree, project_entry)) = cx
-                    .update(|cx| Workspace::project_path_for_path(project_handle, path, true, cx))
+                    .update(|cx| {
+                        Workspace::project_path_for_path(project_handle.clone(), &path, true, cx)
+                    })
                     .await
                     .log_err()
                 {
                     worktree_roots.insert(worktree.read_with(&mut cx, |tree, _| tree.abs_path()));
-                    project_paths.push(project_entry);
+                    project_paths.push(Some(project_entry));
+                } else {
+                    project_paths.push(None);
                 }
             }
 
             // Use the resolved worktree roots to get the serialized_db from the database
             let serialized_workspace = cx.read(|cx| {
                 cx.global::<Db>()
-                    .workspace_for_worktree_roots(&Vec::from_iter(worktree_roots.into_iter())[..])
+                    .workspace_for_roots(&Vec::from_iter(worktree_roots.into_iter())[..])
             });
 
             // Use the serialized workspace to construct the new window
@@ -1303,18 +1305,36 @@ impl Workspace {
             });
 
             // Call open path for each of the project paths
-            // (this will bring them to the front if they were in kthe serialized workspace)
-            let tasks = workspace.update(&mut cx, |workspace, cx| {
-                let tasks = Vec::new();
-                for path in project_paths {
-                    tasks.push(workspace.open_path(path, true, cx));
-                }
-                tasks
-            });
-            futures::future::join_all(tasks.into_iter()).await;
+            // (this will bring them to the front if they were in the serialized workspace)
+            debug_assert!(abs_paths.len() == project_paths.len());
+            let tasks = abs_paths
+                .iter()
+                .cloned()
+                .zip(project_paths.into_iter())
+                .map(|(abs_path, project_path)| {
+                    let workspace = workspace.clone();
+                    cx.spawn(|mut cx| {
+                        let fs = app_state.fs.clone();
+                        async move {
+                            let project_path = project_path?;
+                            if fs.is_file(&abs_path).await {
+                                Some(
+                                    workspace
+                                        .update(&mut cx, |workspace, cx| {
+                                            workspace.open_path(project_path, true, cx)
+                                        })
+                                        .await,
+                                )
+                            } else {
+                                None
+                            }
+                        }
+                    })
+                });
+
+            let opened_items = futures::future::join_all(tasks.into_iter()).await;
 
-            // Finally call callback on the workspace
-            workspace.update(&mut cx, |workspace, cx| callback(workspace, cx))
+            (workspace, opened_items)
         })
     }
 
@@ -1371,12 +1391,16 @@ impl Workspace {
     ) -> Task<T>
     where
         T: 'static,
-        F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
+        F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
     {
         if self.project.read(cx).is_local() {
             Task::Ready(Some(callback(self, cx)))
         } else {
-            Self::new_local(&[], app_state, cx, callback)
+            let task = Self::new_local(Vec::new(), app_state.clone(), cx);
+            cx.spawn(|_vh, mut cx| async move {
+                let (workspace, _) = task.await;
+                workspace.update(&mut cx, callback)
+            })
         }
     }
 
@@ -1539,7 +1563,7 @@ impl Workspace {
             for path in &abs_paths {
                 project_paths.push(
                     this.update(&mut cx, |this, cx| {
-                        Workspace::project_path_for_path(this.project, path, visible, cx)
+                        Workspace::project_path_for_path(this.project.clone(), path, visible, cx)
                     })
                     .await
                     .log_err(),
@@ -3017,8 +3041,15 @@ pub fn open_paths(
     let app_state = app_state.clone();
     let abs_paths = abs_paths.to_vec();
     cx.spawn(|mut cx| async move {
-        let workspace = if let Some(existing) = existing {
-            existing
+        if let Some(existing) = existing {
+            (
+                existing.clone(),
+                existing
+                    .update(&mut cx, |workspace, cx| {
+                        workspace.open_paths(abs_paths, true, cx)
+                    })
+                    .await,
+            )
         } else {
             let contains_directory =
                 futures::future::join_all(abs_paths.iter().map(|path| app_state.fs.is_file(path)))
@@ -3026,28 +3057,32 @@ pub fn open_paths(
                     .contains(&false);
 
             cx.update(|cx| {
-                Workspace::new_local(&abs_paths[..], &app_state, cx, move |workspace, cx| {
-                    if contains_directory {
-                        workspace.toggle_sidebar(SidebarSide::Left, cx);
-                    }
-                    cx.handle()
+                let task = Workspace::new_local(abs_paths, app_state.clone(), cx);
+
+                cx.spawn(|mut cx| async move {
+                    let (workspace, items) = task.await;
+
+                    workspace.update(&mut cx, |workspace, cx| {
+                        if contains_directory {
+                            workspace.toggle_sidebar(SidebarSide::Left, cx);
+                        }
+                    });
+
+                    (workspace, items)
                 })
             })
             .await
-        };
-
-        let items = workspace
-            .update(&mut cx, |workspace, cx| {
-                workspace.open_paths(abs_paths, true, cx)
-            })
-            .await;
-
-        (workspace, items)
+        }
     })
 }
 
 fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) -> Task<()> {
-    Workspace::new_local(&[], app_state, cx, |_, cx| cx.dispatch_action(NewFile))
+    let task = Workspace::new_local(Vec::new(), app_state.clone(), cx);
+    cx.spawn(|mut cx| async move {
+        let (workspace, _) = task.await;
+
+        workspace.update(&mut cx, |_, cx| cx.dispatch_action(NewFile))
+    })
 }
 
 #[cfg(test)]
@@ -3076,8 +3111,14 @@ mod tests {
 
         let fs = FakeFs::new(cx.background());
         let project = Project::test(fs, [], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                default_item_factory,
+                cx,
+            )
+        });
 
         // Adding an item with no ambiguity renders the tab without detail.
         let item1 = cx.add_view(&workspace, |_| {
@@ -3141,8 +3182,14 @@ mod tests {
         .await;
 
         let project = Project::test(fs, ["root1".as_ref()], cx).await;
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                default_item_factory,
+                cx,
+            )
+        });
         let worktree_id = project.read_with(cx, |project, cx| {
             project.worktrees(cx).next().unwrap().read(cx).id()
         });
@@ -3238,8 +3285,14 @@ mod tests {
         fs.insert_tree("/root", json!({ "one": "" })).await;
 
         let project = Project::test(fs, ["root".as_ref()], cx).await;
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), default_item_factory, cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                default_item_factory,
+                cx,
+            )
+        });
 
         // When there are no dirty items, there's nothing to do.
         let item1 = cx.add_view(&workspace, |_| TestItem::new());
@@ -3279,8 +3332,8 @@ mod tests {
         let fs = FakeFs::new(cx.background());
 
         let project = Project::test(fs, None, cx).await;
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
+        let (window_id, workspace) = cx
+            .add_window(|cx| Workspace::new(Default::default(), project, default_item_factory, cx));
 
         let item1 = cx.add_view(&workspace, |_| {
             let mut item = TestItem::new();
@@ -3375,8 +3428,8 @@ mod tests {
         let fs = FakeFs::new(cx.background());
 
         let project = Project::test(fs, [], cx).await;
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
+        let (window_id, workspace) = cx
+            .add_window(|cx| Workspace::new(Default::default(), project, default_item_factory, cx));
 
         // Create several workspace items with single project entries, and two
         // workspace items with multiple project entries.
@@ -3477,8 +3530,8 @@ mod tests {
         let fs = FakeFs::new(cx.background());
 
         let project = Project::test(fs, [], cx).await;
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
+        let (window_id, workspace) = cx
+            .add_window(|cx| Workspace::new(Default::default(), project, default_item_factory, cx));
 
         let item = cx.add_view(&workspace, |_| {
             let mut item = TestItem::new();
@@ -3595,7 +3648,8 @@ mod tests {
         let fs = FakeFs::new(cx.background());
 
         let project = Project::test(fs, [], cx).await;
-        let (_, workspace) = cx.add_window(|cx| Workspace::new(project, default_item_factory, cx));
+        let (_, workspace) = cx
+            .add_window(|cx| Workspace::new(Default::default(), project, default_item_factory, cx));
 
         let item = cx.add_view(&workspace, |_| {
             let mut item = TestItem::new();

crates/zed/src/main.rs 🔗

@@ -23,7 +23,7 @@ use isahc::{config::Configurable, Request};
 use language::LanguageRegistry;
 use log::LevelFilter;
 use parking_lot::Mutex;
-use project::{Fs, HomeDir, ProjectStore};
+use project::{Db, Fs, HomeDir, ProjectStore};
 use serde_json::json;
 use settings::{
     self, settings_file::SettingsFile, KeymapFileContent, Settings, SettingsFileContent,
@@ -148,7 +148,9 @@ fn main() {
 
         let project_store = cx.add_model(|_| ProjectStore::new());
         let db = cx.background().block(db);
-        client.start_telemetry(db.clone());
+        cx.set_global(db);
+
+        client.start_telemetry(cx.global::<Db>().clone());
         client.report_event("start app", Default::default());
 
         let app_state = Arc::new(AppState {
@@ -162,7 +164,12 @@ fn main() {
             initialize_workspace,
             default_item_factory,
         });
-        auto_update::init(db, http, cx);
+        auto_update::init(
+            cx.global::<Db>().clone(),
+            http,
+            client::ZED_SERVER_URL.clone(),
+            cx,
+        );
         workspace::init(app_state.clone(), cx);
         journal::init(app_state.clone(), cx);
         theme_selector::init(app_state.clone(), cx);

crates/zed/src/zed.rs 🔗

@@ -463,10 +463,11 @@ fn open_config_file(
 
         workspace
             .update(&mut cx, |workspace, cx| {
-                workspace.with_local_workspace(app_state, cx, |workspace, cx| {
+                workspace.with_local_workspace(&app_state, cx, |workspace, cx| {
                     workspace.open_paths(vec![path.to_path_buf()], false, cx)
                 })
             })
+            .await
             .await;
         Ok::<_, anyhow::Error>(())
     })
@@ -480,51 +481,55 @@ fn open_log_file(
 ) {
     const MAX_LINES: usize = 1000;
 
-    workspace.with_local_workspace(app_state.clone(), cx, |_, cx| {
-        cx.spawn_weak(|workspace, mut cx| async move {
-            let (old_log, new_log) = futures::join!(
-                app_state.fs.load(&paths::OLD_LOG),
-                app_state.fs.load(&paths::LOG)
-            );
+    workspace
+        .with_local_workspace(&app_state.clone(), cx, move |_, cx| {
+            cx.spawn_weak(|workspace, mut cx| async move {
+                let (old_log, new_log) = futures::join!(
+                    app_state.fs.load(&paths::OLD_LOG),
+                    app_state.fs.load(&paths::LOG)
+                );
 
-            if let Some(workspace) = workspace.upgrade(&cx) {
-                let mut lines = VecDeque::with_capacity(MAX_LINES);
-                for line in old_log
-                    .iter()
-                    .flat_map(|log| log.lines())
-                    .chain(new_log.iter().flat_map(|log| log.lines()))
-                {
-                    if lines.len() == MAX_LINES {
-                        lines.pop_front();
+                if let Some(workspace) = workspace.upgrade(&cx) {
+                    let mut lines = VecDeque::with_capacity(MAX_LINES);
+                    for line in old_log
+                        .iter()
+                        .flat_map(|log| log.lines())
+                        .chain(new_log.iter().flat_map(|log| log.lines()))
+                    {
+                        if lines.len() == MAX_LINES {
+                            lines.pop_front();
+                        }
+                        lines.push_back(line);
                     }
-                    lines.push_back(line);
-                }
-                let log = lines
-                    .into_iter()
-                    .flat_map(|line| [line, "\n"])
-                    .collect::<String>();
-
-                workspace.update(&mut cx, |workspace, cx| {
-                    let project = workspace.project().clone();
-                    let buffer = project
-                        .update(cx, |project, cx| project.create_buffer("", None, cx))
-                        .expect("creating buffers on a local workspace always succeeds");
-                    buffer.update(cx, |buffer, cx| buffer.edit([(0..0, log)], None, cx));
-
-                    let buffer = cx.add_model(|cx| {
-                        MultiBuffer::singleton(buffer, cx).with_title("Log".into())
+                    let log = lines
+                        .into_iter()
+                        .flat_map(|line| [line, "\n"])
+                        .collect::<String>();
+
+                    workspace.update(&mut cx, |workspace, cx| {
+                        let project = workspace.project().clone();
+                        let buffer = project
+                            .update(cx, |project, cx| project.create_buffer("", None, cx))
+                            .expect("creating buffers on a local workspace always succeeds");
+                        buffer.update(cx, |buffer, cx| buffer.edit([(0..0, log)], None, cx));
+
+                        let buffer = cx.add_model(|cx| {
+                            MultiBuffer::singleton(buffer, cx).with_title("Log".into())
+                        });
+                        workspace.add_item(
+                            Box::new(
+                                cx.add_view(|cx| {
+                                    Editor::for_multibuffer(buffer, Some(project), cx)
+                                }),
+                            ),
+                            cx,
+                        );
                     });
-                    workspace.add_item(
-                        Box::new(
-                            cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx)),
-                        ),
-                        cx,
-                    );
-                });
-            }
+                }
+            })
+            .detach();
         })
         .detach();
-    });
 }
 
 fn open_telemetry_log_file(
@@ -532,7 +537,7 @@ fn open_telemetry_log_file(
     app_state: Arc<AppState>,
     cx: &mut ViewContext<Workspace>,
 ) {
-    workspace.with_local_workspace(app_state.clone(), cx, |_, cx| {
+    workspace.with_local_workspace(&app_state.clone(), cx, move |_, cx| {
         cx.spawn_weak(|workspace, mut cx| async move {
             let workspace = workspace.upgrade(&cx)?;
             let path = app_state.client.telemetry_log_file_path()?;
@@ -580,31 +585,36 @@ fn open_telemetry_log_file(
             Some(())
         })
         .detach();
-    });
+    }).detach();
 }
 
 fn open_bundled_config_file(
     workspace: &mut Workspace,
     app_state: Arc<AppState>,
     asset_path: &'static str,
-    title: &str,
+    title: &'static str,
     cx: &mut ViewContext<Workspace>,
 ) {
-    workspace.with_local_workspace(cx, app_state, |workspace, cx| {
-        let project = workspace.project().clone();
-        let buffer = project.update(cx, |project, cx| {
-            let text = Assets::get(asset_path).unwrap().data;
-            let text = str::from_utf8(text.as_ref()).unwrap();
-            project
-                .create_buffer(text, project.languages().get_language("JSON"), cx)
-                .expect("creating buffers on a local workspace always succeeds")
-        });
-        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx).with_title(title.into()));
-        workspace.add_item(
-            Box::new(cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project.clone()), cx))),
-            cx,
-        );
-    });
+    workspace
+        .with_local_workspace(&app_state.clone(), cx, |workspace, cx| {
+            let project = workspace.project().clone();
+            let buffer = project.update(cx, |project, cx| {
+                let text = Assets::get(asset_path).unwrap().data;
+                let text = str::from_utf8(text.as_ref()).unwrap();
+                project
+                    .create_buffer(text, project.languages().get_language("JSON"), cx)
+                    .expect("creating buffers on a local workspace always succeeds")
+            });
+            let buffer =
+                cx.add_model(|cx| MultiBuffer::singleton(buffer, cx).with_title(title.into()));
+            workspace.add_item(
+                Box::new(
+                    cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project.clone()), cx)),
+                ),
+                cx,
+            );
+        })
+        .detach();
 }
 
 fn schema_file_match(path: &Path) -> &Path {
@@ -808,8 +818,9 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
 
         let entries = cx.read(|cx| workspace.file_project_paths(cx));
         let file1 = entries[0].clone();
@@ -928,8 +939,9 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/dir1".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
 
         // Open a file within an existing worktree.
         cx.update(|cx| {
@@ -1088,8 +1100,9 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
 
         // Open a file within an existing worktree.
         cx.update(|cx| {
@@ -1131,8 +1144,9 @@ mod tests {
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
         project.update(cx, |project, _| project.languages().add(rust_lang()));
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
         let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
 
         // Create a new untitled buffer
@@ -1221,8 +1235,9 @@ mod tests {
 
         let project = Project::test(app_state.fs.clone(), [], cx).await;
         project.update(cx, |project, _| project.languages().add(rust_lang()));
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
 
         // Create a new untitled buffer
         cx.dispatch_action(window_id, NewFile);
@@ -1275,8 +1290,9 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (window_id, workspace) =
-            cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
+        let (window_id, workspace) = cx.add_window(|cx| {
+            Workspace::new(Default::default(), project, |_, _| unimplemented!(), cx)
+        });
 
         let entries = cx.read(|cx| workspace.file_project_paths(cx));
         let file1 = entries[0].clone();
@@ -1350,8 +1366,14 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
+        });
 
         let entries = cx.read(|cx| workspace.file_project_paths(cx));
         let file1 = entries[0].clone();
@@ -1615,8 +1637,14 @@ mod tests {
             .await;
 
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
-        let (_, workspace) =
-            cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
+        let (_, workspace) = cx.add_window(|cx| {
+            Workspace::new(
+                Default::default(),
+                project.clone(),
+                |_, _| unimplemented!(),
+                cx,
+            )
+        });
         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
 
         let entries = cx.read(|cx| workspace.file_project_paths(cx));