WIP

Nathan Sobo created

Change summary

crates/gpui2/src/app.rs               |  48 ++--
crates/gpui2/src/app/async_context.rs |  36 ++
crates/gpui2/src/app/entity_map.rs    |   2 
crates/gpui2/src/app/model_context.rs |  13 +
crates/gpui2/src/app/test_context.rs  |   9 
crates/gpui2/src/gpui2.rs             |  53 ++++
crates/gpui2/src/window.rs            |  78 +++++-
crates/workspace2/src/workspace2.rs   | 315 ++++++++++++++--------------
crates/zed2/src/main.rs               |  29 +-
9 files changed, 367 insertions(+), 216 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -267,30 +267,6 @@ impl AppContext {
             .collect()
     }
 
-    pub(crate) fn update_window<R>(
-        &mut self,
-        handle: AnyWindowHandle,
-        update: impl FnOnce(&mut WindowContext) -> R,
-    ) -> Result<R> {
-        self.update(|cx| {
-            let mut window = cx
-                .windows
-                .get_mut(handle.id)
-                .ok_or_else(|| anyhow!("window not found"))?
-                .take()
-                .unwrap();
-
-            let result = update(&mut WindowContext::new(cx, &mut window));
-
-            cx.windows
-                .get_mut(handle.id)
-                .ok_or_else(|| anyhow!("window not found"))?
-                .replace(window);
-
-            Ok(result)
-        })
-    }
-
     pub fn update_window_root<V, R>(
         &mut self,
         handle: &WindowHandle<V>,
@@ -753,6 +729,7 @@ impl AppContext {
 }
 
 impl Context for AppContext {
+    type WindowContext<'a> = WindowContext<'a>;
     type ModelContext<'a, T> = ModelContext<'a, T>;
     type Result<T> = T;
 
@@ -784,6 +761,29 @@ impl Context for AppContext {
             result
         })
     }
+
+    fn update_window<T, F>(&mut self, handle: AnyWindowHandle, update: F) -> Result<T>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> T,
+    {
+        self.update(|cx| {
+            let mut window = cx
+                .windows
+                .get_mut(handle.id)
+                .ok_or_else(|| anyhow!("window not found"))?
+                .take()
+                .unwrap();
+
+            let result = update(&mut WindowContext::new(cx, &mut window));
+
+            cx.windows
+                .get_mut(handle.id)
+                .ok_or_else(|| anyhow!("window not found"))?
+                .replace(window);
+
+            Ok(result)
+        })
+    }
 }
 
 impl<C> MainThread<C>

crates/gpui2/src/app/async_context.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
-    AnyWindowHandle, AppContext, Context, Executor, MainThread, Model, ModelContext, Result, Task,
-    View, ViewContext, VisualContext, WindowContext, WindowHandle,
+    AnyWindowHandle, AppContext, Context, Executor, MainThread, Model, ModelContext, Render,
+    Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle,
 };
 use anyhow::Context as _;
 use derive_more::{Deref, DerefMut};
@@ -14,6 +14,7 @@ pub struct AsyncAppContext {
 }
 
 impl Context for AsyncAppContext {
+    type WindowContext<'a> = WindowContext<'a>;
     type ModelContext<'a, T> = ModelContext<'a, T>;
     type Result<T> = Result<T>;
 
@@ -38,6 +39,13 @@ impl Context for AsyncAppContext {
         let mut lock = app.lock(); // Need this to compile
         Ok(lock.update_model(handle, update))
     }
+
+    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> T,
+    {
+        todo!()
+    }
 }
 
 impl AsyncAppContext {
@@ -60,12 +68,12 @@ impl AsyncAppContext {
 
     pub fn update_window<R>(
         &self,
-        handle: AnyWindowHandle,
+        window: AnyWindowHandle,
         update: impl FnOnce(&mut WindowContext) -> R,
     ) -> Result<R> {
         let app = self.app.upgrade().context("app was released")?;
         let mut app_context = app.lock();
-        app_context.update_window(handle, update)
+        app_context.update_window(window, update)
     }
 
     pub fn update_window_root<V, R>(
@@ -224,7 +232,9 @@ impl AsyncWindowContext {
 }
 
 impl Context for AsyncWindowContext {
+    type WindowContext<'a> = WindowContext<'a>;
     type ModelContext<'a, T> = ModelContext<'a, T>;
+
     type Result<T> = Result<T>;
 
     fn build_model<T>(
@@ -246,6 +256,13 @@ impl Context for AsyncWindowContext {
         self.app
             .update_window(self.window, |cx| cx.update_model(handle, update))
     }
+
+    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> T,
+    {
+        self.app.update_window(window, update)
+    }
 }
 
 impl VisualContext for AsyncWindowContext {
@@ -270,6 +287,17 @@ impl VisualContext for AsyncWindowContext {
         self.app
             .update_window(self.window, |cx| cx.update_view(view, update))
     }
+
+    fn replace_root_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut Self::ViewContext<'_, V>) -> V,
+    ) -> Self::Result<View<V>>
+    where
+        V: 'static + Send + Render,
+    {
+        self.app
+            .update_window(self.window, |cx| cx.replace_root_view(build_view))
+    }
 }
 
 #[cfg(test)]

crates/gpui2/src/app/entity_map.rs 🔗

@@ -1,4 +1,4 @@
-use crate::{private::Sealed, AnyBox, AppContext, Context, Entity};
+use crate::{private::Sealed, AnyBox, AppContext, AsyncAppContext, Context, Entity, ModelContext};
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};

crates/gpui2/src/app/model_context.rs 🔗

@@ -1,7 +1,8 @@
 use crate::{
-    AppContext, AsyncAppContext, Context, Effect, Entity, EntityId, EventEmitter, MainThread,
-    Model, Subscription, Task, WeakModel,
+    AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId, EventEmitter,
+    MainThread, Model, Subscription, Task, WeakModel, WindowContext,
 };
+use anyhow::Result;
 use derive_more::{Deref, DerefMut};
 use futures::FutureExt;
 use std::{
@@ -228,6 +229,7 @@ where
 }
 
 impl<'a, T> Context for ModelContext<'a, T> {
+    type WindowContext<'b> = WindowContext<'b>;
     type ModelContext<'b, U> = ModelContext<'b, U>;
     type Result<U> = U;
 
@@ -248,6 +250,13 @@ impl<'a, T> Context for ModelContext<'a, T> {
     ) -> R {
         self.app.update_model(handle, update)
     }
+
+    fn update_window<R, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<R>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> R,
+    {
+        self.app.update_window(window, update)
+    }
 }
 
 impl<T> Borrow<AppContext> for ModelContext<'_, T> {

crates/gpui2/src/app/test_context.rs 🔗

@@ -12,6 +12,7 @@ pub struct TestAppContext {
 }
 
 impl Context for TestAppContext {
+    type WindowContext<'a> = WindowContext<'a>;
     type ModelContext<'a, T> = ModelContext<'a, T>;
     type Result<T> = T;
 
@@ -34,6 +35,14 @@ impl Context for TestAppContext {
         let mut lock = self.app.lock();
         lock.update_model(handle, update)
     }
+
+    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> T,
+    {
+        let mut lock = self.app.lock();
+        lock.update_window(window, f)
+    }
 }
 
 impl TestAppContext {

crates/gpui2/src/gpui2.rs 🔗

@@ -77,6 +77,7 @@ use taffy::TaffyLayoutEngine;
 type AnyBox = Box<dyn Any + Send>;
 
 pub trait Context {
+    type WindowContext<'a>: VisualContext;
     type ModelContext<'a, T>;
     type Result<T>;
 
@@ -87,11 +88,17 @@ pub trait Context {
     where
         T: 'static + Send;
 
-    fn update_model<T: 'static, R>(
+    fn update_model<T, R>(
         &mut self,
         handle: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
-    ) -> Self::Result<R>;
+    ) -> Self::Result<R>
+    where
+        T: 'static;
+
+    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> T;
 }
 
 pub trait VisualContext: Context {
@@ -99,7 +106,7 @@ pub trait VisualContext: Context {
 
     fn build_view<V>(
         &mut self,
-        build_view_state: impl FnOnce(&mut Self::ViewContext<'_, V>) -> V,
+        build_view: impl FnOnce(&mut Self::ViewContext<'_, V>) -> V,
     ) -> Self::Result<View<V>>
     where
         V: 'static + Send;
@@ -109,6 +116,13 @@ pub trait VisualContext: Context {
         view: &View<V>,
         update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, V>) -> R,
     ) -> Self::Result<R>;
+
+    fn replace_root_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut Self::ViewContext<'_, V>) -> V,
+    ) -> Self::Result<View<V>>
+    where
+        V: 'static + Send + Render;
 }
 
 pub trait Entity<T>: Sealed {
@@ -145,6 +159,7 @@ impl<T> DerefMut for MainThread<T> {
 }
 
 impl<C: Context> Context for MainThread<C> {
+    type WindowContext<'a> = MainThread<C::WindowContext<'a>>;
     type ModelContext<'a, T> = MainThread<C::ModelContext<'a, T>>;
     type Result<T> = C::Result<T>;
 
@@ -181,6 +196,20 @@ impl<C: Context> Context for MainThread<C> {
             update(entity, cx)
         })
     }
+
+    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> T,
+    {
+        self.0.update_window(window, |cx| {
+            let cx = unsafe {
+                mem::transmute::<&mut C::WindowContext<'_>, &mut MainThread<C::WindowContext<'_>>>(
+                    cx,
+                )
+            };
+            update(cx)
+        })
+    }
 }
 
 impl<C: VisualContext> VisualContext for MainThread<C> {
@@ -219,6 +248,24 @@ impl<C: VisualContext> VisualContext for MainThread<C> {
             update(view_state, cx)
         })
     }
+
+    fn replace_root_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut Self::ViewContext<'_, V>) -> V,
+    ) -> Self::Result<View<V>>
+    where
+        V: 'static + Send + Render,
+    {
+        self.0.replace_root_view(|cx| {
+            let cx = unsafe {
+                mem::transmute::<
+                    &mut C::ViewContext<'_, V>,
+                    &mut MainThread<C::ViewContext<'_, V>>,
+                >(cx)
+            };
+            build_view(cx)
+        })
+    }
 }
 
 pub trait BorrowAppContext {

crates/gpui2/src/window.rs 🔗

@@ -5,7 +5,7 @@ use crate::{
     Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
     MainThread, MainThreadOnly, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
     MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow,
-    Point, PolychromeSprite, Quad, RenderGlyphParams, RenderImageParams, RenderSvgParams,
+    Point, PolychromeSprite, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams,
     ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine,
     Task, Underline, UnderlineStyle, View, VisualContext, WeakView, WindowOptions,
     SUBPIXEL_VARIANTS,
@@ -315,6 +315,8 @@ impl<'a> WindowContext<'a> {
         Self { app, window }
     }
 
+    // fn replace_root(&mut )
+
     /// Obtain a handle to the window that belongs to this context.
     pub fn window_handle(&self) -> AnyWindowHandle {
         self.window.handle
@@ -1264,6 +1266,7 @@ impl<'a> WindowContext<'a> {
 }
 
 impl Context for WindowContext<'_> {
+    type WindowContext<'a> = WindowContext<'a>;
     type ModelContext<'a, T> = ModelContext<'a, T>;
     type Result<T> = T;
 
@@ -1292,6 +1295,17 @@ impl Context for WindowContext<'_> {
         self.entities.end_lease(entity);
         result
     }
+
+    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> T,
+    {
+        if window == self.window.handle {
+            Ok(update(self))
+        } else {
+            self.app.update_window(window, update)
+        }
+    }
 }
 
 impl VisualContext for WindowContext<'_> {
@@ -1326,6 +1340,24 @@ impl VisualContext for WindowContext<'_> {
         cx.app.entities.end_lease(lease);
         result
     }
+
+    fn replace_root_view<V>(
+        &mut self,
+        build_view: impl FnOnce(&mut Self::ViewContext<'_, V>) -> V,
+    ) -> Self::Result<View<V>>
+    where
+        V: 'static + Send + Render,
+    {
+        let slot = self.app.entities.reserve();
+        let view = View {
+            model: slot.clone(),
+        };
+        let mut cx = ViewContext::new(&mut *self.app, &mut *self.window, &view);
+        let entity = build_view(&mut cx);
+        self.entities.insert(slot, entity);
+        self.window.root_view = Some(view.clone().into());
+        view
+    }
 }
 
 impl<'a> std::ops::Deref for WindowContext<'a> {
@@ -1900,6 +1932,7 @@ impl<V: 'static> MainThread<ViewContext<'_, V>> {
 }
 
 impl<V> Context for ViewContext<'_, V> {
+    type WindowContext<'a> = WindowContext<'a>;
     type ModelContext<'b, U> = ModelContext<'b, U>;
     type Result<U> = U;
 
@@ -1920,6 +1953,13 @@ impl<V> Context for ViewContext<'_, V> {
     ) -> R {
         self.window_cx.update_model(model, update)
     }
+
+    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
+    where
+        F: FnOnce(&mut Self::WindowContext<'_>) -> T,
+    {
+        self.window_cx.update_window(window, update)
+    }
 }
 
 impl<V: 'static> VisualContext for ViewContext<'_, V> {
@@ -1939,6 +1979,16 @@ impl<V: 'static> VisualContext for ViewContext<'_, V> {
     ) -> Self::Result<R> {
         self.window_cx.update_view(view, update)
     }
+
+    fn replace_root_view<W>(
+        &mut self,
+        build_view: impl FnOnce(&mut Self::ViewContext<'_, W>) -> W,
+    ) -> Self::Result<View<W>>
+    where
+        W: 'static + Send + Render,
+    {
+        self.window_cx.replace_root_view(build_view)
+    }
 }
 
 impl<'a, V> std::ops::Deref for ViewContext<'a, V> {
@@ -1972,18 +2022,7 @@ pub struct WindowHandle<V> {
     state_type: PhantomData<V>,
 }
 
-impl<V> Copy for WindowHandle<V> {}
-
-impl<V> Clone for WindowHandle<V> {
-    fn clone(&self) -> Self {
-        WindowHandle {
-            any_handle: self.any_handle,
-            state_type: PhantomData,
-        }
-    }
-}
-
-impl<V: 'static> WindowHandle<V> {
+impl<V: 'static + Render> WindowHandle<V> {
     pub fn new(id: WindowId) -> Self {
         WindowHandle {
             any_handle: AnyWindowHandle {
@@ -1994,7 +2033,7 @@ impl<V: 'static> WindowHandle<V> {
         }
     }
 
-    pub fn update<R>(
+    pub fn update_root<R>(
         &self,
         cx: &mut AppContext,
         update: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
@@ -2012,6 +2051,17 @@ impl<V: 'static> WindowHandle<V> {
     }
 }
 
+impl<V> Copy for WindowHandle<V> {}
+
+impl<V> Clone for WindowHandle<V> {
+    fn clone(&self) -> Self {
+        WindowHandle {
+            any_handle: self.any_handle,
+            state_type: PhantomData,
+        }
+    }
+}
+
 impl<V: 'static> Into<AnyWindowHandle> for WindowHandle<V> {
     fn into(self) -> AnyWindowHandle {
         self.any_handle

crates/workspace2/src/workspace2.rs 🔗

@@ -23,8 +23,8 @@ use futures::{
     FutureExt,
 };
 use gpui2::{
-    AnyModel, AnyView, AppContext, AsyncAppContext, AsyncWindowContext, DisplayId, Entity,
-    EventEmitter, MainThread, Model, ModelContext, Subscription, Task, View, ViewContext,
+    div, AnyModel, AnyView, AppContext, AsyncAppContext, AsyncWindowContext, Div, Entity,
+    EventEmitter, MainThread, Model, ModelContext, Render, Subscription, Task, View, ViewContext,
     VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
@@ -32,7 +32,10 @@ use language2::LanguageRegistry;
 use node_runtime::NodeRuntime;
 pub use pane::*;
 pub use pane_group::*;
-use persistence::model::{ItemId, WorkspaceLocation};
+use persistence::{
+    model::{ItemId, WorkspaceLocation},
+    DB,
+};
 use project2::{Project, ProjectEntryId, ProjectPath, Worktree};
 use std::{
     any::TypeId,
@@ -773,139 +776,137 @@ impl Workspace {
     //         }
     //     }
 
-    // fn new_local(
-    //     abs_paths: Vec<PathBuf>,
-    //     app_state: Arc<AppState>,
-    //     requesting_window: Option<WindowHandle<Workspace>>,
-    //     cx: &mut AppContext,
-    // ) -> Task<(
-    //     WeakView<Workspace>,
-    //     Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
-    // )> {
-    //     let project_handle = Project::local(
-    //         app_state.client.clone(),
-    //         app_state.node_runtime.clone(),
-    //         app_state.user_store.clone(),
-    //         app_state.languages.clone(),
-    //         app_state.fs.clone(),
-    //         cx,
-    //     );
-
-    //     cx.spawn(|mut cx| async move {
-    //         let serialized_workspace = persistence::DB.workspace_for_roots(&abs_paths.as_slice());
-
-    //         let paths_to_open = Arc::new(abs_paths);
-
-    //         // Get project paths for all of the abs_paths
-    //         let mut worktree_roots: HashSet<Arc<Path>> = Default::default();
-    //         let mut project_paths: Vec<(PathBuf, Option<ProjectPath>)> =
-    //             Vec::with_capacity(paths_to_open.len());
-    //         for path in paths_to_open.iter().cloned() {
-    //             if let Some((worktree, project_entry)) = 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((path, Some(project_entry)));
-    //             } else {
-    //                 project_paths.push((path, None));
-    //             }
-    //         }
-
-    //             let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() {
-    //                 serialized_workspace.id
-    //             } else {
-    //                 DB.next_id().await.unwrap_or(0)
-    //             };
-
-    //             let window = if let Some(window) = requesting_window {
-    //                 window.replace_root(&mut cx, |cx| {
-    //                     Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
-    //                 });
-    //                 window
-    //             } else {
-    //                 {
-    //                     let window_bounds_override = window_bounds_env_override(&cx);
-    //                     let (bounds, display) = if let Some(bounds) = window_bounds_override {
-    //                         (Some(bounds), None)
-    //                     } else {
-    //                         serialized_workspace
-    //                             .as_ref()
-    //                             .and_then(|serialized_workspace| {
-    //                                 let display = serialized_workspace.display?;
-    //                                 let mut bounds = serialized_workspace.bounds?;
-
-    //                                 // Stored bounds are relative to the containing display.
-    //                                 // So convert back to global coordinates if that screen still exists
-    //                                 if let WindowBounds::Fixed(mut window_bounds) = bounds {
-    //                                     if let Some(screen) = cx.platform().screen_by_id(display) {
-    //                                         let screen_bounds = screen.bounds();
-    //                                         window_bounds.set_origin_x(
-    //                                             window_bounds.origin_x() + screen_bounds.origin_x(),
-    //                                         );
-    //                                         window_bounds.set_origin_y(
-    //                                             window_bounds.origin_y() + screen_bounds.origin_y(),
-    //                                         );
-    //                                         bounds = WindowBounds::Fixed(window_bounds);
-    //                                     } else {
-    //                                         // Screen no longer exists. Return none here.
-    //                                         return None;
-    //                                     }
-    //                                 }
-
-    //                                 Some((bounds, display))
-    //                             })
-    //                             .unzip()
-    //                     };
-
-    //                     // Use the serialized workspace to construct the new window
-    //                     cx.add_window(
-    //                         (app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
-    //                         |cx| {
-    //                             Workspace::new(
-    //                                 workspace_id,
-    //                                 project_handle.clone(),
-    //                                 app_state.clone(),
-    //                                 cx,
-    //                             )
-    //                         },
-    //                     )
-    //                 }
-    //             };
+    fn new_local(
+        abs_paths: Vec<PathBuf>,
+        app_state: Arc<AppState>,
+        requesting_window: Option<WindowHandle<Workspace>>,
+        cx: &mut MainThread<AppContext>,
+    ) -> Task<(
+        WeakView<Workspace>,
+        Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
+    )> {
+        let project_handle = Project::local(
+            app_state.client.clone(),
+            app_state.node_runtime.clone(),
+            app_state.user_store.clone(),
+            app_state.languages.clone(),
+            app_state.fs.clone(),
+            cx,
+        );
+
+        cx.spawn_on_main(|mut cx| async move {
+            let serialized_workspace = persistence::DB.workspace_for_roots(&abs_paths.as_slice());
+
+            let paths_to_open = Arc::new(abs_paths);
+
+            // Get project paths for all of the abs_paths
+            let mut worktree_roots: HashSet<Arc<Path>> = Default::default();
+            let mut project_paths: Vec<(PathBuf, Option<ProjectPath>)> =
+                Vec::with_capacity(paths_to_open.len());
+            for path in paths_to_open.iter().cloned() {
+                if let Some((worktree, project_entry)) = cx
+                    .update(|cx| {
+                        Workspace::project_path_for_path(project_handle.clone(), &path, true, cx)
+                    })?
+                    .await
+                    .log_err()
+                {
+                    worktree_roots.extend(worktree.update(&mut cx, |tree, _| tree.abs_path()).ok());
+                    project_paths.push((path, Some(project_entry)));
+                } else {
+                    project_paths.push((path, None));
+                }
+            }
 
-    //             // We haven't yielded the main thread since obtaining the window handle,
-    //             // so the window exists.
-    //             let workspace = window.root(&cx).unwrap();
+            let workspace_id = if let Some(serialized_workspace) = serialized_workspace.as_ref() {
+                serialized_workspace.id
+            } else {
+                DB.next_id().await.unwrap_or(0)
+            };
 
-    //             (app_state.initialize_workspace)(
-    //                 workspace.downgrade(),
-    //                 serialized_workspace.is_some(),
-    //                 app_state.clone(),
-    //                 cx.clone(),
-    //             )
-    //             .await
-    //             .log_err();
+            let window = if let Some(window) = requesting_window {
+                cx.update_window(window.into(), |cx| {
+                    cx.replace_root_view(|cx| {
+                        Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
+                    });
+                });
+                window
+            } else {
+                {
+                    let window_bounds_override = window_bounds_env_override(&cx);
+                    let (bounds, display) = if let Some(bounds) = window_bounds_override {
+                        (Some(bounds), None)
+                    } else {
+                        serialized_workspace
+                            .as_ref()
+                            .and_then(|serialized_workspace| {
+                                let display = serialized_workspace.display?;
+                                let mut bounds = serialized_workspace.bounds?;
+
+                                // Stored bounds are relative to the containing display.
+                                // So convert back to global coordinates if that screen still exists
+                                if let WindowBounds::Fixed(mut window_bounds) = bounds {
+                                    if let Some(screen) = cx.platform().screen_by_id(display) {
+                                        let screen_bounds = screen.bounds();
+                                        window_bounds.origin.x += screen_bounds.origin.x;
+                                        window_bounds.origin.y += screen_bounds.origin.y;
+                                        bounds = WindowBounds::Fixed(window_bounds);
+                                    } else {
+                                        // Screen no longer exists. Return none here.
+                                        return None;
+                                    }
+                                }
+
+                                Some((bounds, display))
+                            })
+                            .unzip()
+                    };
+
+                    // Use the serialized workspace to construct the new window
+                    cx.open_window(
+                        (app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
+                        |cx| {
+                            Workspace::new(
+                                workspace_id,
+                                project_handle.clone(),
+                                app_state.clone(),
+                                cx,
+                            )
+                        },
+                    )
+                }
+            };
 
-    //             window.update(&mut cx, |cx| cx.activate_window());
+            // We haven't yielded the main thread since obtaining the window handle,
+            // so the window exists.
+            let workspace = window.root(&cx).unwrap();
 
-    //             let workspace = workspace.downgrade();
-    //             notify_if_database_failed(&workspace, &mut cx);
-    //             let opened_items = open_items(
-    //                 serialized_workspace,
-    //                 &workspace,
-    //                 project_paths,
-    //                 app_state,
-    //                 cx,
-    //             )
-    //             .await
-    //             .unwrap_or_default();
+            (app_state.initialize_workspace)(
+                workspace.downgrade(),
+                serialized_workspace.is_some(),
+                app_state.clone(),
+                cx.clone(),
+            )
+            .await
+            .log_err();
+
+            window.update_root(&mut cx, |cx| cx.activate_window());
+
+            let workspace = workspace.downgrade();
+            notify_if_database_failed(&workspace, &mut cx);
+            let opened_items = open_items(
+                serialized_workspace,
+                &workspace,
+                project_paths,
+                app_state,
+                cx,
+            )
+            .await
+            .unwrap_or_default();
 
-    //             (workspace, opened_items)
-    //         })
-    //     }
+            (workspace, opened_items)
+        })
+    }
 
     pub fn weak_handle(&self) -> WeakView<Self> {
         self.weak_self.clone()
@@ -3744,6 +3745,14 @@ impl EventEmitter for Workspace {
     type Event = Event;
 }
 
+impl Render for Workspace {
+    type Element = Div<Self>;
+
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+        div()
+    }
+}
+
 // todo!()
 // impl Entity for Workspace {
 //     type Event = Event;
@@ -3960,7 +3969,7 @@ impl WorkspaceStore {
             let mut response = proto::FollowResponse::default();
             for workspace in &this.workspaces {
                 workspace
-                    .update(cx, |workspace, cx| {
+                    .update_root(cx, |workspace, cx| {
                         let handler_response = workspace.handle_follow(follower.project_id, cx);
                         if response.views.is_empty() {
                             response.views = handler_response.views;
@@ -4118,9 +4127,9 @@ pub async fn activate_workspace_for_project(
     .await
 }
 
-// pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
-//     DB.last_workspace().await.log_err().flatten()
-// }
+pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
+    DB.last_workspace().await.log_err().flatten()
+}
 
 // async fn join_channel_internal(
 //     channel_id: u64,
@@ -4345,24 +4354,24 @@ pub fn open_paths(
     })
 }
 
-// pub fn open_new(
-//     app_state: &Arc<AppState>,
-//     cx: &mut AppContext,
-//     init: impl FnOnce(&mut Workspace, &mut ViewContext<Workspace>) + 'static,
-// ) -> Task<()> {
-//     let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx);
-//     cx.spawn(|mut cx| async move {
-//         let (workspace, opened_paths) = task.await;
-
-//         workspace
-//             .update(&mut cx, |workspace, cx| {
-//                 if opened_paths.is_empty() {
-//                     init(workspace, cx)
-//                 }
-//             })
-//             .log_err();
-//     })
-// }
+pub fn open_new(
+    app_state: &Arc<AppState>,
+    cx: &mut AppContext,
+    init: impl FnOnce(&mut Workspace, &mut ViewContext<Workspace>) + 'static,
+) -> Task<()> {
+    let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx);
+    cx.spawn(|mut cx| async move {
+        let (workspace, opened_paths) = task.await;
+
+        workspace
+            .update(&mut cx, |workspace, cx| {
+                if opened_paths.is_empty() {
+                    init(workspace, cx)
+                }
+            })
+            .log_err();
+    })
+}
 
 // pub fn create_and_open_local_file(
 //     path: &'static Path,

crates/zed2/src/main.rs 🔗

@@ -314,21 +314,20 @@ async fn installation_id() -> Result<String> {
 }
 
 async fn restore_or_create_workspace(_app_state: &Arc<AppState>, mut _cx: AsyncAppContext) {
-    todo!("workspace")
-    // if let Some(location) = workspace::last_opened_workspace_paths().await {
-    //     cx.update(|cx| workspace::open_paths(location.paths().as_ref(), app_state, None, cx))
-    //         .await
-    //         .log_err();
-    // } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) {
-    //     cx.update(|cx| show_welcome_experience(app_state, cx));
-    // } else {
-    //     cx.update(|cx| {
-    //         workspace::open_new(app_state, cx, |workspace, cx| {
-    //             Editor::new_file(workspace, &Default::default(), cx)
-    //         })
-    //         .detach();
-    //     });
-    // }
+    if let Some(location) = workspace2::last_opened_workspace_paths().await {
+        cx.update(|cx| workspace2::open_paths(location.paths().as_ref(), app_state, None, cx))
+            .await
+            .log_err();
+    } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) {
+        cx.update(|cx| show_welcome_experience(app_state, cx));
+    } else {
+        cx.update(|cx| {
+            workspace2::open_new(app_state, cx, |workspace, cx| {
+                Editor::new_file(workspace, &Default::default(), cx)
+            })
+            .detach();
+        });
+    }
 }
 
 fn init_paths() {