WIP

Antonio Scandurra created

Change summary

crates/workspace2/src/item.rs              |   4 
crates/workspace2/src/persistence/model.rs | 241 +++++++++---------
crates/workspace2/src/workspace2.rs        | 308 ++++++++++++------------
3 files changed, 277 insertions(+), 276 deletions(-)

Detailed changes

crates/workspace2/src/item.rs 🔗

@@ -13,7 +13,7 @@ use client2::{
 };
 use gpui2::{
     AnyElement, AnyView, AppContext, Entity, EntityId, EventEmitter, HighlightStyle, Model, Pixels,
-    Point, Render, SharedString, Task, View, ViewContext, WeakView, WindowContext, WindowHandle,
+    Point, Render, SharedString, Task, View, ViewContext, WeakView, WindowContext,
 };
 use parking_lot::Mutex;
 use project2::{Project, ProjectEntryId, ProjectPath};
@@ -190,7 +190,7 @@ pub trait Item: Render + EventEmitter + Send {
 
     fn deserialize(
         _project: Model<Project>,
-        _workspace: WindowHandle<Workspace>,
+        _workspace: WeakView<Workspace>,
         _workspace_id: WorkspaceId,
         _item_id: ItemId,
         _cx: &mut ViewContext<Pane>,

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

@@ -1,14 +1,19 @@
-use crate::{Axis, WorkspaceId};
+use crate::{
+    item::ItemHandle, Axis, ItemDeserializers, Member, Pane, PaneAxis, Workspace, WorkspaceId,
+};
 use anyhow::{Context, Result};
+use async_recursion::async_recursion;
 use db2::sqlez::{
     bindable::{Bind, Column, StaticColumnCount},
     statement::Statement,
 };
-use gpui2::WindowBounds;
+use gpui2::{AsyncWindowContext, Model, Task, View, WeakView, WindowBounds};
+use project2::Project;
 use std::{
     path::{Path, PathBuf},
     sync::Arc,
 };
+use util::ResultExt;
 use uuid::Uuid;
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -142,75 +147,73 @@ impl Default for SerializedPaneGroup {
     }
 }
 
-// impl SerializedPaneGroup {
-//     #[async_recursion(?Send)]
-//     pub(crate) async fn deserialize(
-//         self,
-//         project: &Model<Project>,
-//         workspace_id: WorkspaceId,
-//         workspace: WeakView<Workspace>,
-//         cx: &mut AsyncAppContext,
-//     ) -> Option<(Member, Option<View<Pane>>, Vec<Option<Box<dyn ItemHandle>>>)> {
-//         match self {
-//             SerializedPaneGroup::Group {
-//                 axis,
-//                 children,
-//                 flexes,
-//             } => {
-//                 let mut current_active_pane = None;
-//                 let mut members = Vec::new();
-//                 let mut items = Vec::new();
-//                 for child in children {
-//                     if let Some((new_member, active_pane, new_items)) = child
-//                         .deserialize(project, workspace_id, workspace, cx)
-//                         .await
-//                     {
-//                         members.push(new_member);
-//                         items.extend(new_items);
-//                         current_active_pane = current_active_pane.or(active_pane);
-//                     }
-//                 }
-
-//                 if members.is_empty() {
-//                     return None;
-//                 }
-
-//                 if members.len() == 1 {
-//                     return Some((members.remove(0), current_active_pane, items));
-//                 }
-
-//                 Some((
-//                     Member::Axis(PaneAxis::load(axis, members, flexes)),
-//                     current_active_pane,
-//                     items,
-//                 ))
-//             }
-//             SerializedPaneGroup::Pane(serialized_pane) => {
-//                 let pane = workspace
-//                     .update(cx, |workspace, cx| workspace.add_pane(cx).downgrade())
-//                     .log_err()?;
-//                 let active = serialized_pane.active;
-//                 let new_items = serialized_pane
-//                     .deserialize_to(project, &pane, workspace_id, workspace, cx)
-//                     .await
-//                     .log_err()?;
-
-//                 // todo!();
-//                 // if pane.update(cx, |pane, _| pane.items_len() != 0).log_err()? {
-//                 //     let pane = pane.upgrade()?;
-//                 //     Some((Member::Pane(pane.clone()), active.then(|| pane), new_items))
-//                 // } else {
-//                 //     let pane = pane.upgrade()?;
-//                 //     workspace
-//                 //         .update(cx, |workspace, cx| workspace.force_remove_pane(&pane, cx))
-//                 //         .log_err()?;
-//                 //     None
-//                 // }
-//                 None
-//             }
-//         }
-//     }
-// }
+impl SerializedPaneGroup {
+    #[async_recursion(?Send)]
+    pub(crate) async fn deserialize(
+        self,
+        project: &Model<Project>,
+        workspace_id: WorkspaceId,
+        workspace: WeakView<Workspace>,
+        cx: &mut AsyncWindowContext,
+    ) -> Option<(Member, Option<View<Pane>>, Vec<Option<Box<dyn ItemHandle>>>)> {
+        match self {
+            SerializedPaneGroup::Group {
+                axis,
+                children,
+                flexes,
+            } => {
+                let mut current_active_pane = None;
+                let mut members = Vec::new();
+                let mut items = Vec::new();
+                for child in children {
+                    if let Some((new_member, active_pane, new_items)) = child
+                        .deserialize(project, workspace_id, workspace, cx)
+                        .await
+                    {
+                        members.push(new_member);
+                        items.extend(new_items);
+                        current_active_pane = current_active_pane.or(active_pane);
+                    }
+                }
+
+                if members.is_empty() {
+                    return None;
+                }
+
+                if members.len() == 1 {
+                    return Some((members.remove(0), current_active_pane, items));
+                }
+
+                Some((
+                    Member::Axis(PaneAxis::load(axis, members, flexes)),
+                    current_active_pane,
+                    items,
+                ))
+            }
+            SerializedPaneGroup::Pane(serialized_pane) => {
+                let pane = workspace
+                    .update(cx, |workspace, cx| workspace.add_pane(cx).downgrade())
+                    .log_err()?;
+                let active = serialized_pane.active;
+                let new_items = serialized_pane
+                    .deserialize_to(project, &pane, workspace_id, workspace, cx)
+                    .await
+                    .log_err()?;
+
+                if pane.update(cx, |pane, _| pane.items_len() != 0).log_err()? {
+                    let pane = pane.upgrade()?;
+                    Some((Member::Pane(pane.clone()), active.then(|| pane), new_items))
+                } else {
+                    let pane = pane.upgrade()?;
+                    workspace
+                        .update(cx, |workspace, cx| workspace.force_remove_pane(&pane, cx))
+                        .log_err()?;
+                    None
+                }
+            }
+        }
+    }
+}
 
 #[derive(Debug, PartialEq, Eq, Default, Clone)]
 pub struct SerializedPane {
@@ -223,55 +226,53 @@ impl SerializedPane {
         SerializedPane { children, active }
     }
 
-    //     pub async fn deserialize_to(
-    //         &self,
-    //         _project: &Model<Project>,
-    //         _pane: &WeakView<Pane>,
-    //         _workspace_id: WorkspaceId,
-    //         _workspace: WindowHandle<Workspace>,
-    //         _cx: &mut AsyncAppContext,
-    //     ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
-    //         anyhow::bail!("todo!()")
-    //         // todo!()
-    //         // let mut items = Vec::new();
-    //         // let mut active_item_index = None;
-    //         // for (index, item) in self.children.iter().enumerate() {
-    //         //     let project = project.clone();
-    //         //     let item_handle = pane
-    //         //         .update(cx, |_, cx| {
-    //         //             if let Some(deserializer) = cx.global::<ItemDeserializers>().get(&item.kind) {
-    //         //                 deserializer(project, workspace, workspace_id, item.item_id, cx)
-    //         //             } else {
-    //         //                 Task::ready(Err(anyhow::anyhow!(
-    //         //                     "Deserializer does not exist for item kind: {}",
-    //         //                     item.kind
-    //         //                 )))
-    //         //             }
-    //         //         })?
-    //         //         .await
-    //         //         .log_err();
-
-    //         //     items.push(item_handle.clone());
-
-    //         //     if let Some(item_handle) = item_handle {
-    //         //         pane.update(cx, |pane, cx| {
-    //         //             pane.add_item(item_handle.clone(), true, true, None, cx);
-    //         //         })?;
-    //         //     }
-
-    //         //     if item.active {
-    //         //         active_item_index = Some(index);
-    //         //     }
-    //         // }
-
-    //         // if let Some(active_item_index) = active_item_index {
-    //         //     pane.update(cx, |pane, cx| {
-    //         //         pane.activate_item(active_item_index, false, false, cx);
-    //         //     })?;
-    //         // }
-
-    //         // anyhow::Ok(items)
-    //     }
+    pub async fn deserialize_to(
+        &self,
+        project: &Model<Project>,
+        pane: &WeakView<Pane>,
+        workspace_id: WorkspaceId,
+        workspace: WeakView<Workspace>,
+        cx: &mut AsyncWindowContext,
+    ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
+        let mut items = Vec::new();
+        let mut active_item_index = None;
+        for (index, item) in self.children.iter().enumerate() {
+            let project = project.clone();
+            let item_handle = pane
+                .update(cx, |_, cx| {
+                    if let Some(deserializer) = cx.global::<ItemDeserializers>().get(&item.kind) {
+                        deserializer(project, workspace.clone(), workspace_id, item.item_id, cx)
+                    } else {
+                        Task::ready(Err(anyhow::anyhow!(
+                            "Deserializer does not exist for item kind: {}",
+                            item.kind
+                        )))
+                    }
+                })?
+                .await
+                .log_err();
+
+            items.push(item_handle.clone());
+
+            if let Some(item_handle) = item_handle {
+                pane.update(cx, |pane, cx| {
+                    pane.add_item(item_handle.clone(), true, true, None, cx);
+                })?;
+            }
+
+            if item.active {
+                active_item_index = Some(index);
+            }
+        }
+
+        if let Some(active_item_index) = active_item_index {
+            pane.update(cx, |pane, cx| {
+                pane.activate_item(active_item_index, false, false, cx);
+            })?;
+        }
+
+        anyhow::Ok(items)
+    }
 }
 
 pub type GroupId = i64;

crates/workspace2/src/workspace2.rs 🔗

@@ -401,7 +401,7 @@ type ItemDeserializers = HashMap<
     Arc<str>,
     fn(
         Model<Project>,
-        WindowHandle<Workspace>,
+        WeakView<Workspace>,
         WorkspaceId,
         ItemId,
         &mut ViewContext<Pane>,
@@ -936,17 +936,17 @@ impl Workspace {
         self.weak_self.clone()
     }
 
-    // pub fn left_dock(&self) -> &View<Dock> {
-    //     &self.left_dock
-    // }
+    pub fn left_dock(&self) -> &View<Dock> {
+        &self.left_dock
+    }
 
-    // pub fn bottom_dock(&self) -> &View<Dock> {
-    //     &self.bottom_dock
-    // }
+    pub fn bottom_dock(&self) -> &View<Dock> {
+        &self.bottom_dock
+    }
 
-    // pub fn right_dock(&self) -> &View<Dock> {
-    //     &self.right_dock
-    // }
+    pub fn right_dock(&self) -> &View<Dock> {
+        &self.right_dock
+    }
 
     //     pub fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>)
     //     where
@@ -3394,126 +3394,127 @@ impl Workspace {
         cx: &mut ViewContext<Workspace>,
     ) -> Task<Result<Vec<Option<Box<dyn ItemHandle>>>>> {
         cx.spawn(|workspace, mut cx| async move {
-            // let (project, old_center_pane) = workspace.update(&mut cx, |workspace, _| {
-            //     (
-            //         workspace.project().clone(),
-            //         workspace.last_active_center_pane.clone(),
-            //     )
-            // })?;
-
-            // // let mut center_group: Option = None;
-            // // let mut center_items: Option<Vec<Option<Box<dyn ItemHandle>>>> = None;
-
-            // // todo!()
-            // // // Traverse the splits tree and add to things
-            // if let Some((group, active_pane, items)) = serialized_workspace
-            //     .center_group
-            //     .deserialize(&project, serialized_workspace.id, workspace, &mut cx)
-            //     .await
-            // {
-            //     center_items = Some(items);
-            //     center_group = Some((group, active_pane))
-            // }
-
-            // let mut items_by_project_path = cx.update(|_, cx| {
-            //     center_items
-            //         .unwrap_or_default()
-            //         .into_iter()
-            //         .filter_map(|item| {
-            //             let item = item?;
-            //             let project_path = item.project_path(cx)?;
-            //             Some((project_path, item))
-            //         })
-            //         .collect::<HashMap<_, _>>()
-            // })?;
-
-            // let opened_items = paths_to_open
-            //     .into_iter()
-            //     .map(|path_to_open| {
-            //         path_to_open
-            //             .and_then(|path_to_open| items_by_project_path.remove(&path_to_open))
-            //     })
-            //     .collect::<Vec<_>>();
-
-            // todo!()
-            // // Remove old panes from workspace panes list
-            // workspace.update(&mut cx, |workspace, cx| {
-            //     if let Some((center_group, active_pane)) = center_group {
-            //         workspace.remove_panes(workspace.center.root.clone(), cx);
+            let (project, old_center_pane) = workspace.update(&mut cx, |workspace, _| {
+                (
+                    workspace.project().clone(),
+                    workspace.last_active_center_pane.clone(),
+                )
+            })?;
 
-            //         // Swap workspace center group
-            //         workspace.center = PaneGroup::with_root(center_group);
+            let mut center_group = None;
+            let mut center_items = None;
 
-            //         // Change the focus to the workspace first so that we retrigger focus in on the pane.
-            //         cx.focus_self();
+            // Traverse the splits tree and add to things
+            if let Some((group, active_pane, items)) = serialized_workspace
+                .center_group
+                .deserialize(&project, serialized_workspace.id, workspace, &mut cx)
+                .await
+            {
+                center_items = Some(items);
+                center_group = Some((group, active_pane))
+            }
 
-            //         if let Some(active_pane) = active_pane {
-            //             cx.focus(&active_pane);
-            //         } else {
-            //             cx.focus(workspace.panes.last().unwrap());
-            //         }
-            //     } else {
-            //         let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade());
-            //         if let Some(old_center_handle) = old_center_handle {
-            //             cx.focus(&old_center_handle)
-            //         } else {
-            //             cx.focus_self()
-            //         }
-            //     }
+            let mut items_by_project_path = cx.update(|_, cx| {
+                center_items
+                    .unwrap_or_default()
+                    .into_iter()
+                    .filter_map(|item| {
+                        let item = item?;
+                        let project_path = item.project_path(cx)?;
+                        Some((project_path, item))
+                    })
+                    .collect::<HashMap<_, _>>()
+            })?;
+
+            let opened_items = paths_to_open
+                .into_iter()
+                .map(|path_to_open| {
+                    path_to_open
+                        .and_then(|path_to_open| items_by_project_path.remove(&path_to_open))
+                })
+                .collect::<Vec<_>>();
+
+            // Remove old panes from workspace panes list
+            workspace.update(&mut cx, |workspace, cx| {
+                if let Some((center_group, active_pane)) = center_group {
+                    workspace.remove_panes(workspace.center.root.clone(), cx);
+
+                    // Swap workspace center group
+                    workspace.center = PaneGroup::with_root(center_group);
+
+                    // Change the focus to the workspace first so that we retrigger focus in on the pane.
+                    todo!()
+                    // cx.focus_self();
+                    // if let Some(active_pane) = active_pane {
+                    //     cx.focus(&active_pane);
+                    // } else {
+                    //     cx.focus(workspace.panes.last().unwrap());
+                    // }
+                } else {
+                    todo!()
+                    // let old_center_handle = old_center_pane.and_then(|weak| weak.upgrade());
+                    // if let Some(old_center_handle) = old_center_handle {
+                    //     cx.focus(&old_center_handle)
+                    // } else {
+                    //     cx.focus_self()
+                    // }
+                }
 
-            //     let docks = serialized_workspace.docks;
-            //     workspace.left_dock.update(cx, |dock, cx| {
-            //         dock.set_open(docks.left.visible, cx);
-            //         if let Some(active_panel) = docks.left.active_panel {
-            //             if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
-            //                 dock.activate_panel(ix, cx);
-            //             }
-            //         }
-            //         dock.active_panel()
-            //             .map(|panel| panel.set_zoomed(docks.left.zoom, cx));
-            //         if docks.left.visible && docks.left.zoom {
-            //             cx.focus_self()
-            //         }
-            //     });
-            //     // TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something
-            //     workspace.right_dock.update(cx, |dock, cx| {
-            //         dock.set_open(docks.right.visible, cx);
-            //         if let Some(active_panel) = docks.right.active_panel {
-            //             if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
-            //                 dock.activate_panel(ix, cx);
-            //             }
-            //         }
-            //         dock.active_panel()
-            //             .map(|panel| panel.set_zoomed(docks.right.zoom, cx));
+                let docks = serialized_workspace.docks;
+                workspace.left_dock.update(cx, |dock, cx| {
+                    dock.set_open(docks.left.visible, cx);
+                    if let Some(active_panel) = docks.left.active_panel {
+                        if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
+                            dock.activate_panel(ix, cx);
+                        }
+                    }
+                    dock.active_panel()
+                        .map(|panel| panel.set_zoomed(docks.left.zoom, cx));
+                    if docks.left.visible && docks.left.zoom {
+                        todo!()
+                        // cx.focus_self()
+                    }
+                });
+                // TODO: I think the bug is that setting zoom or active undoes the bottom zoom or something
+                workspace.right_dock.update(cx, |dock, cx| {
+                    dock.set_open(docks.right.visible, cx);
+                    if let Some(active_panel) = docks.right.active_panel {
+                        if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
+                            dock.activate_panel(ix, cx);
+                        }
+                    }
+                    dock.active_panel()
+                        .map(|panel| panel.set_zoomed(docks.right.zoom, cx));
 
-            //         if docks.right.visible && docks.right.zoom {
-            //             cx.focus_self()
-            //         }
-            //     });
-            //     workspace.bottom_dock.update(cx, |dock, cx| {
-            //         dock.set_open(docks.bottom.visible, cx);
-            //         if let Some(active_panel) = docks.bottom.active_panel {
-            //             if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
-            //                 dock.activate_panel(ix, cx);
-            //             }
-            //         }
+                    if docks.right.visible && docks.right.zoom {
+                        todo!()
+                        // cx.focus_self()
+                    }
+                });
+                workspace.bottom_dock.update(cx, |dock, cx| {
+                    dock.set_open(docks.bottom.visible, cx);
+                    if let Some(active_panel) = docks.bottom.active_panel {
+                        if let Some(ix) = dock.panel_index_for_ui_name(&active_panel, cx) {
+                            dock.activate_panel(ix, cx);
+                        }
+                    }
 
-            //         dock.active_panel()
-            //             .map(|panel| panel.set_zoomed(docks.bottom.zoom, cx));
+                    dock.active_panel()
+                        .map(|panel| panel.set_zoomed(docks.bottom.zoom, cx));
 
-            //         if docks.bottom.visible && docks.bottom.zoom {
-            //             cx.focus_self()
-            //         }
-            //     });
+                    if docks.bottom.visible && docks.bottom.zoom {
+                        todo!()
+                        // cx.focus_self()
+                    }
+                });
 
-            //     cx.notify();
-            // })?;
+                cx.notify();
+            })?;
 
             // Serialize ourself to make sure our timestamps and any pane / item changes are replicated
-            // workspace.update(&mut cx, |workspace, cx| workspace.serialize_workspace(cx))?;
+            workspace.update(&mut cx, |workspace, cx| workspace.serialize_workspace(cx))?;
 
-            // Ok(opened_items)
-            anyhow::bail!("todo")
+            Ok(opened_items)
         })
     }
 
@@ -3585,49 +3586,48 @@ fn window_bounds_env_override(cx: &AsyncAppContext) -> Option<WindowBounds> {
 }
 
 fn open_items(
-    _serialized_workspace: Option<SerializedWorkspace>,
+    serialized_workspace: Option<SerializedWorkspace>,
     project_paths_to_open: Vec<(PathBuf, Option<ProjectPath>)>,
     app_state: Arc<AppState>,
     cx: &mut ViewContext<Workspace>,
 ) -> impl Future<Output = Result<Vec<Option<Result<Box<dyn ItemHandle>>>>>> {
     let mut opened_items = Vec::with_capacity(project_paths_to_open.len());
 
-    // todo!()
-    // if let Some(serialized_workspace) = serialized_workspace {
-    //     let restored_items = Workspace::load_workspace(
-    //         serialized_workspace,
-    //         project_paths_to_open
-    //             .iter()
-    //             .map(|(_, project_path)| project_path)
-    //             .cloned()
-    //             .collect(),
-    //         cx,
-    //     )
-    //     .await?;
-
-    //     let restored_project_paths = restored_items
-    //         .iter()
-    //         .filter_map(|item| item.as_ref()?.project_path(cx))
-    //         .collect::<HashSet<_>>();
-
-    //     for restored_item in restored_items {
-    //         opened_items.push(restored_item.map(Ok));
-    //     }
+    if let Some(serialized_workspace) = serialized_workspace {
+        let restored_items = Workspace::load_workspace(
+            serialized_workspace,
+            project_paths_to_open
+                .iter()
+                .map(|(_, project_path)| project_path)
+                .cloned()
+                .collect(),
+            cx,
+        )
+        .await?;
 
-    //     project_paths_to_open
-    //         .iter_mut()
-    //         .for_each(|(_, project_path)| {
-    //             if let Some(project_path_to_open) = project_path {
-    //                 if restored_project_paths.contains(project_path_to_open) {
-    //                     *project_path = None;
-    //                 }
-    //             }
-    //         });
-    // } else {
-    for _ in 0..project_paths_to_open.len() {
-        opened_items.push(None);
+        let restored_project_paths = restored_items
+            .iter()
+            .filter_map(|item| item.as_ref()?.project_path(cx))
+            .collect::<HashSet<_>>();
+
+        for restored_item in restored_items {
+            opened_items.push(restored_item.map(Ok));
+        }
+
+        project_paths_to_open
+            .iter_mut()
+            .for_each(|(_, project_path)| {
+                if let Some(project_path_to_open) = project_path {
+                    if restored_project_paths.contains(project_path_to_open) {
+                        *project_path = None;
+                    }
+                }
+            });
+    } else {
+        for _ in 0..project_paths_to_open.len() {
+            opened_items.push(None);
+        }
     }
-    // }
     assert!(opened_items.len() == project_paths_to_open.len());
 
     let tasks =