wip progress

KCaverly created

Change summary

crates/workspace2/src/dock.rs              | 419 ++++++++++++-----------
crates/workspace2/src/item.rs              |  92 ++--
crates/workspace2/src/pane.rs              |  16 
crates/workspace2/src/pane_group.rs        |  70 ++-
crates/workspace2/src/persistence/model.rs |   4 
crates/workspace2/src/searchable.rs        |  50 +-
crates/workspace2/src/workspace2.rs        | 263 ++++++++------
7 files changed, 474 insertions(+), 440 deletions(-)

Detailed changes

crates/workspace2/src/dock.rs 🔗

@@ -1,6 +1,7 @@
 use crate::{status_bar::StatusItemView, Axis, Workspace};
 use gpui2::{
-    Action, AnyView, EventEmitter, Render, Subscription, View, ViewContext, WeakView, WindowContext,
+    Action, AnyView, Div, EventEmitter, Render, Subscription, View, ViewContext, WeakView,
+    WindowContext,
 };
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
@@ -176,226 +177,226 @@ pub struct PanelButtons {
     workspace: WeakView<Workspace>,
 }
 
-// impl Dock {
-//     pub fn new(position: DockPosition) -> Self {
-//         Self {
-//             position,
-//             panel_entries: Default::default(),
-//             active_panel_index: 0,
-//             is_open: false,
-//         }
-//     }
+impl Dock {
+    //     pub fn new(position: DockPosition) -> Self {
+    //         Self {
+    //             position,
+    //             panel_entries: Default::default(),
+    //             active_panel_index: 0,
+    //             is_open: false,
+    //         }
+    //     }
 
-//     pub fn position(&self) -> DockPosition {
-//         self.position
-//     }
+    //     pub fn position(&self) -> DockPosition {
+    //         self.position
+    //     }
 
-//     pub fn is_open(&self) -> bool {
-//         self.is_open
-//     }
+    pub fn is_open(&self) -> bool {
+        self.is_open
+    }
 
-//     pub fn has_focus(&self, cx: &WindowContext) -> bool {
-//         self.visible_panel()
-//             .map_or(false, |panel| panel.has_focus(cx))
-//     }
+    //     pub fn has_focus(&self, cx: &WindowContext) -> bool {
+    //         self.visible_panel()
+    //             .map_or(false, |panel| panel.has_focus(cx))
+    //     }
 
-//     pub fn panel<T: Panel>(&self) -> Option<View<T>> {
-//         self.panel_entries
-//             .iter()
-//             .find_map(|entry| entry.panel.as_any().clone().downcast())
-//     }
+    //     pub fn panel<T: Panel>(&self) -> Option<View<T>> {
+    //         self.panel_entries
+    //             .iter()
+    //             .find_map(|entry| entry.panel.as_any().clone().downcast())
+    //     }
 
-//     pub fn panel_index_for_type<T: Panel>(&self) -> Option<usize> {
-//         self.panel_entries
-//             .iter()
-//             .position(|entry| entry.panel.as_any().is::<T>())
-//     }
+    //     pub fn panel_index_for_type<T: Panel>(&self) -> Option<usize> {
+    //         self.panel_entries
+    //             .iter()
+    //             .position(|entry| entry.panel.as_any().is::<T>())
+    //     }
 
-//     pub fn panel_index_for_ui_name(&self, ui_name: &str, cx: &AppContext) -> Option<usize> {
-//         todo!()
-//         // self.panel_entries.iter().position(|entry| {
-//         //     let panel = entry.panel.as_any();
-//         //     cx.view_ui_name(panel.window(), panel.id()) == Some(ui_name)
-//         // })
-//     }
+    //     pub fn panel_index_for_ui_name(&self, ui_name: &str, cx: &AppContext) -> Option<usize> {
+    //         todo!()
+    //         // self.panel_entries.iter().position(|entry| {
+    //         //     let panel = entry.panel.as_any();
+    //         //     cx.view_ui_name(panel.window(), panel.id()) == Some(ui_name)
+    //         // })
+    //     }
 
-//     pub fn active_panel_index(&self) -> usize {
-//         self.active_panel_index
-//     }
+    //     pub fn active_panel_index(&self) -> usize {
+    //         self.active_panel_index
+    //     }
 
-//     pub(crate) fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
-//         if open != self.is_open {
-//             self.is_open = open;
-//             if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
-//                 active_panel.panel.set_active(open, cx);
-//             }
+    //     pub(crate) fn set_open(&mut self, open: bool, cx: &mut ViewContext<Self>) {
+    //         if open != self.is_open {
+    //             self.is_open = open;
+    //             if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+    //                 active_panel.panel.set_active(open, cx);
+    //             }
 
-//             cx.notify();
-//         }
-//     }
+    //             cx.notify();
+    //         }
+    //     }
 
-//     pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext<Self>) {
-//         for entry in &mut self.panel_entries {
-//             if entry.panel.as_any() == panel {
-//                 if zoomed != entry.panel.is_zoomed(cx) {
-//                     entry.panel.set_zoomed(zoomed, cx);
-//                 }
-//             } else if entry.panel.is_zoomed(cx) {
-//                 entry.panel.set_zoomed(false, cx);
-//             }
-//         }
+    //     pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext<Self>) {
+    //         for entry in &mut self.panel_entries {
+    //             if entry.panel.as_any() == panel {
+    //                 if zoomed != entry.panel.is_zoomed(cx) {
+    //                     entry.panel.set_zoomed(zoomed, cx);
+    //                 }
+    //             } else if entry.panel.is_zoomed(cx) {
+    //                 entry.panel.set_zoomed(false, cx);
+    //             }
+    //         }
+
+    //         cx.notify();
+    //     }
 
-//         cx.notify();
-//     }
+    //     pub fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
+    //         for entry in &mut self.panel_entries {
+    //             if entry.panel.is_zoomed(cx) {
+    //                 entry.panel.set_zoomed(false, cx);
+    //             }
+    //         }
+    //     }
 
-//     pub fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
-//         for entry in &mut self.panel_entries {
-//             if entry.panel.is_zoomed(cx) {
-//                 entry.panel.set_zoomed(false, cx);
-//             }
-//         }
-//     }
+    //     pub(crate) fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>) {
+    //         let subscriptions = [
+    //             cx.observe(&panel, |_, _, cx| cx.notify()),
+    //             cx.subscribe(&panel, |this, panel, event, cx| {
+    //                 if T::should_activate_on_event(event) {
+    //                     if let Some(ix) = this
+    //                         .panel_entries
+    //                         .iter()
+    //                         .position(|entry| entry.panel.id() == panel.id())
+    //                     {
+    //                         this.set_open(true, cx);
+    //                         this.activate_panel(ix, cx);
+    //                         cx.focus(&panel);
+    //                     }
+    //                 } else if T::should_close_on_event(event)
+    //                     && this.visible_panel().map_or(false, |p| p.id() == panel.id())
+    //                 {
+    //                     this.set_open(false, cx);
+    //                 }
+    //             }),
+    //         ];
+
+    //         let dock_view_id = cx.view_id();
+    //         self.panel_entries.push(PanelEntry {
+    //             panel: Arc::new(panel),
+    //             // todo!()
+    //             // context_menu: cx.add_view(|cx| {
+    //             //     let mut menu = ContextMenu::new(dock_view_id, cx);
+    //             //     menu.set_position_mode(OverlayPositionMode::Local);
+    //             //     menu
+    //             // }),
+    //             _subscriptions: subscriptions,
+    //         });
+    //         cx.notify()
+    //     }
 
-//     pub(crate) fn add_panel<T: Panel>(&mut self, panel: View<T>, cx: &mut ViewContext<Self>) {
-//         let subscriptions = [
-//             cx.observe(&panel, |_, _, cx| cx.notify()),
-//             cx.subscribe(&panel, |this, panel, event, cx| {
-//                 if T::should_activate_on_event(event) {
-//                     if let Some(ix) = this
-//                         .panel_entries
-//                         .iter()
-//                         .position(|entry| entry.panel.id() == panel.id())
-//                     {
-//                         this.set_open(true, cx);
-//                         this.activate_panel(ix, cx);
-//                         cx.focus(&panel);
-//                     }
-//                 } else if T::should_close_on_event(event)
-//                     && this.visible_panel().map_or(false, |p| p.id() == panel.id())
-//                 {
-//                     this.set_open(false, cx);
-//                 }
-//             }),
-//         ];
-
-//         let dock_view_id = cx.view_id();
-//         self.panel_entries.push(PanelEntry {
-//             panel: Arc::new(panel),
-//             // todo!()
-//             // context_menu: cx.add_view(|cx| {
-//             //     let mut menu = ContextMenu::new(dock_view_id, cx);
-//             //     menu.set_position_mode(OverlayPositionMode::Local);
-//             //     menu
-//             // }),
-//             _subscriptions: subscriptions,
-//         });
-//         cx.notify()
-//     }
+    //     pub fn remove_panel<T: Panel>(&mut self, panel: &View<T>, cx: &mut ViewContext<Self>) {
+    //         if let Some(panel_ix) = self
+    //             .panel_entries
+    //             .iter()
+    //             .position(|entry| entry.panel.id() == panel.id())
+    //         {
+    //             if panel_ix == self.active_panel_index {
+    //                 self.active_panel_index = 0;
+    //                 self.set_open(false, cx);
+    //             } else if panel_ix < self.active_panel_index {
+    //                 self.active_panel_index -= 1;
+    //             }
+    //             self.panel_entries.remove(panel_ix);
+    //             cx.notify();
+    //         }
+    //     }
 
-//     pub fn remove_panel<T: Panel>(&mut self, panel: &View<T>, cx: &mut ViewContext<Self>) {
-//         if let Some(panel_ix) = self
-//             .panel_entries
-//             .iter()
-//             .position(|entry| entry.panel.id() == panel.id())
-//         {
-//             if panel_ix == self.active_panel_index {
-//                 self.active_panel_index = 0;
-//                 self.set_open(false, cx);
-//             } else if panel_ix < self.active_panel_index {
-//                 self.active_panel_index -= 1;
-//             }
-//             self.panel_entries.remove(panel_ix);
-//             cx.notify();
-//         }
-//     }
+    //     pub fn panels_len(&self) -> usize {
+    //         self.panel_entries.len()
+    //     }
 
-//     pub fn panels_len(&self) -> usize {
-//         self.panel_entries.len()
-//     }
+    //     pub fn activate_panel(&mut self, panel_ix: usize, cx: &mut ViewContext<Self>) {
+    //         if panel_ix != self.active_panel_index {
+    //             if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+    //                 active_panel.panel.set_active(false, cx);
+    //             }
 
-//     pub fn activate_panel(&mut self, panel_ix: usize, cx: &mut ViewContext<Self>) {
-//         if panel_ix != self.active_panel_index {
-//             if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
-//                 active_panel.panel.set_active(false, cx);
-//             }
+    //             self.active_panel_index = panel_ix;
+    //             if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
+    //                 active_panel.panel.set_active(true, cx);
+    //             }
 
-//             self.active_panel_index = panel_ix;
-//             if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) {
-//                 active_panel.panel.set_active(true, cx);
-//             }
-
-//             cx.notify();
-//         }
-//     }
+    //             cx.notify();
+    //         }
+    //     }
 
-//     pub fn visible_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
-//         let entry = self.visible_entry()?;
-//         Some(&entry.panel)
-//     }
+    pub fn visible_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
+        let entry = self.visible_entry()?;
+        Some(&entry.panel)
+    }
 
-//     pub fn active_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
-//         Some(&self.panel_entries.get(self.active_panel_index)?.panel)
-//     }
+    //     pub fn active_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
+    //         Some(&self.panel_entries.get(self.active_panel_index)?.panel)
+    //     }
 
-//     fn visible_entry(&self) -> Option<&PanelEntry> {
-//         if self.is_open {
-//             self.panel_entries.get(self.active_panel_index)
-//         } else {
-//             None
-//         }
-//     }
+    fn visible_entry(&self) -> Option<&PanelEntry> {
+        if self.is_open {
+            self.panel_entries.get(self.active_panel_index)
+        } else {
+            None
+        }
+    }
 
-//     pub fn zoomed_panel(&self, cx: &WindowContext) -> Option<Arc<dyn PanelHandle>> {
-//         let entry = self.visible_entry()?;
-//         if entry.panel.is_zoomed(cx) {
-//             Some(entry.panel.clone())
-//         } else {
-//             None
-//         }
-//     }
+    //     pub fn zoomed_panel(&self, cx: &WindowContext) -> Option<Arc<dyn PanelHandle>> {
+    //         let entry = self.visible_entry()?;
+    //         if entry.panel.is_zoomed(cx) {
+    //             Some(entry.panel.clone())
+    //         } else {
+    //             None
+    //         }
+    //     }
 
-//     pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option<f32> {
-//         self.panel_entries
-//             .iter()
-//             .find(|entry| entry.panel.id() == panel.id())
-//             .map(|entry| entry.panel.size(cx))
-//     }
+    //     pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option<f32> {
+    //         self.panel_entries
+    //             .iter()
+    //             .find(|entry| entry.panel.id() == panel.id())
+    //             .map(|entry| entry.panel.size(cx))
+    //     }
 
-//     pub fn active_panel_size(&self, cx: &WindowContext) -> Option<f32> {
-//         if self.is_open {
-//             self.panel_entries
-//                 .get(self.active_panel_index)
-//                 .map(|entry| entry.panel.size(cx))
-//         } else {
-//             None
-//         }
-//     }
+    //     pub fn active_panel_size(&self, cx: &WindowContext) -> Option<f32> {
+    //         if self.is_open {
+    //             self.panel_entries
+    //                 .get(self.active_panel_index)
+    //                 .map(|entry| entry.panel.size(cx))
+    //         } else {
+    //             None
+    //         }
+    //     }
 
-//     pub fn resize_active_panel(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
-//         if let Some(entry) = self.panel_entries.get_mut(self.active_panel_index) {
-//             entry.panel.set_size(size, cx);
-//             cx.notify();
-//         }
-//     }
+    //     pub fn resize_active_panel(&mut self, size: Option<f32>, cx: &mut ViewContext<Self>) {
+    //         if let Some(entry) = self.panel_entries.get_mut(self.active_panel_index) {
+    //             entry.panel.set_size(size, cx);
+    //             cx.notify();
+    //         }
+    //     }
 
-//     pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement<Workspace> {
-//         todo!()
-// if let Some(active_entry) = self.visible_entry() {
-//     Empty::new()
-//         .into_any()
-//         .contained()
-//         .with_style(self.style(cx))
-//         .resizable::<WorkspaceBounds>(
-//             self.position.to_resize_handle_side(),
-//             active_entry.panel.size(cx),
-//             |_, _, _| {},
-//         )
-//         .into_any()
-// } else {
-//     Empty::new().into_any()
-// }
-//     }
-// }
+    //     pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement<Workspace> {
+    //         todo!()
+    // if let Some(active_entry) = self.visible_entry() {
+    //     Empty::new()
+    //         .into_any()
+    //         .contained()
+    //         .with_style(self.style(cx))
+    //         .resizable::<WorkspaceBounds>(
+    //             self.position.to_resize_handle_side(),
+    //             active_entry.panel.size(cx),
+    //             |_, _, _| {},
+    //         )
+    //         .into_any()
+    // } else {
+    //     Empty::new().into_any()
+    // }
+    //     }
+}
 
 // todo!()
 // impl View for Dock {
@@ -596,15 +597,23 @@ impl EventEmitter for PanelButtons {
 //     }
 // }
 
-// impl StatusItemView for PanelButtons {
-//     fn set_active_pane_item(
-//         &mut self,
-//         active_pane_item: Option<&dyn crate::ItemHandle>,
-//         cx: &mut ViewContext<Self>,
-//     ) {
-//         todo!()
-//     }
-// }
+impl Render for PanelButtons {
+    type Element = Div<Self>;
+
+    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+        todo!()
+    }
+}
+
+impl StatusItemView for PanelButtons {
+    fn set_active_pane_item(
+        &mut self,
+        active_pane_item: Option<&dyn crate::ItemHandle>,
+        cx: &mut ViewContext<Self>,
+    ) {
+        todo!()
+    }
+}
 
 #[cfg(any(test, feature = "test-support"))]
 pub mod test {

crates/workspace2/src/item.rs 🔗

@@ -691,58 +691,58 @@ pub trait FollowableItemHandle: ItemHandle {
     fn is_project_item(&self, cx: &AppContext) -> bool;
 }
 
-// impl<T: FollowableItem> FollowableItemHandle for View<T> {
-//     fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId> {
-//         self.read(cx).remote_id().or_else(|| {
-//             client.peer_id().map(|creator| ViewId {
-//                 creator,
-//                 id: self.id() as u64,
-//             })
-//         })
-//     }
+impl<T: FollowableItem> FollowableItemHandle for View<T> {
+    fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId> {
+        self.read(cx).remote_id().or_else(|| {
+            client.peer_id().map(|creator| ViewId {
+                creator,
+                id: self.id() as u64,
+            })
+        })
+    }
 
-//     fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext) {
-//         self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx))
-//     }
+    fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext) {
+        self.update(cx, |this, cx| this.set_leader_peer_id(leader_peer_id, cx))
+    }
 
-//     fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
-//         self.read(cx).to_state_proto(cx)
-//     }
+    fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant> {
+        self.read(cx).to_state_proto(cx)
+    }
 
-//     fn add_event_to_update_proto(
-//         &self,
-//         event: &dyn Any,
-//         update: &mut Option<proto::update_view::Variant>,
-//         cx: &AppContext,
-//     ) -> bool {
-//         if let Some(event) = event.downcast_ref() {
-//             self.read(cx).add_event_to_update_proto(event, update, cx)
-//         } else {
-//             false
-//         }
-//     }
+    fn add_event_to_update_proto(
+        &self,
+        event: &dyn Any,
+        update: &mut Option<proto::update_view::Variant>,
+        cx: &AppContext,
+    ) -> bool {
+        if let Some(event) = event.downcast_ref() {
+            self.read(cx).add_event_to_update_proto(event, update, cx)
+        } else {
+            false
+        }
+    }
 
-//     fn apply_update_proto(
-//         &self,
-//         project: &Model<Project>,
-//         message: proto::update_view::Variant,
-//         cx: &mut WindowContext,
-//     ) -> Task<Result<()>> {
-//         self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
-//     }
+    fn apply_update_proto(
+        &self,
+        project: &Model<Project>,
+        message: proto::update_view::Variant,
+        cx: &mut WindowContext,
+    ) -> Task<Result<()>> {
+        self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
+    }
 
-//     fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
-//         if let Some(event) = event.downcast_ref() {
-//             T::should_unfollow_on_event(event, cx)
-//         } else {
-//             false
-//         }
-//     }
+    fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
+        if let Some(event) = event.downcast_ref() {
+            T::should_unfollow_on_event(event, cx)
+        } else {
+            false
+        }
+    }
 
-//     fn is_project_item(&self, cx: &AppContext) -> bool {
-//         self.read(cx).is_project_item(cx)
-//     }
-// }
+    fn is_project_item(&self, cx: &AppContext) -> bool {
+        self.read(cx).is_project_item(cx)
+    }
+}
 
 // #[cfg(any(test, feature = "test-support"))]
 // pub mod test {

crates/workspace2/src/pane.rs 🔗

@@ -178,7 +178,7 @@ pub struct Pane {
     //     tab_context_menu: ViewHandle<ContextMenu>,
     //     workspace: WeakView<Workspace>,
     project: Model<Project>,
-    //     has_focus: bool,
+    has_focus: bool,
     //     can_drop: Rc<dyn Fn(&DragAndDrop<Workspace>, &WindowContext) -> bool>,
     //     can_split: bool,
     //     render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>,
@@ -348,7 +348,7 @@ impl Pane {
             // tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)),
             // workspace,
             project,
-            // has_focus: false,
+            has_focus: false,
             // can_drop: Rc::new(|_, _| true),
             // can_split: true,
             // render_tab_bar_buttons: Rc::new(move |pane, cx| {
@@ -415,9 +415,9 @@ impl Pane {
     //         &self.workspace
     //     }
 
-    //     pub fn has_focus(&self) -> bool {
-    //         self.has_focus
-    //     }
+    pub fn has_focus(&self) -> bool {
+        self.has_focus
+    }
 
     //     pub fn active_item_index(&self) -> usize {
     //         self.active_item_index
@@ -614,9 +614,9 @@ impl Pane {
         self.items.len()
     }
 
-    //     pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> + DoubleEndedIterator {
-    //         self.items.iter()
-    //     }
+    pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> + DoubleEndedIterator {
+        self.items.iter()
+    }
 
     //     pub fn items_of_type<T: View>(&self) -> impl '_ + Iterator<Item = ViewHandle<T>> {
     //         self.items

crates/workspace2/src/pane_group.rs 🔗

@@ -2,22 +2,24 @@ use crate::{AppState, FollowerState, Pane, Workspace};
 use anyhow::{anyhow, Result};
 use call2::ActiveCall;
 use collections::HashMap;
-use gpui2::{size, AnyElement, AnyView, Bounds, Handle, Model, Pixels, Point, View, ViewContext};
+use gpui2::{point, size, AnyElement, AnyView, Bounds, Model, Pixels, Point, View, ViewContext};
+use parking_lot::Mutex;
 use project2::Project;
 use serde::Deserialize;
-use std::{cell::RefCell, rc::Rc, sync::Arc};
+use std::sync::Arc;
 use theme2::Theme;
 
 const HANDLE_HITBOX_SIZE: f32 = 4.0;
 const HORIZONTAL_MIN_SIZE: f32 = 80.;
 const VERTICAL_MIN_SIZE: f32 = 100.;
 
+#[derive(Copy, Clone, PartialEq, Eq)]
 pub enum Axis {
     Vertical,
     Horizontal,
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, PartialEq)]
 pub struct PaneGroup {
     pub(crate) root: Member,
 }
@@ -305,18 +307,24 @@ impl Member {
     }
 }
 
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone)]
 pub(crate) struct PaneAxis {
     pub axis: Axis,
     pub members: Vec<Member>,
-    pub flexes: Rc<RefCell<Vec<f32>>>,
-    pub bounding_boxes: Rc<RefCell<Vec<Option<Bounds<Pixels>>>>>,
+    pub flexes: Arc<Mutex<Vec<f32>>>,
+    pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+}
+
+impl PartialEq for PaneAxis {
+    fn eq(&self, other: &Self) -> bool {
+        todo!()
+    }
 }
 
 impl PaneAxis {
     pub fn new(axis: Axis, members: Vec<Member>) -> Self {
-        let flexes = Rc::new(RefCell::new(vec![1.; members.len()]));
-        let bounding_boxes = Rc::new(RefCell::new(vec![None; members.len()]));
+        let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));
+        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
         Self {
             axis,
             members,
@@ -329,8 +337,8 @@ impl PaneAxis {
         let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]);
         debug_assert!(members.len() == flexes.len());
 
-        let flexes = Rc::new(RefCell::new(flexes));
-        let bounding_boxes = Rc::new(RefCell::new(vec![None; members.len()]));
+        let flexes = Arc::new(Mutex::new(flexes));
+        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
         Self {
             axis,
             members,
@@ -360,7 +368,7 @@ impl PaneAxis {
                             }
 
                             self.members.insert(idx, Member::Pane(new_pane.clone()));
-                            *self.flexes.borrow_mut() = vec![1.; self.members.len()];
+                            *self.flexes.lock() = vec![1.; self.members.len()];
                         } else {
                             *member =
                                 Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
@@ -400,12 +408,12 @@ impl PaneAxis {
         if found_pane {
             if let Some(idx) = remove_member {
                 self.members.remove(idx);
-                *self.flexes.borrow_mut() = vec![1.; self.members.len()];
+                *self.flexes.lock() = vec![1.; self.members.len()];
             }
 
             if self.members.len() == 1 {
                 let result = self.members.pop();
-                *self.flexes.borrow_mut() = vec![1.; self.members.len()];
+                *self.flexes.lock() = vec![1.; self.members.len()];
                 Ok(result)
             } else {
                 Ok(None)
@@ -431,13 +439,13 @@ impl PaneAxis {
     }
 
     fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
-        debug_assert!(self.members.len() == self.bounding_boxes.borrow().len());
+        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 
         for (idx, member) in self.members.iter().enumerate() {
             match member {
                 Member::Pane(found) => {
                     if pane == found {
-                        return self.bounding_boxes.borrow()[idx];
+                        return self.bounding_boxes.lock()[idx];
                     }
                 }
                 Member::Axis(axis) => {
@@ -451,9 +459,9 @@ impl PaneAxis {
     }
 
     fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&View<Pane>> {
-        debug_assert!(self.members.len() == self.bounding_boxes.borrow().len());
+        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
 
-        let bounding_boxes = self.bounding_boxes.borrow();
+        let bounding_boxes = self.bounding_boxes.lock();
 
         for (idx, member) in self.members.iter().enumerate() {
             if let Some(coordinates) = bounding_boxes[idx] {
@@ -480,7 +488,7 @@ impl PaneAxis {
         app_state: &Arc<AppState>,
         cx: &mut ViewContext<Workspace>,
     ) -> AnyElement<Workspace> {
-        debug_assert!(self.members.len() == self.flexes.borrow().len());
+        debug_assert!(self.members.len() == self.flexes.lock().len());
 
         todo!()
         // let mut pane_axis = PaneAxisElement::new(
@@ -546,32 +554,32 @@ impl SplitDirection {
         [Self::Up, Self::Down, Self::Left, Self::Right]
     }
 
-    pub fn edge(&self, rect: Bounds<Pixels>) -> f32 {
+    pub fn edge(&self, rect: Bounds<Pixels>) -> Pixels {
         match self {
-            Self::Up => rect.min_y(),
-            Self::Down => rect.max_y(),
-            Self::Left => rect.min_x(),
-            Self::Right => rect.max_x(),
+            Self::Up => rect.origin.y,
+            Self::Down => rect.lower_left().y,
+            Self::Left => rect.lower_left().x,
+            Self::Right => rect.lower_right().x,
         }
     }
 
     pub fn along_edge(&self, bounds: Bounds<Pixels>, length: Pixels) -> Bounds<Pixels> {
         match self {
             Self::Up => Bounds {
-                origin: bounds.origin(),
-                size: size(bounds.width(), length),
+                origin: bounds.origin,
+                size: size(bounds.size.width, length),
             },
             Self::Down => Bounds {
-                origin: size(bounds.min_x(), bounds.max_y() - length),
-                size: size(bounds.width(), length),
+                origin: point(bounds.lower_left().x, bounds.lower_left().y - length),
+                size: size(bounds.size.width, length),
             },
             Self::Left => Bounds {
-                origin: bounds.origin(),
-                size: size(length, bounds.height()),
+                origin: bounds.origin,
+                size: size(length, bounds.size.height),
             },
             Self::Right => Bounds {
-                origin: size(bounds.max_x() - length, bounds.min_y()),
-                size: size(length, bounds.height()),
+                origin: point(bounds.lower_right().x - length, bounds.lower_left().y),
+                size: size(length, bounds.size.height),
             },
         }
     }

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

@@ -55,7 +55,7 @@ impl Column for WorkspaceLocation {
     }
 }
 
-#[derive(Debug, PartialEq, Clone)]
+#[derive(PartialEq, Clone)]
 pub struct SerializedWorkspace {
     pub id: WorkspaceId,
     pub location: WorkspaceLocation,
@@ -127,7 +127,7 @@ impl Bind for DockData {
     }
 }
 
-#[derive(Debug, PartialEq, Clone)]
+#[derive(PartialEq, Clone)]
 pub enum SerializedPaneGroup {
     Group {
         axis: Axis,

crates/workspace2/src/searchable.rs 🔗

@@ -1,6 +1,6 @@
 use std::{any::Any, sync::Arc};
 
-use gpui2::{AppContext, Subscription, Task, View, ViewContext, WindowContext};
+use gpui2::{AnyView, AppContext, Subscription, Task, View, ViewContext, WindowContext};
 use project2::search::SearchQuery;
 
 use crate::{
@@ -95,7 +95,7 @@ pub trait SearchableItemHandle: ItemHandle {
     fn subscribe_to_search_events(
         &self,
         cx: &mut WindowContext,
-        handler: Box<dyn Fn(SearchEvent, &mut WindowContext)>,
+        handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>,
     ) -> Subscription;
     fn clear_matches(&self, cx: &mut WindowContext);
     fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
@@ -130,7 +130,8 @@ pub trait SearchableItemHandle: ItemHandle {
 
 impl<T: SearchableItem> SearchableItemHandle for View<T> {
     fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle> {
-        Box::new(self.downgrade())
+        // Box::new(self.downgrade())
+        todo!()
     }
 
     fn boxed_clone(&self) -> Box<dyn SearchableItemHandle> {
@@ -144,7 +145,7 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
     fn subscribe_to_search_events(
         &self,
         cx: &mut WindowContext,
-        handler: Box<dyn Fn(SearchEvent, &mut WindowContext)>,
+        handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>,
     ) -> Subscription {
         cx.subscribe(self, move |handle, event, cx| {
             let search_event = handle.update(cx, |handle, cx| handle.to_search_event(event, cx));
@@ -198,7 +199,7 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
         cx: &mut WindowContext,
     ) -> Task<Vec<Box<dyn Any + Send>>> {
         let matches = self.update(cx, |this, cx| this.find_matches(query, cx));
-        cx.foreground().spawn(async {
+        cx.spawn_on_main(|cx| async {
             let matches = matches.await;
             matches
                 .into_iter()
@@ -231,23 +232,21 @@ fn downcast_matches<T: Any + Clone>(matches: &Vec<Box<dyn Any + Send>>) -> Vec<T
         )
 }
 
-// todo!()
-// impl From<Box<dyn SearchableItemHandle>> for AnyViewHandle {
-//     fn from(this: Box<dyn SearchableItemHandle>) -> Self {
-//         this.as_any().clone()
-//     }
-// }
+impl From<Box<dyn SearchableItemHandle>> for AnyView {
+    fn from(this: Box<dyn SearchableItemHandle>) -> Self {
+        this.to_any().clone()
+    }
+}
 
-// todo!()
-// impl From<&Box<dyn SearchableItemHandle>> for AnyViewHandle {
-//     fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
-//         this.as_any().clone()
-//     }
-// }
+impl From<&Box<dyn SearchableItemHandle>> for AnyView {
+    fn from(this: &Box<dyn SearchableItemHandle>) -> Self {
+        this.to_any().clone()
+    }
+}
 
 impl PartialEq for Box<dyn SearchableItemHandle> {
     fn eq(&self, other: &Self) -> bool {
-        self.id() == other.id() && self.window() == other.window()
+        self.id() == other.id()
     }
 }
 
@@ -256,24 +255,23 @@ impl Eq for Box<dyn SearchableItemHandle> {}
 pub trait WeakSearchableItemHandle: WeakItemHandle {
     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
 
-    // todo!()
-    // fn into_any(self) -> AnyWeakViewHandle;
+    // fn into_any(self) -> AnyWeakView;
 }
 
 // todo!()
-// impl<T: SearchableItem> WeakSearchableItemHandle for WeakViewHandle<T> {
+// impl<T: SearchableItem> WeakSearchableItemHandle for WeakView<T> {
 //     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
 //         Some(Box::new(self.upgrade(cx)?))
 //     }
 
-//     fn into_any(self) -> AnyWeakViewHandle {
-//         self.into_any()
-//     }
+//     // fn into_any(self) -> AnyView {
+//     //     self.into_any()
+//     // }
 // }
 
 impl PartialEq for Box<dyn WeakSearchableItemHandle> {
     fn eq(&self, other: &Self) -> bool {
-        self.id() == other.id() && self.window() == other.window()
+        self.id() == other.id()
     }
 }
 
@@ -281,6 +279,6 @@ impl Eq for Box<dyn WeakSearchableItemHandle> {}
 
 impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        (self.id(), self.window().id()).hash(state)
+        self.id().hash(state)
     }
 }

crates/workspace2/src/workspace2.rs 🔗

@@ -11,21 +11,25 @@ mod toolbar;
 mod workspace_settings;
 
 use anyhow::{anyhow, Result};
+use call2::ActiveCall;
 use client2::{
     proto::{self, PeerId},
     Client, UserStore,
 };
 use collections::{HashMap, HashSet};
+use dock::Dock;
 use futures::{channel::oneshot, FutureExt};
 use gpui2::{
-    AnyModel, AnyView, AppContext, AsyncAppContext, DisplayId, MainThread, Model, Task, View,
-    ViewContext, VisualContext, WeakModel, WeakView, WindowBounds, WindowHandle, WindowOptions,
+    AnyModel, AnyView, AppContext, AsyncAppContext, DisplayId, EventEmitter, MainThread, Model,
+    Subscription, Task, View, ViewContext, VisualContext, WeakModel, WeakView, WindowBounds,
+    WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
 use language2::LanguageRegistry;
 use node_runtime::NodeRuntime;
 pub use pane::*;
 pub use pane_group::*;
+use persistence::model::{ItemId, WorkspaceLocation};
 use project2::{Project, ProjectEntryId, ProjectPath, Worktree};
 use std::{
     any::TypeId,
@@ -37,7 +41,8 @@ pub use toolbar::{ToolbarItemLocation, ToolbarItemView};
 use util::ResultExt;
 
 use crate::persistence::model::{
-    DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
+    DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup,
+    SerializedWorkspace,
 };
 
 // lazy_static! {
@@ -386,14 +391,13 @@ type ItemDeserializers = HashMap<
     ) -> Task<Result<Box<dyn ItemHandle>>>,
 >;
 pub fn register_deserializable_item<I: Item>(cx: &mut AppContext) {
-    cx.update_default_global(|deserializers: &mut ItemDeserializers, _cx| {
+    cx.update_global(|deserializers: &mut ItemDeserializers, _cx| {
         if let Some(serialized_item_kind) = I::serialized_item_kind() {
             deserializers.insert(
                 Arc::from(serialized_item_kind),
                 |project, workspace, workspace_id, item_id, cx| {
                     let task = I::deserialize(project, workspace, workspace_id, item_id, cx);
-                    cx.foreground()
-                        .spawn(async { Ok(Box::new(task.await?) as Box<_>) })
+                    cx.spawn_on_main(|cx| async { Ok(Box::new(task.await?) as Box<_>) })
                 },
             );
         }
@@ -426,6 +430,7 @@ struct Follower {
     peer_id: PeerId,
 }
 
+// todo!()
 // impl AppState {
 //     #[cfg(any(test, feature = "test-support"))]
 //     pub fn test(cx: &mut AppContext) -> Arc<Self> {
@@ -476,7 +481,7 @@ impl DelayedDebouncedEditAction {
 
     fn fire_new<F>(&mut self, delay: Duration, cx: &mut ViewContext<Workspace>, func: F)
     where
-        F: 'static + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> Task<Result<()>>,
+        F: 'static + Send + FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> Task<Result<()>>,
     {
         if let Some(channel) = self.cancel_channel.take() {
             _ = channel.send(());
@@ -517,7 +522,7 @@ pub struct Workspace {
     //     modal: Option<ActiveModal>,
     //     zoomed: Option<AnyWeakViewHandle>,
     //     zoomed_position: Option<DockPosition>,
-    //     center: PaneGroup,
+    center: PaneGroup,
     left_dock: View<Dock>,
     bottom_dock: View<Dock>,
     right_dock: View<Dock>,
@@ -533,9 +538,9 @@ pub struct Workspace {
     follower_states: HashMap<View<Pane>, FollowerState>,
     last_leaders_by_pane: HashMap<WeakView<Pane>, PeerId>,
     //     window_edited: bool,
-    //     active_call: Option<(ModelHandle<ActiveCall>, Vec<Subscription>)>,
+    active_call: Option<(Model<ActiveCall>, Vec<Subscription>)>,
     //     leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
-    //     database_id: WorkspaceId,
+    database_id: WorkspaceId,
     app_state: Arc<AppState>,
     //     subscriptions: Vec<Subscription>,
     //     _apply_leader_updates: Task<Result<()>>,
@@ -1925,19 +1930,21 @@ impl Workspace {
     //     }
 
     fn add_pane(&mut self, cx: &mut ViewContext<Self>) -> View<Pane> {
-        let pane = cx.build_view(|cx| {
-            Pane::new(
-                self.weak_handle(),
-                self.project.clone(),
-                self.pane_history_timestamp.clone(),
-                cx,
-            )
-        });
-        cx.subscribe(&pane, Self::handle_pane_event).detach();
-        self.panes.push(pane.clone());
-        cx.focus(&pane);
-        cx.emit(Event::PaneAdded(pane.clone()));
-        pane
+        todo!()
+        // let pane = cx.build_view(|cx| {
+        //     Pane::new(
+        //         self.weak_handle(),
+        //         self.project.clone(),
+        //         self.pane_history_timestamp.clone(),
+        //         cx,
+        //     )
+        // });
+        // cx.subscribe(&pane, Self::handle_pane_event).detach();
+        // self.panes.push(pane.clone());
+        // todo!()
+        // cx.focus(&pane);
+        // cx.emit(Event::PaneAdded(pane.clone()));
+        // pane
     }
 
     //     pub fn add_item_to_center(
@@ -2083,19 +2090,19 @@ impl Workspace {
     ) -> Task<
         Result<(
             ProjectEntryId,
-            impl 'static + FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
+            impl 'static + Send + FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
         )>,
     > {
         let project = self.project().clone();
         let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
-        cx.spawn(|_, mut cx| async move {
+        cx.spawn(|_, cx| async move {
             let (project_entry_id, project_item) = project_item.await?;
             let build_item = cx.update(|cx| {
                 cx.default_global::<ProjectItemBuilders>()
-                    .get(&project_item.model_type())
+                    .get(&project_item.type_id())
                     .ok_or_else(|| anyhow!("no item builder for project item"))
                     .cloned()
-            })?;
+            })??;
             let build_item =
                 move |cx: &mut ViewContext<Pane>| build_item(project, project_item, cx);
             Ok((project_entry_id, build_item))
@@ -3012,14 +3019,14 @@ impl Workspace {
         &self,
         project_only: bool,
         update: proto::update_followers::Variant,
-        cx: &AppContext,
+        cx: &mut AppContext,
     ) -> Option<()> {
         let project_id = if project_only {
             self.project.read(cx).remote_id()
         } else {
             None
         };
-        self.app_state().workspace_store.read_with(cx, |store, cx| {
+        self.app_state().workspace_store.update(cx, |store, cx| {
             store.update_followers(project_id, update, cx)
         })
     }
@@ -3141,9 +3148,9 @@ impl Workspace {
     //         }
     //     }
 
-    //     fn active_call(&self) -> Option<&ModelHandle<ActiveCall>> {
-    //         self.active_call.as_ref().map(|(call, _)| call)
-    //     }
+    fn active_call(&self) -> Option<&Model<ActiveCall>> {
+        self.active_call.as_ref().map(|(call, _)| call)
+    }
 
     //     fn on_active_call_event(
     //         &mut self,
@@ -3164,21 +3171,21 @@ impl Workspace {
     //         self.database_id
     //     }
 
-    //     fn location(&self, cx: &AppContext) -> Option<WorkspaceLocation> {
-    //         let project = self.project().read(cx);
+    fn location(&self, cx: &AppContext) -> Option<WorkspaceLocation> {
+        let project = self.project().read(cx);
 
-    //         if project.is_local() {
-    //             Some(
-    //                 project
-    //                     .visible_worktrees(cx)
-    //                     .map(|worktree| worktree.read(cx).abs_path())
-    //                     .collect::<Vec<_>>()
-    //                     .into(),
-    //             )
-    //         } else {
-    //             None
-    //         }
-    //     }
+        if project.is_local() {
+            Some(
+                project
+                    .visible_worktrees(cx)
+                    .map(|worktree| worktree.read(cx).abs_path())
+                    .collect::<Vec<_>>()
+                    .into(),
+            )
+        } else {
+            None
+        }
+    }
 
     //     fn remove_panes(&mut self, member: Member, cx: &mut ViewContext<Workspace>) {
     //         match member {
@@ -3193,14 +3200,17 @@ impl Workspace {
     //         }
     //     }
 
-    //     fn force_remove_pane(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Workspace>) {
-    //         self.panes.retain(|p| p != pane);
-    //         cx.focus(self.panes.last().unwrap());
-    //         if self.last_active_center_pane == Some(pane.downgrade()) {
-    //             self.last_active_center_pane = None;
-    //         }
-    //         cx.notify();
-    //     }
+    fn force_remove_pane(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Workspace>) {
+        self.panes.retain(|p| p != pane);
+        if true {
+            todo!()
+            // cx.focus(self.panes.last().unwrap());
+        }
+        if self.last_active_center_pane == Some(pane.downgrade()) {
+            self.last_active_center_pane = None;
+        }
+        cx.notify();
+    }
 
     //     fn schedule_serialize(&mut self, cx: &mut ViewContext<Self>) {
     //         self._schedule_serialize = Some(cx.spawn(|this, cx| async move {
@@ -3248,7 +3258,7 @@ impl Workspace {
                         .iter()
                         .map(|member| build_serialized_pane_group(member, cx))
                         .collect::<Vec<_>>(),
-                    flexes: Some(flexes.borrow().clone()),
+                    flexes: Some(flexes.lock().clone()),
                 },
                 Member::Pane(pane_handle) => {
                     SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))
@@ -3260,10 +3270,11 @@ impl Workspace {
             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| {
-                Some(
-                    cx.view_ui_name(panel.as_any().window(), panel.id())?
-                        .to_string(),
-                )
+                todo!()
+                // Some(
+                //     cx.view_ui_name(panel.as_any().window(), panel.id())?
+                //         .to_string(),
+                // )
             });
             let left_dock_zoom = left_dock
                 .visible_panel()
@@ -3273,10 +3284,11 @@ impl Workspace {
             let right_dock = this.right_dock.read(cx);
             let right_visible = right_dock.is_open();
             let right_active_panel = right_dock.visible_panel().and_then(|panel| {
-                Some(
-                    cx.view_ui_name(panel.as_any().window(), panel.id())?
-                        .to_string(),
-                )
+                todo!()
+                // Some(
+                //     cx.view_ui_name(panel.as_any().window(), panel.id())?
+                //         .to_string(),
+                // )
             });
             let right_dock_zoom = right_dock
                 .visible_panel()
@@ -3286,10 +3298,11 @@ impl Workspace {
             let bottom_dock = this.bottom_dock.read(cx);
             let bottom_visible = bottom_dock.is_open();
             let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| {
-                Some(
-                    cx.view_ui_name(panel.as_any().window(), panel.id())?
-                        .to_string(),
-                )
+                todo!()
+                // Some(
+                //     cx.view_ui_name(panel.as_any().window(), panel.id())?
+                //         .to_string(),
+                // )
             });
             let bottom_dock_zoom = bottom_dock
                 .visible_panel()
@@ -3332,8 +3345,7 @@ impl Workspace {
                     docks,
                 };
 
-                cx.background()
-                    .spawn(persistence::DB.save_workspace(serialized_workspace))
+                cx.spawn(|_, _| persistence::DB.save_workspace(serialized_workspace))
                     .detach();
             }
         }
@@ -3719,6 +3731,11 @@ impl Workspace {
 //         .log_err();
 // }
 
+impl EventEmitter for Workspace {
+    type Event = Event;
+}
+
+// todo!()
 // impl Entity for Workspace {
 //     type Event = Event;
 
@@ -3869,54 +3886,55 @@ impl Workspace {
 //     }
 // }
 
-// impl WorkspaceStore {
-//     pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
-//         Self {
-//             workspaces: Default::default(),
-//             followers: Default::default(),
-//             _subscriptions: vec![
-//                 client.add_request_handler(cx.handle(), Self::handle_follow),
-//                 client.add_message_handler(cx.handle(), Self::handle_unfollow),
-//                 client.add_message_handler(cx.handle(), Self::handle_update_followers),
-//             ],
-//             client,
-//         }
-//     }
+impl WorkspaceStore {
+    //     pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
+    //         Self {
+    //             workspaces: Default::default(),
+    //             followers: Default::default(),
+    //             _subscriptions: vec![
+    //                 client.add_request_handler(cx.handle(), Self::handle_follow),
+    //                 client.add_message_handler(cx.handle(), Self::handle_unfollow),
+    //                 client.add_message_handler(cx.handle(), Self::handle_update_followers),
+    //             ],
+    //             client,
+    //         }
+    //     }
 
-//     pub fn update_followers(
-//         &self,
-//         project_id: Option<u64>,
-//         update: proto::update_followers::Variant,
-//         cx: &AppContext,
-//     ) -> Option<()> {
-//         if !cx.has_global::<ModelHandle<ActiveCall>>() {
-//             return None;
-//         }
+    pub fn update_followers(
+        &self,
+        project_id: Option<u64>,
+        update: proto::update_followers::Variant,
+        cx: &AppContext,
+    ) -> Option<()> {
+        if !cx.has_global::<Model<ActiveCall>>() {
+            return None;
+        }
 
-//         let room_id = ActiveCall::global(cx).read(cx).room()?.read(cx).id();
-//         let follower_ids: Vec<_> = self
-//             .followers
-//             .iter()
-//             .filter_map(|follower| {
-//                 if follower.project_id == project_id || project_id.is_none() {
-//                     Some(follower.peer_id.into())
-//                 } else {
-//                     None
-//                 }
-//             })
-//             .collect();
-//         if follower_ids.is_empty() {
-//             return None;
-//         }
-//         self.client
-//             .send(proto::UpdateFollowers {
-//                 room_id,
-//                 project_id,
-//                 follower_ids,
-//                 variant: Some(update),
-//             })
-//             .log_err()
-//     }
+        let room_id = ActiveCall::global(cx).read(cx).room()?.read(cx).id();
+        let follower_ids: Vec<_> = self
+            .followers
+            .iter()
+            .filter_map(|follower| {
+                if follower.project_id == project_id || project_id.is_none() {
+                    Some(follower.peer_id.into())
+                } else {
+                    None
+                }
+            })
+            .collect();
+        if follower_ids.is_empty() {
+            return None;
+        }
+        self.client
+            .send(proto::UpdateFollowers {
+                room_id,
+                project_id,
+                follower_ids,
+                variant: Some(update),
+            })
+            .log_err()
+    }
+}
 
 //     async fn handle_follow(
 //         this: ModelHandle<Self>,
@@ -4303,13 +4321,14 @@ pub fn open_paths(
         .await;
 
         if let Some(existing) = existing {
-            Ok((
-                existing.clone(),
-                cx.update_window_root(&existing, |workspace, cx| {
-                    workspace.open_paths(abs_paths, true, cx)
-                })?
-                .await,
-            ))
+            // Ok((
+            //     existing.clone(),
+            //     cx.update_window_root(&existing, |workspace, cx| {
+            //         workspace.open_paths(abs_paths, true, cx)
+            //     })?
+            //     .await,
+            // ))
+            todo!()
         } else {
             todo!()
             // Ok(cx