workspace2 is compiling

KCaverly and Kirill created

Co-Authored-By: Kirill <kirill@zed.dev>

Change summary

crates/gpui2/src/platform.rs               |  63 ++++++++
crates/workspace2/src/item.rs              |   8 
crates/workspace2/src/pane_group.rs        |  34 ++++
crates/workspace2/src/persistence/model.rs |   4 
crates/workspace2/src/workspace2.rs        | 175 +++++++++++------------
5 files changed, 187 insertions(+), 97 deletions(-)

Detailed changes

crates/gpui2/src/platform.rs 🔗

@@ -9,11 +9,13 @@ use crate::{
     GlobalPixels, GlyphId, InputEvent, LineLayout, Pixels, Point, RenderGlyphParams,
     RenderImageParams, RenderSvgParams, Result, Scene, SharedString, Size,
 };
-use anyhow::anyhow;
+use anyhow::{anyhow, bail};
 use async_task::Runnable;
 use futures::channel::oneshot;
 use seahash::SeaHasher;
 use serde::{Deserialize, Serialize};
+use sqlez::bindable::{Bind, Column, StaticColumnCount};
+use sqlez::statement::Statement;
 use std::borrow::Cow;
 use std::hash::{Hash, Hasher};
 use std::time::Duration;
@@ -368,6 +370,65 @@ pub enum WindowBounds {
     Fixed(Bounds<GlobalPixels>),
 }
 
+impl StaticColumnCount for WindowBounds {
+    fn column_count() -> usize {
+        5
+    }
+}
+
+impl Bind for WindowBounds {
+    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+        let (region, next_index) = match self {
+            WindowBounds::Fullscreen => {
+                let next_index = statement.bind(&"Fullscreen", start_index)?;
+                (None, next_index)
+            }
+            WindowBounds::Maximized => {
+                let next_index = statement.bind(&"Maximized", start_index)?;
+                (None, next_index)
+            }
+            WindowBounds::Fixed(region) => {
+                let next_index = statement.bind(&"Fixed", start_index)?;
+                (Some(*region), next_index)
+            }
+        };
+
+        todo!()
+        // statement.bind(
+        //     &region.map(|region| {
+        //         (
+        //             region.min_x(),
+        //             region.min_y(),
+        //             region.width(),
+        //             region.height(),
+        //         )
+        //     }),
+        //     next_index,
+        // )
+    }
+}
+
+impl Column for WindowBounds {
+    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
+        let (window_state, next_index) = String::column(statement, start_index)?;
+        let bounds = match window_state.as_str() {
+            "Fullscreen" => WindowBounds::Fullscreen,
+            "Maximized" => WindowBounds::Maximized,
+            "Fixed" => {
+                // let ((x, y, width, height), _) = Column::column(statement, next_index)?;
+                // WindowBounds::Fixed(RectF::new(
+                //     Vector2F::new(x, y),
+                //     Vector2F::new(width, height),
+                // ))
+                todo!()
+            }
+            _ => bail!("Window State did not have a valid string"),
+        };
+
+        Ok((bounds, next_index + 4))
+    }
+}
+
 #[derive(Copy, Clone, Debug)]
 pub enum WindowAppearance {
     Light,

crates/workspace2/src/item.rs 🔗

@@ -253,9 +253,9 @@ pub trait ItemHandle: 'static + Send {
     fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option<AnyView>;
     fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
     fn on_release(
-        &self,
+        &mut self,
         cx: &mut AppContext,
-        callback: Box<dyn FnOnce(&mut AppContext) + Send>,
+        callback: Box<dyn FnMut(&mut AppContext) + Send>,
     ) -> gpui2::Subscription;
     fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
     fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation;
@@ -571,9 +571,9 @@ impl<T: Item> ItemHandle for View<T> {
     }
 
     fn on_release(
-        &self,
+        &mut self,
         cx: &mut AppContext,
-        callback: Box<dyn FnOnce(&mut AppContext) + Send>,
+        mut callback: Box<dyn FnMut(&mut AppContext) + Send>,
     ) -> gpui2::Subscription {
         cx.observe_release(self, move |_, cx| callback(cx))
     }

crates/workspace2/src/pane_group.rs 🔗

@@ -1,7 +1,11 @@
 use crate::{AppState, FollowerState, Pane, Workspace};
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, bail, Result};
 use call2::ActiveCall;
 use collections::HashMap;
+use db2::sqlez::{
+    bindable::{Bind, Column, StaticColumnCount},
+    statement::Statement,
+};
 use gpui2::{point, size, AnyElement, AnyView, Bounds, Model, Pixels, Point, View, ViewContext};
 use parking_lot::Mutex;
 use project2::Project;
@@ -13,12 +17,38 @@ const HANDLE_HITBOX_SIZE: f32 = 4.0;
 const HORIZONTAL_MIN_SIZE: f32 = 80.;
 const VERTICAL_MIN_SIZE: f32 = 100.;
 
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum Axis {
     Vertical,
     Horizontal,
 }
 
+impl StaticColumnCount for Axis {}
+impl Bind for Axis {
+    fn bind(&self, statement: &Statement, start_index: i32) -> anyhow::Result<i32> {
+        match self {
+            Axis::Horizontal => "Horizontal",
+            Axis::Vertical => "Vertical",
+        }
+        .bind(statement, start_index)
+    }
+}
+
+impl Column for Axis {
+    fn column(statement: &mut Statement, start_index: i32) -> anyhow::Result<(Self, i32)> {
+        String::column(statement, start_index).and_then(|(axis_text, next_index)| {
+            Ok((
+                match axis_text.as_str() {
+                    "Horizontal" => Axis::Horizontal,
+                    "Vertical" => Axis::Vertical,
+                    _ => bail!("Stored serialized item kind is incorrect"),
+                },
+                next_index,
+            ))
+        })
+    }
+}
+
 #[derive(Clone, PartialEq)]
 pub struct PaneGroup {
     pub(crate) root: Member,

crates/workspace2/src/persistence/model.rs 🔗

@@ -7,7 +7,7 @@ use db2::sqlez::{
     bindable::{Bind, Column, StaticColumnCount},
     statement::Statement,
 };
-use gpui2::{AsyncAppContext, AsyncWindowContext, Model, Task, View, WeakView, WindowBounds};
+use gpui2::{AsyncWindowContext, Model, Task, View, WeakView, WindowBounds};
 use project2::Project;
 use std::{
     path::{Path, PathBuf},
@@ -232,7 +232,7 @@ impl SerializedPane {
         pane: &WeakView<Pane>,
         workspace_id: WorkspaceId,
         workspace: &WeakView<Workspace>,
-        cx: &mut AsyncAppContext,
+        cx: &mut AsyncWindowContext,
     ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
         let mut items = Vec::new();
         let mut active_item_index = None;

crates/workspace2/src/workspace2.rs 🔗

@@ -491,7 +491,7 @@ impl DelayedDebouncedEditAction {
         self.cancel_channel = Some(sender);
 
         let previous_task = self.task.take();
-        self.task = Some(cx.spawn(|workspace, mut cx| async move {
+        self.task = Some(cx.spawn(move |workspace, mut cx| async move {
             let mut timer = cx.executor().timer(delay).fuse();
             if let Some(previous_task) = previous_task {
                 previous_task.await;
@@ -765,47 +765,47 @@ 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));
-    //                 }
+    // 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
@@ -1470,13 +1470,13 @@ impl Workspace {
         visible: bool,
         cx: &mut ViewContext<Self>,
     ) -> Task<Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>> {
-        log::info!("open paths {:?}", abs_paths);
+        log::info!("open paths {abs_paths:?}");
 
         let fs = self.app_state.fs.clone();
 
         // Sort the paths to ensure we add worktrees for parents before their children.
         abs_paths.sort_unstable();
-        cx.spawn(|this, mut cx| async move {
+        cx.spawn(move |this, mut cx| async move {
             let mut tasks = Vec::with_capacity(abs_paths.len());
             for abs_path in &abs_paths {
                 let project_path = match this
@@ -1495,45 +1495,41 @@ impl Workspace {
                 };
 
                 let this = this.clone();
-                let task = cx.spawn(|mut cx| {
-                    let fs = fs.clone();
-                    let abs_path = abs_path.clone();
-                    async move {
-                        let (worktree, project_path) = project_path?;
-                        if fs.is_file(&abs_path).await {
-                            Some(
-                                this.update(&mut cx, |this, cx| {
-                                    this.open_path(project_path, None, true, cx)
-                                })
-                                .log_err()?
-                                .await,
-                            )
-                        } else {
-                            this.update(&mut cx, |workspace, cx| {
-                                let worktree = worktree.read(cx);
-                                let worktree_abs_path = worktree.abs_path();
-                                let entry_id = if abs_path == worktree_abs_path.as_ref() {
-                                    worktree.root_entry()
-                                } else {
-                                    abs_path
-                                        .strip_prefix(worktree_abs_path.as_ref())
-                                        .ok()
-                                        .and_then(|relative_path| {
-                                            worktree.entry_for_path(relative_path)
-                                        })
-                                }
-                                .map(|entry| entry.id);
-                                if let Some(entry_id) = entry_id {
-                                    workspace.project.update(cx, |_, cx| {
-                                        cx.emit(project2::Event::ActiveEntryChanged(Some(
-                                            entry_id,
-                                        )));
-                                    })
-                                }
+                let abs_path = abs_path.clone();
+                let fs = fs.clone();
+                let task = cx.spawn(move |mut cx| async move {
+                    let (worktree, project_path) = project_path?;
+                    if fs.is_file(&abs_path).await {
+                        Some(
+                            this.update(&mut cx, |this, cx| {
+                                this.open_path(project_path, None, true, cx)
                             })
-                            .log_err()?;
-                            None
-                        }
+                            .log_err()?
+                            .await,
+                        )
+                    } else {
+                        this.update(&mut cx, |workspace, cx| {
+                            let worktree = worktree.read(cx);
+                            let worktree_abs_path = worktree.abs_path();
+                            let entry_id = if abs_path == worktree_abs_path.as_ref() {
+                                worktree.root_entry()
+                            } else {
+                                abs_path
+                                    .strip_prefix(worktree_abs_path.as_ref())
+                                    .ok()
+                                    .and_then(|relative_path| {
+                                        worktree.entry_for_path(relative_path)
+                                    })
+                            }
+                            .map(|entry| entry.id);
+                            if let Some(entry_id) = entry_id {
+                                workspace.project.update(cx, |_, cx| {
+                                    cx.emit(project2::Event::ActiveEntryChanged(Some(entry_id)));
+                                })
+                            }
+                        })
+                        .log_err()?;
+                        None
                     }
                 });
                 tasks.push(task);
@@ -1572,7 +1568,7 @@ impl Workspace {
         let entry = project.update(cx, |project, cx| {
             project.find_or_create_local_worktree(abs_path, visible, cx)
         });
-        cx.spawn(|cx| async move {
+        cx.spawn(|mut cx| async move {
             let (worktree, path) = entry.await?;
             let worktree_id = worktree.update(&mut cx, |t, _| t.id())?;
             Ok((
@@ -2043,7 +2039,7 @@ impl Workspace {
         });
 
         let task = self.load_path(path.into(), cx);
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn(move |_, mut cx| async move {
             let (project_entry_id, build_item) = task.await?;
             pane.update(&mut cx, |pane, cx| {
                 pane.open_item(project_entry_id, focus_item, cx, build_item)
@@ -3220,7 +3216,7 @@ impl Workspace {
     //         }));
     //     }
 
-    fn serialize_workspace(&self, cx: &ViewContext<Self>) {
+    fn serialize_workspace(&self, cx: &mut ViewContext<Self>) {
         fn serialize_pane_handle(pane_handle: &View<Pane>, cx: &AppContext) -> SerializedPane {
             let (items, active) = {
                 let pane = pane_handle.read(cx);
@@ -3266,7 +3262,10 @@ impl Workspace {
             }
         }
 
-        fn build_serialized_docks(this: &Workspace, cx: &ViewContext<Workspace>) -> DockStructure {
+        fn build_serialized_docks(
+            this: &Workspace,
+            cx: &mut ViewContext<Workspace>,
+        ) -> DockStructure {
             let left_dock = this.left_dock.read(cx);
             let left_visible = left_dock.is_open();
             let left_active_panel = left_dock.visible_panel().and_then(|panel| {
@@ -4313,9 +4312,9 @@ pub fn open_paths(
 > {
     let app_state = app_state.clone();
     let abs_paths = abs_paths.to_vec();
-    cx.spawn(|mut cx| async move {
+    cx.spawn(move |mut cx| async move {
         // Open paths in existing workspace if possible
-        let existing = activate_workspace_for_project(&mut cx, |project, cx| {
+        let existing = activate_workspace_for_project(&mut cx, move |project, cx| {
             project.contains_paths(&abs_paths, cx)
         })
         .await;
@@ -4330,12 +4329,12 @@ pub fn open_paths(
             // ))
             todo!()
         } else {
-            todo!()
             // Ok(cx
             //     .update(|cx| {
             //         Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
             //     })
             //     .await)
+            todo!()
         }
     })
 }