Make `activate_workspace_for_project` compile

Antonio Scandurra , Mikayla Maki , and Kirill Bulatov created

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
Co-Authored-By: Kirill Bulatov <kirill@zed.dev>

Change summary

crates/gpui2/src/app.rs               |   65 
crates/gpui2/src/app/async_context.rs |   56 
crates/gpui2/src/app/test_context.rs  |    4 
crates/gpui2/src/view.rs              |   32 
crates/gpui2/src/window.rs            |   62 
crates/workspace2/src/item.rs         | 2160 ++++++++++++++--------------
crates/workspace2/src/pane.rs         |    5 
crates/workspace2/src/workspace2.rs   |  285 +-
8 files changed, 1,336 insertions(+), 1,333 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -13,11 +13,12 @@ use smallvec::SmallVec;
 pub use test_context::*;
 
 use crate::{
-    current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AppMetadata, AssetSource,
-    ClipboardItem, Context, DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId,
-    KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly, Pixels, Platform, Point,
-    SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
-    TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
+    current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle,
+    AppMetadata, AssetSource, ClipboardItem, Context, DispatchPhase, DisplayId, Executor,
+    FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly,
+    Pixels, Platform, Point, SharedString, SubscriberSet, Subscription, SvgRenderer, Task,
+    TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle,
+    WindowId,
 };
 use anyhow::{anyhow, Result};
 use collections::{HashMap, HashSet, VecDeque};
@@ -249,14 +250,21 @@ impl AppContext {
         result
     }
 
+    pub fn windows(&self) -> Vec<AnyWindowHandle> {
+        self.windows
+            .values()
+            .filter_map(|window| Some(window.as_ref()?.handle.clone()))
+            .collect()
+    }
+
     pub(crate) fn read_window<R>(
         &mut self,
-        id: WindowId,
+        handle: AnyWindowHandle,
         read: impl FnOnce(&WindowContext) -> R,
     ) -> Result<R> {
         let window = self
             .windows
-            .get(id)
+            .get(handle.id)
             .ok_or_else(|| anyhow!("window not found"))?
             .as_ref()
             .unwrap();
@@ -265,13 +273,13 @@ impl AppContext {
 
     pub(crate) fn update_window<R>(
         &mut self,
-        id: WindowId,
+        handle: AnyWindowHandle,
         update: impl FnOnce(&mut WindowContext) -> R,
     ) -> Result<R> {
         self.update(|cx| {
             let mut window = cx
                 .windows
-                .get_mut(id)
+                .get_mut(handle.id)
                 .ok_or_else(|| anyhow!("window not found"))?
                 .take()
                 .unwrap();
@@ -279,7 +287,7 @@ impl AppContext {
             let result = update(&mut WindowContext::mutable(cx, &mut window));
 
             cx.windows
-                .get_mut(id)
+                .get_mut(handle.id)
                 .ok_or_else(|| anyhow!("window not found"))?
                 .replace(window);
 
@@ -315,8 +323,11 @@ impl AppContext {
                         self.apply_notify_effect(emitter);
                     }
                     Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
-                    Effect::FocusChanged { window_id, focused } => {
-                        self.apply_focus_changed_effect(window_id, focused);
+                    Effect::FocusChanged {
+                        window_handle,
+                        focused,
+                    } => {
+                        self.apply_focus_changed_effect(window_handle, focused);
                     }
                     Effect::Refresh => {
                         self.apply_refresh_effect();
@@ -336,18 +347,19 @@ impl AppContext {
         let dirty_window_ids = self
             .windows
             .iter()
-            .filter_map(|(window_id, window)| {
+            .filter_map(|(_, window)| {
                 let window = window.as_ref().unwrap();
                 if window.dirty {
-                    Some(window_id)
+                    Some(window.handle.clone())
                 } else {
                     None
                 }
             })
             .collect::<SmallVec<[_; 8]>>();
 
-        for dirty_window_id in dirty_window_ids {
-            self.update_window(dirty_window_id, |cx| cx.draw()).unwrap();
+        for dirty_window_handle in dirty_window_ids {
+            self.update_window(dirty_window_handle, |cx| cx.draw())
+                .unwrap();
         }
     }
 
@@ -369,9 +381,8 @@ impl AppContext {
     }
 
     fn release_dropped_focus_handles(&mut self) {
-        let window_ids = self.windows.keys().collect::<SmallVec<[_; 8]>>();
-        for window_id in window_ids {
-            self.update_window(window_id, |cx| {
+        for window_handle in self.windows() {
+            self.update_window(window_handle, |cx| {
                 let mut blur_window = false;
                 let focus = cx.window.focus;
                 cx.window.focus_handles.write().retain(|handle_id, count| {
@@ -406,8 +417,12 @@ impl AppContext {
             .retain(&emitter, |handler| handler(&event, self));
     }
 
-    fn apply_focus_changed_effect(&mut self, window_id: WindowId, focused: Option<FocusId>) {
-        self.update_window(window_id, |cx| {
+    fn apply_focus_changed_effect(
+        &mut self,
+        window_handle: AnyWindowHandle,
+        focused: Option<FocusId>,
+    ) {
+        self.update_window(window_handle, |cx| {
             if cx.window.focus == focused {
                 let mut listeners = mem::take(&mut cx.window.focus_listeners);
                 let focused =
@@ -752,12 +767,12 @@ impl MainThread<AppContext> {
         })
     }
 
-    pub(crate) fn update_window<R>(
+    pub fn update_window<R>(
         &mut self,
-        id: WindowId,
+        handle: AnyWindowHandle,
         update: impl FnOnce(&mut MainThread<WindowContext>) -> R,
     ) -> Result<R> {
-        self.0.update_window(id, |cx| {
+        self.0.update_window(handle, |cx| {
             update(unsafe {
                 std::mem::transmute::<&mut WindowContext, &mut MainThread<WindowContext>>(cx)
             })
@@ -800,7 +815,7 @@ pub(crate) enum Effect {
         event: Box<dyn Any + Send + Sync + 'static>,
     },
     FocusChanged {
-        window_id: WindowId,
+        window_handle: AnyWindowHandle,
         focused: Option<FocusId>,
     },
     Refresh,

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

@@ -2,7 +2,7 @@ use crate::{
     AnyWindowHandle, AppContext, Context, Executor, Handle, MainThread, ModelContext, Result, Task,
     ViewContext, WindowContext,
 };
-use anyhow::anyhow;
+use anyhow::Context as _;
 use derive_more::{Deref, DerefMut};
 use parking_lot::Mutex;
 use std::{any::Any, future::Future, sync::Weak};
@@ -24,10 +24,7 @@ impl Context for AsyncAppContext {
     where
         T: Any + Send + Sync,
     {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let mut lock = app.lock(); // Need this to compile
         Ok(lock.entity(build_entity))
     }
@@ -37,10 +34,7 @@ impl Context for AsyncAppContext {
         handle: &Handle<T>,
         update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
     ) -> Self::Result<R> {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let mut lock = app.lock(); // Need this to compile
         Ok(lock.update_entity(handle, update))
     }
@@ -48,10 +42,7 @@ impl Context for AsyncAppContext {
 
 impl AsyncAppContext {
     pub fn refresh(&mut self) -> Result<()> {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let mut lock = app.lock(); // Need this to compile
         lock.refresh();
         Ok(())
@@ -62,10 +53,7 @@ impl AsyncAppContext {
     }
 
     pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let mut lock = app.lock();
         Ok(f(&mut *lock))
     }
@@ -75,12 +63,9 @@ impl AsyncAppContext {
         handle: AnyWindowHandle,
         update: impl FnOnce(&WindowContext) -> R,
     ) -> Result<R> {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let mut app_context = app.lock();
-        app_context.read_window(handle.id, update)
+        app_context.read_window(handle, update)
     }
 
     pub fn update_window<R>(
@@ -88,12 +73,9 @@ impl AsyncAppContext {
         handle: AnyWindowHandle,
         update: impl FnOnce(&mut WindowContext) -> R,
     ) -> Result<R> {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let mut app_context = app.lock();
-        app_context.update_window(handle.id, update)
+        app_context.update_window(handle, update)
     }
 
     pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
@@ -124,28 +106,19 @@ impl AsyncAppContext {
     where
         R: Send + 'static,
     {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let mut app_context = app.lock();
         Ok(app_context.run_on_main(f))
     }
 
     pub fn has_global<G: 'static>(&self) -> Result<bool> {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let lock = app.lock(); // Need this to compile
         Ok(lock.has_global::<G>())
     }
 
     pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let lock = app.lock(); // Need this to compile
         Ok(read(lock.global(), &lock))
     }
@@ -163,10 +136,7 @@ impl AsyncAppContext {
         &mut self,
         update: impl FnOnce(&mut G, &mut AppContext) -> R,
     ) -> Result<R> {
-        let app = self
-            .app
-            .upgrade()
-            .ok_or_else(|| anyhow!("app was released"))?;
+        let app = self.app.upgrade().context("app was released")?;
         let mut lock = app.lock(); // Need this to compile
         Ok(lock.update_global(update))
     }

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

@@ -73,7 +73,7 @@ impl TestAppContext {
         read: impl FnOnce(&WindowContext) -> R,
     ) -> R {
         let mut app_context = self.app.lock();
-        app_context.read_window(handle.id, read).unwrap()
+        app_context.read_window(handle, read).unwrap()
     }
 
     pub fn update_window<R>(
@@ -82,7 +82,7 @@ impl TestAppContext {
         update: impl FnOnce(&mut WindowContext) -> R,
     ) -> R {
         let mut app = self.app.lock();
-        app.update_window(handle.id, update).unwrap()
+        app.update_window(handle, update).unwrap()
     }
 
     pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>

crates/gpui2/src/view.rs 🔗

@@ -1,8 +1,8 @@
 use parking_lot::Mutex;
 
 use crate::{
-    AnyBox, AnyElement, BorrowWindow, Bounds, Element, ElementId, EntityId, Handle, IntoAnyElement,
-    LayoutId, Pixels, ViewContext, WindowContext,
+    AnyBox, AnyElement, AnyHandle, BorrowWindow, Bounds, Element, ElementId, Handle,
+    IntoAnyElement, LayoutId, Pixels, ViewContext, WindowContext,
 };
 use std::{marker::PhantomData, sync::Arc};
 
@@ -54,7 +54,7 @@ impl<V: 'static> Element for View<V> {
     type ViewState = ();
     type ElementState = AnyElement<V>;
 
-    fn id(&self) -> Option<crate::ElementId> {
+    fn id(&self) -> Option<ElementId> {
         Some(ElementId::View(self.state.entity_id))
     }
 
@@ -109,7 +109,7 @@ impl<V: 'static, ParentV: 'static> Element for EraseViewState<V, ParentV> {
     type ViewState = ParentV;
     type ElementState = AnyBox;
 
-    fn id(&self) -> Option<crate::ElementId> {
+    fn id(&self) -> Option<ElementId> {
         Element::id(&self.view)
     }
 
@@ -143,19 +143,19 @@ impl<V: 'static, ParentV: 'static> Element for EraseViewState<V, ParentV> {
 }
 
 trait ViewObject: Send + Sync {
-    fn entity_id(&self) -> EntityId;
+    fn entity_handle(&self) -> &AnyHandle;
     fn initialize(&mut self, cx: &mut WindowContext) -> AnyBox;
     fn layout(&mut self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId;
     fn paint(&mut self, bounds: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext);
 }
 
 impl<V: 'static> ViewObject for View<V> {
-    fn entity_id(&self) -> EntityId {
-        self.state.entity_id
+    fn entity_handle(&self) -> &AnyHandle {
+        &self.state
     }
 
     fn initialize(&mut self, cx: &mut WindowContext) -> AnyBox {
-        cx.with_element_id(self.entity_id(), |_global_id, cx| {
+        cx.with_element_id(self.state.entity_id, |_global_id, cx| {
             self.state.update(cx, |state, cx| {
                 let mut any_element = Box::new((self.render)(state, cx));
                 any_element.initialize(state, cx);
@@ -165,7 +165,7 @@ impl<V: 'static> ViewObject for View<V> {
     }
 
     fn layout(&mut self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId {
-        cx.with_element_id(self.entity_id(), |_global_id, cx| {
+        cx.with_element_id(self.state.entity_id, |_global_id, cx| {
             self.state.update(cx, |state, cx| {
                 let element = element.downcast_mut::<AnyElement<V>>().unwrap();
                 element.layout(state, cx)
@@ -174,7 +174,7 @@ impl<V: 'static> ViewObject for View<V> {
     }
 
     fn paint(&mut self, _: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext) {
-        cx.with_element_id(self.entity_id(), |_global_id, cx| {
+        cx.with_element_id(self.state.entity_id, |_global_id, cx| {
             self.state.update(cx, |state, cx| {
                 let element = element.downcast_mut::<AnyElement<V>>().unwrap();
                 element.paint(state, cx);
@@ -187,6 +187,12 @@ pub struct AnyView {
     view: Arc<Mutex<dyn ViewObject>>,
 }
 
+impl AnyView {
+    pub fn entity_handle(&self) -> AnyHandle {
+        self.view.lock().entity_handle().clone()
+    }
+}
+
 impl<ParentV: 'static> IntoAnyElement<ParentV> for AnyView {
     fn into_any(self) -> AnyElement<ParentV> {
         AnyElement::new(EraseAnyViewState {
@@ -200,8 +206,8 @@ impl Element for AnyView {
     type ViewState = ();
     type ElementState = AnyBox;
 
-    fn id(&self) -> Option<crate::ElementId> {
-        Some(ElementId::View(self.view.lock().entity_id()))
+    fn id(&self) -> Option<ElementId> {
+        Some(ElementId::View(self.view.lock().entity_handle().entity_id))
     }
 
     fn initialize(
@@ -251,7 +257,7 @@ impl<ParentV: 'static> Element for EraseAnyViewState<ParentV> {
     type ViewState = ParentV;
     type ElementState = AnyBox;
 
-    fn id(&self) -> Option<crate::ElementId> {
+    fn id(&self) -> Option<ElementId> {
         Element::id(&self.view)
     }
 

crates/gpui2/src/window.rs 🔗

@@ -1,14 +1,14 @@
 use crate::{
-    px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
-    Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect,
-    Element, EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent, FontId,
-    GlobalElementId, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
-    KeyMatcher, Keystroke, LayoutId, MainThread, MainThreadOnly, Modifiers, MonochromeSprite,
-    MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
-    PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams,
-    RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription,
-    TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, WindowOptions,
-    SUBPIXEL_VARIANTS,
+    px, size, Action, AnyBox, AnyDrag, AnyHandle, AnyView, AppContext, AsyncWindowContext,
+    AvailableSpace, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId,
+    Edges, Effect, Element, EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent,
+    FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener,
+    KeyMatch, KeyMatcher, Keystroke, LayoutId, MainThread, MainThreadOnly, Modifiers,
+    MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
+    PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
+    RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
+    Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle,
+    WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
 use collections::HashMap;
@@ -145,7 +145,7 @@ impl Drop for FocusHandle {
 }
 
 pub struct Window {
-    handle: AnyWindowHandle,
+    pub(crate) handle: AnyWindowHandle,
     platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
     display_id: DisplayId,
     sprite_atlas: Arc<dyn PlatformAtlas>,
@@ -305,6 +305,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.handle
     }
 
+    pub fn root_view(&self) -> Option<AnyHandle> {
+        Some(self.window.root_view.as_ref()?.entity_handle())
+    }
+
     pub fn notify(&mut self) {
         self.window.dirty = true;
     }
@@ -324,10 +328,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             self.window.last_blur = Some(self.window.focus);
         }
 
-        let window_id = self.window.handle.id;
         self.window.focus = Some(handle.id);
         self.app.push_effect(Effect::FocusChanged {
-            window_id,
+            window_handle: self.window.handle,
             focused: Some(handle.id),
         });
         self.notify();
@@ -338,10 +341,9 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             self.window.last_blur = Some(self.window.focus);
         }
 
-        let window_id = self.window.handle.id;
         self.window.focus = None;
         self.app.push_effect(Effect::FocusChanged {
-            window_id,
+            window_handle: self.window.handle,
             focused: None,
         });
         self.notify();
@@ -359,8 +361,8 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 mem::transmute::<&mut Self, &mut MainThread<Self>>(self)
             })))
         } else {
-            let id = self.window.handle.id;
-            self.app.run_on_main(move |cx| cx.update_window(id, f))
+            let handle = self.window.handle;
+            self.app.run_on_main(move |cx| cx.update_window(handle, f))
         }
     }
 
@@ -1076,10 +1078,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         &mut self,
         f: impl Fn(&mut WindowContext<'_, '_>) + Send + Sync + 'static,
     ) -> Subscription {
-        let window_id = self.window.handle.id;
+        let window_handle = self.window.handle;
         self.global_observers.insert(
             TypeId::of::<G>(),
-            Box::new(move |cx| cx.update_window(window_id, |cx| f(cx)).is_ok()),
+            Box::new(move |cx| cx.update_window(window_handle, |cx| f(cx)).is_ok()),
         )
     }
 
@@ -1162,6 +1164,16 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     }
 }
 
+impl<'a, 'w> MainThread<WindowContext<'a, 'w>> {
+    fn platform_window(&self) -> &dyn PlatformWindow {
+        self.window.platform_window.borrow_on_main_thread().as_ref()
+    }
+
+    pub fn activate_window(&self) {
+        self.platform_window().activate();
+    }
+}
+
 impl Context for WindowContext<'_, '_> {
     type EntityContext<'a, 'w, T> = ViewContext<'a, 'w, T>;
     type Result<T> = T;
@@ -1457,7 +1469,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
         self.app.observers.insert(
             handle.entity_id,
             Box::new(move |cx| {
-                cx.update_window(window_handle.id, |cx| {
+                cx.update_window(window_handle, |cx| {
                     if let Some(handle) = handle.upgrade() {
                         this.update(cx, |this, cx| on_notify(this, handle, cx))
                             .is_ok()
@@ -1484,7 +1496,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
         self.app.event_listeners.insert(
             handle.entity_id,
             Box::new(move |event, cx| {
-                cx.update_window(window_handle.id, |cx| {
+                cx.update_window(window_handle, |cx| {
                     if let Some(handle) = handle.upgrade() {
                         let event = event.downcast_ref().expect("invalid event type");
                         this.update(cx, |this, cx| on_event(this, handle, event, cx))
@@ -1508,7 +1520,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
             Box::new(move |this, cx| {
                 let this = this.downcast_mut().expect("invalid entity type");
                 // todo!("are we okay with silently swallowing the error?")
-                let _ = cx.update_window(window_handle.id, |cx| on_release(this, cx));
+                let _ = cx.update_window(window_handle, |cx| on_release(this, cx));
             }),
         )
     }
@@ -1521,7 +1533,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
         let this = self.handle();
         let window_handle = self.window.handle;
         self.app.observe_release(handle, move |entity, cx| {
-            let _ = cx.update_window(window_handle.id, |cx| {
+            let _ = cx.update_window(window_handle, |cx| {
                 this.update(cx, |this, cx| on_release(this, entity, cx))
             });
         })
@@ -1678,12 +1690,12 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
         &mut self,
         f: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) + Send + Sync + 'static,
     ) -> Subscription {
-        let window_id = self.window.handle.id;
+        let window_handle = self.window.handle;
         let handle = self.handle();
         self.global_observers.insert(
             TypeId::of::<G>(),
             Box::new(move |cx| {
-                cx.update_window(window_id, |cx| {
+                cx.update_window(window_handle, |cx| {
                     handle.update(cx, |view, cx| f(view, cx)).is_ok()
                 })
                 .unwrap_or(false)

crates/workspace2/src/item.rs 🔗

@@ -1,1081 +1,1083 @@
-use crate::{
-    pane, persistence::model::ItemId, searchable::SearchableItemHandle, FollowableItemBuilders,
-    ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
-};
-use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
-use anyhow::Result;
-use client2::{
-    proto::{self, PeerId},
-    Client,
-};
-use gpui2::geometry::vector::Vector2F;
-use gpui2::AnyWindowHandle;
-use gpui2::{
-    fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View,
-    ViewContext, ViewHandle, WeakViewHandle, WindowContext,
-};
-use project2::{Project, ProjectEntryId, ProjectPath};
-use schemars::JsonSchema;
-use serde_derive::{Deserialize, Serialize};
-use settings2::Setting;
-use smallvec::SmallVec;
-use std::{
-    any::{Any, TypeId},
-    borrow::Cow,
-    cell::RefCell,
-    fmt,
-    ops::Range,
-    path::PathBuf,
-    rc::Rc,
-    sync::{
-        atomic::{AtomicBool, Ordering},
-        Arc,
-    },
-    time::Duration,
-};
-use theme2::Theme;
-
-#[derive(Deserialize)]
-pub struct ItemSettings {
-    pub git_status: bool,
-    pub close_position: ClosePosition,
+// use crate::{
+//     pane, persistence::model::ItemId, searchable::SearchableItemHandle, FollowableItemBuilders,
+//     ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId,
+// };
+// use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
+// use anyhow::Result;
+// use client2::{
+//     proto::{self, PeerId},
+//     Client,
+// };
+// use gpui2::geometry::vector::Vector2F;
+// use gpui2::AnyWindowHandle;
+// use gpui2::{
+//     fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View,
+//     ViewContext, ViewHandle, WeakViewHandle, WindowContext,
+// };
+// use project2::{Project, ProjectEntryId, ProjectPath};
+// use schemars::JsonSchema;
+// use serde_derive::{Deserialize, Serialize};
+// use settings2::Setting;
+// use smallvec::SmallVec;
+// use std::{
+//     any::{Any, TypeId},
+//     borrow::Cow,
+//     cell::RefCell,
+//     fmt,
+//     ops::Range,
+//     path::PathBuf,
+//     rc::Rc,
+//     sync::{
+//         atomic::{AtomicBool, Ordering},
+//         Arc,
+//     },
+//     time::Duration,
+// };
+// use theme2::Theme;
+
+// #[derive(Deserialize)]
+// pub struct ItemSettings {
+//     pub git_status: bool,
+//     pub close_position: ClosePosition,
+// }
+
+// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
+// #[serde(rename_all = "lowercase")]
+// pub enum ClosePosition {
+//     Left,
+//     #[default]
+//     Right,
+// }
+
+// impl ClosePosition {
+//     pub fn right(&self) -> bool {
+//         match self {
+//             ClosePosition::Left => false,
+//             ClosePosition::Right => true,
+//         }
+//     }
+// }
+
+// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
+// pub struct ItemSettingsContent {
+//     git_status: Option<bool>,
+//     close_position: Option<ClosePosition>,
+// }
+
+// impl Setting for ItemSettings {
+//     const KEY: Option<&'static str> = Some("tabs");
+
+//     type FileContent = ItemSettingsContent;
+
+//     fn load(
+//         default_value: &Self::FileContent,
+//         user_values: &[&Self::FileContent],
+//         _: &gpui2::AppContext,
+//     ) -> anyhow::Result<Self> {
+//         Self::load_via_json_merge(default_value, user_values)
+//     }
+// }
+
+// #[derive(Eq, PartialEq, Hash, Debug)]
+// pub enum ItemEvent {
+//     CloseItem,
+//     UpdateTab,
+//     UpdateBreadcrumbs,
+//     Edit,
+// }
+
+// // TODO: Combine this with existing HighlightedText struct?
+// pub struct BreadcrumbText {
+//     pub text: String,
+//     pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
+// }
+
+// pub trait Item: View {
+//     fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
+//     fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
+//     fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
+//         false
+//     }
+//     fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
+//         None
+//     }
+//     fn tab_description<'a>(&'a self, _: usize, _: &'a AppContext) -> Option<Cow<str>> {
+//         None
+//     }
+//     fn tab_content<V: 'static>(
+//         &self,
+//         detail: Option<usize>,
+//         style: &theme2::Tab,
+//         cx: &AppContext,
+//     ) -> AnyElement<V>;
+//     fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item)) {
+//     } // (model id, Item)
+//     fn is_singleton(&self, _cx: &AppContext) -> bool {
+//         false
+//     }
+//     fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>) {}
+//     fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext<Self>) -> Option<Self>
+//     where
+//         Self: Sized,
+//     {
+//         None
+//     }
+//     fn is_dirty(&self, _: &AppContext) -> bool {
+//         false
+//     }
+//     fn has_conflict(&self, _: &AppContext) -> bool {
+//         false
+//     }
+//     fn can_save(&self, _cx: &AppContext) -> bool {
+//         false
+//     }
+//     fn save(
+//         &mut self,
+//         _project: ModelHandle<Project>,
+//         _cx: &mut ViewContext<Self>,
+//     ) -> Task<Result<()>> {
+//         unimplemented!("save() must be implemented if can_save() returns true")
+//     }
+//     fn save_as(
+//         &mut self,
+//         _project: ModelHandle<Project>,
+//         _abs_path: PathBuf,
+//         _cx: &mut ViewContext<Self>,
+//     ) -> Task<Result<()>> {
+//         unimplemented!("save_as() must be implemented if can_save() returns true")
+//     }
+//     fn reload(
+//         &mut self,
+//         _project: ModelHandle<Project>,
+//         _cx: &mut ViewContext<Self>,
+//     ) -> Task<Result<()>> {
+//         unimplemented!("reload() must be implemented if can_save() returns true")
+//     }
+//     fn to_item_events(_event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
+//         SmallVec::new()
+//     }
+//     fn should_close_item_on_event(_: &Self::Event) -> bool {
+//         false
+//     }
+//     fn should_update_tab_on_event(_: &Self::Event) -> bool {
+//         false
+//     }
+
+//     fn act_as_type<'a>(
+//         &'a self,
+//         type_id: TypeId,
+//         self_handle: &'a ViewHandle<Self>,
+//         _: &'a AppContext,
+//     ) -> Option<&AnyViewHandle> {
+//         if TypeId::of::<Self>() == type_id {
+//             Some(self_handle)
+//         } else {
+//             None
+//         }
+//     }
+
+//     fn as_searchable(&self, _: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+//         None
+//     }
+
+//     fn breadcrumb_location(&self) -> ToolbarItemLocation {
+//         ToolbarItemLocation::Hidden
+//     }
+
+//     fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+//         None
+//     }
+
+//     fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext<Self>) {}
+
+//     fn serialized_item_kind() -> Option<&'static str> {
+//         None
+//     }
+
+//     fn deserialize(
+//         _project: ModelHandle<Project>,
+//         _workspace: WeakViewHandle<Workspace>,
+//         _workspace_id: WorkspaceId,
+//         _item_id: ItemId,
+//         _cx: &mut ViewContext<Pane>,
+//     ) -> Task<Result<ViewHandle<Self>>> {
+//         unimplemented!(
+//             "deserialize() must be implemented if serialized_item_kind() returns Some(_)"
+//         )
+//     }
+//     fn show_toolbar(&self) -> bool {
+//         true
+//     }
+//     fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Vector2F> {
+//         None
+//     }
+// }
+
+use core::fmt;
+
+pub trait ItemHandle: 'static + fmt::Debug + Send + Sync {
+    //     fn subscribe_to_item_events(
+    //         &self,
+    //         cx: &mut WindowContext,
+    //         handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
+    //     ) -> gpui2::Subscription;
+    //     fn tab_tooltip_text<'a>(&self, cx: &'a AppContext) -> Option<Cow<'a, str>>;
+    //     fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>>;
+    //     fn tab_content(
+    //         &self,
+    //         detail: Option<usize>,
+    //         style: &theme2::Tab,
+    //         cx: &AppContext,
+    //     ) -> AnyElement<Pane>;
+    //     fn dragged_tab_content(
+    //         &self,
+    //         detail: Option<usize>,
+    //         style: &theme2::Tab,
+    //         cx: &AppContext,
+    //     ) -> AnyElement<Workspace>;
+    //     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
+    //     fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
+    //     fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]>;
+    //     fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item));
+    //     fn is_singleton(&self, cx: &AppContext) -> bool;
+    //     fn boxed_clone(&self) -> Box<dyn ItemHandle>;
+    //     fn clone_on_split(
+    //         &self,
+    //         workspace_id: WorkspaceId,
+    //         cx: &mut WindowContext,
+    //     ) -> Option<Box<dyn ItemHandle>>;
+    //     fn added_to_pane(
+    //         &self,
+    //         workspace: &mut Workspace,
+    //         pane: ViewHandle<Pane>,
+    //         cx: &mut ViewContext<Workspace>,
+    //     );
+    //     fn deactivated(&self, cx: &mut WindowContext);
+    //     fn workspace_deactivated(&self, cx: &mut WindowContext);
+    //     fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
+    //     fn id(&self) -> usize;
+    //     fn window(&self) -> AnyWindowHandle;
+    //     fn as_any(&self) -> &AnyViewHandle;
+    //     fn is_dirty(&self, cx: &AppContext) -> bool;
+    //     fn has_conflict(&self, cx: &AppContext) -> bool;
+    //     fn can_save(&self, cx: &AppContext) -> bool;
+    //     fn save(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
+    //     fn save_as(
+    //         &self,
+    //         project: ModelHandle<Project>,
+    //         abs_path: PathBuf,
+    //         cx: &mut WindowContext,
+    //     ) -> Task<Result<()>>;
+    //     fn reload(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
+    //     fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle>;
+    //     fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
+    //     fn on_release(
+    //         &self,
+    //         cx: &mut AppContext,
+    //         callback: Box<dyn FnOnce(&mut AppContext)>,
+    //     ) -> gpui2::Subscription;
+    //     fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
+    //     fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation;
+    //     fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>>;
+    //     fn serialized_item_kind(&self) -> Option<&'static str>;
+    //     fn show_toolbar(&self, cx: &AppContext) -> bool;
+    //     fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F>;
 }
 
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
-#[serde(rename_all = "lowercase")]
-pub enum ClosePosition {
-    Left,
-    #[default]
-    Right,
-}
-
-impl ClosePosition {
-    pub fn right(&self) -> bool {
-        match self {
-            ClosePosition::Left => false,
-            ClosePosition::Right => true,
-        }
-    }
-}
-
-#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
-pub struct ItemSettingsContent {
-    git_status: Option<bool>,
-    close_position: Option<ClosePosition>,
-}
-
-impl Setting for ItemSettings {
-    const KEY: Option<&'static str> = Some("tabs");
-
-    type FileContent = ItemSettingsContent;
-
-    fn load(
-        default_value: &Self::FileContent,
-        user_values: &[&Self::FileContent],
-        _: &gpui2::AppContext,
-    ) -> anyhow::Result<Self> {
-        Self::load_via_json_merge(default_value, user_values)
-    }
-}
-
-#[derive(Eq, PartialEq, Hash, Debug)]
-pub enum ItemEvent {
-    CloseItem,
-    UpdateTab,
-    UpdateBreadcrumbs,
-    Edit,
-}
-
-// TODO: Combine this with existing HighlightedText struct?
-pub struct BreadcrumbText {
-    pub text: String,
-    pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
-}
-
-pub trait Item: View {
-    fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
-    fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
-    fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
-        false
-    }
-    fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
-        None
-    }
-    fn tab_description<'a>(&'a self, _: usize, _: &'a AppContext) -> Option<Cow<str>> {
-        None
-    }
-    fn tab_content<V: 'static>(
-        &self,
-        detail: Option<usize>,
-        style: &theme2::Tab,
-        cx: &AppContext,
-    ) -> AnyElement<V>;
-    fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item)) {
-    } // (model id, Item)
-    fn is_singleton(&self, _cx: &AppContext) -> bool {
-        false
-    }
-    fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>) {}
-    fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext<Self>) -> Option<Self>
-    where
-        Self: Sized,
-    {
-        None
-    }
-    fn is_dirty(&self, _: &AppContext) -> bool {
-        false
-    }
-    fn has_conflict(&self, _: &AppContext) -> bool {
-        false
-    }
-    fn can_save(&self, _cx: &AppContext) -> bool {
-        false
-    }
-    fn save(
-        &mut self,
-        _project: ModelHandle<Project>,
-        _cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        unimplemented!("save() must be implemented if can_save() returns true")
-    }
-    fn save_as(
-        &mut self,
-        _project: ModelHandle<Project>,
-        _abs_path: PathBuf,
-        _cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        unimplemented!("save_as() must be implemented if can_save() returns true")
-    }
-    fn reload(
-        &mut self,
-        _project: ModelHandle<Project>,
-        _cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>> {
-        unimplemented!("reload() must be implemented if can_save() returns true")
-    }
-    fn to_item_events(_event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
-        SmallVec::new()
-    }
-    fn should_close_item_on_event(_: &Self::Event) -> bool {
-        false
-    }
-    fn should_update_tab_on_event(_: &Self::Event) -> bool {
-        false
-    }
-
-    fn act_as_type<'a>(
-        &'a self,
-        type_id: TypeId,
-        self_handle: &'a ViewHandle<Self>,
-        _: &'a AppContext,
-    ) -> Option<&AnyViewHandle> {
-        if TypeId::of::<Self>() == type_id {
-            Some(self_handle)
-        } else {
-            None
-        }
-    }
-
-    fn as_searchable(&self, _: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
-        None
-    }
-
-    fn breadcrumb_location(&self) -> ToolbarItemLocation {
-        ToolbarItemLocation::Hidden
-    }
-
-    fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
-        None
-    }
-
-    fn added_to_workspace(&mut self, _workspace: &mut Workspace, _cx: &mut ViewContext<Self>) {}
-
-    fn serialized_item_kind() -> Option<&'static str> {
-        None
-    }
-
-    fn deserialize(
-        _project: ModelHandle<Project>,
-        _workspace: WeakViewHandle<Workspace>,
-        _workspace_id: WorkspaceId,
-        _item_id: ItemId,
-        _cx: &mut ViewContext<Pane>,
-    ) -> Task<Result<ViewHandle<Self>>> {
-        unimplemented!(
-            "deserialize() must be implemented if serialized_item_kind() returns Some(_)"
-        )
-    }
-    fn show_toolbar(&self) -> bool {
-        true
-    }
-    fn pixel_position_of_cursor(&self, _: &AppContext) -> Option<Vector2F> {
-        None
-    }
-}
-
-pub trait ItemHandle: 'static + fmt::Debug {
-    fn subscribe_to_item_events(
-        &self,
-        cx: &mut WindowContext,
-        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
-    ) -> gpui2::Subscription;
-    fn tab_tooltip_text<'a>(&self, cx: &'a AppContext) -> Option<Cow<'a, str>>;
-    fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>>;
-    fn tab_content(
-        &self,
-        detail: Option<usize>,
-        style: &theme2::Tab,
-        cx: &AppContext,
-    ) -> AnyElement<Pane>;
-    fn dragged_tab_content(
-        &self,
-        detail: Option<usize>,
-        style: &theme2::Tab,
-        cx: &AppContext,
-    ) -> AnyElement<Workspace>;
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
-    fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]>;
-    fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]>;
-    fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item));
-    fn is_singleton(&self, cx: &AppContext) -> bool;
-    fn boxed_clone(&self) -> Box<dyn ItemHandle>;
-    fn clone_on_split(
-        &self,
-        workspace_id: WorkspaceId,
-        cx: &mut WindowContext,
-    ) -> Option<Box<dyn ItemHandle>>;
-    fn added_to_pane(
-        &self,
-        workspace: &mut Workspace,
-        pane: ViewHandle<Pane>,
-        cx: &mut ViewContext<Workspace>,
-    );
-    fn deactivated(&self, cx: &mut WindowContext);
-    fn workspace_deactivated(&self, cx: &mut WindowContext);
-    fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
-    fn id(&self) -> usize;
-    fn window(&self) -> AnyWindowHandle;
-    fn as_any(&self) -> &AnyViewHandle;
-    fn is_dirty(&self, cx: &AppContext) -> bool;
-    fn has_conflict(&self, cx: &AppContext) -> bool;
-    fn can_save(&self, cx: &AppContext) -> bool;
-    fn save(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
-    fn save_as(
-        &self,
-        project: ModelHandle<Project>,
-        abs_path: PathBuf,
-        cx: &mut WindowContext,
-    ) -> Task<Result<()>>;
-    fn reload(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>>;
-    fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle>;
-    fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>>;
-    fn on_release(
-        &self,
-        cx: &mut AppContext,
-        callback: Box<dyn FnOnce(&mut AppContext)>,
-    ) -> gpui2::Subscription;
-    fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
-    fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation;
-    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>>;
-    fn serialized_item_kind(&self) -> Option<&'static str>;
-    fn show_toolbar(&self, cx: &AppContext) -> bool;
-    fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F>;
-}
-
-pub trait WeakItemHandle {
-    fn id(&self) -> usize;
-    fn window(&self) -> AnyWindowHandle;
-    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>>;
-}
-
-impl dyn ItemHandle {
-    pub fn downcast<T: View>(&self) -> Option<ViewHandle<T>> {
-        self.as_any().clone().downcast()
-    }
-
-    pub fn act_as<T: View>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
-        self.act_as_type(TypeId::of::<T>(), cx)
-            .and_then(|t| t.clone().downcast())
-    }
-}
-
-impl<T: Item> ItemHandle for ViewHandle<T> {
-    fn subscribe_to_item_events(
-        &self,
-        cx: &mut WindowContext,
-        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
-    ) -> gpui2::Subscription {
-        cx.subscribe(self, move |_, event, cx| {
-            for item_event in T::to_item_events(event) {
-                handler(item_event, cx)
-            }
-        })
-    }
-
-    fn tab_tooltip_text<'a>(&self, cx: &'a AppContext) -> Option<Cow<'a, str>> {
-        self.read(cx).tab_tooltip_text(cx)
-    }
-
-    fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>> {
-        self.read(cx).tab_description(detail, cx)
-    }
-
-    fn tab_content(
-        &self,
-        detail: Option<usize>,
-        style: &theme2::Tab,
-        cx: &AppContext,
-    ) -> AnyElement<Pane> {
-        self.read(cx).tab_content(detail, style, cx)
-    }
-
-    fn dragged_tab_content(
-        &self,
-        detail: Option<usize>,
-        style: &theme2::Tab,
-        cx: &AppContext,
-    ) -> AnyElement<Workspace> {
-        self.read(cx).tab_content(detail, style, cx)
-    }
-
-    fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
-        let this = self.read(cx);
-        let mut result = None;
-        if this.is_singleton(cx) {
-            this.for_each_project_item(cx, &mut |_, item| {
-                result = item.project_path(cx);
-            });
-        }
-        result
-    }
-
-    fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]> {
-        let mut result = SmallVec::new();
-        self.read(cx).for_each_project_item(cx, &mut |_, item| {
-            if let Some(id) = item.entry_id(cx) {
-                result.push(id);
-            }
-        });
-        result
-    }
-
-    fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]> {
-        let mut result = SmallVec::new();
-        self.read(cx).for_each_project_item(cx, &mut |id, _| {
-            result.push(id);
-        });
-        result
-    }
-
-    fn for_each_project_item(
-        &self,
-        cx: &AppContext,
-        f: &mut dyn FnMut(usize, &dyn project2::Item),
-    ) {
-        self.read(cx).for_each_project_item(cx, f)
-    }
-
-    fn is_singleton(&self, cx: &AppContext) -> bool {
-        self.read(cx).is_singleton(cx)
-    }
-
-    fn boxed_clone(&self) -> Box<dyn ItemHandle> {
-        Box::new(self.clone())
-    }
-
-    fn clone_on_split(
-        &self,
-        workspace_id: WorkspaceId,
-        cx: &mut WindowContext,
-    ) -> Option<Box<dyn ItemHandle>> {
-        self.update(cx, |item, cx| {
-            cx.add_option_view(|cx| item.clone_on_split(workspace_id, cx))
-        })
-        .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
-    }
-
-    fn added_to_pane(
-        &self,
-        workspace: &mut Workspace,
-        pane: ViewHandle<Pane>,
-        cx: &mut ViewContext<Workspace>,
-    ) {
-        let history = pane.read(cx).nav_history_for_item(self);
-        self.update(cx, |this, cx| {
-            this.set_nav_history(history, cx);
-            this.added_to_workspace(workspace, cx);
-        });
-
-        if let Some(followed_item) = self.to_followable_item_handle(cx) {
-            if let Some(message) = followed_item.to_state_proto(cx) {
-                workspace.update_followers(
-                    followed_item.is_project_item(cx),
-                    proto::update_followers::Variant::CreateView(proto::View {
-                        id: followed_item
-                            .remote_id(&workspace.app_state.client, cx)
-                            .map(|id| id.to_proto()),
-                        variant: Some(message),
-                        leader_id: workspace.leader_for_pane(&pane),
-                    }),
-                    cx,
-                );
-            }
-        }
-
-        if workspace
-            .panes_by_item
-            .insert(self.id(), pane.downgrade())
-            .is_none()
-        {
-            let mut pending_autosave = DelayedDebouncedEditAction::new();
-            let pending_update = Rc::new(RefCell::new(None));
-            let pending_update_scheduled = Rc::new(AtomicBool::new(false));
-
-            let mut event_subscription =
-                Some(cx.subscribe(self, move |workspace, item, event, cx| {
-                    let pane = if let Some(pane) = workspace
-                        .panes_by_item
-                        .get(&item.id())
-                        .and_then(|pane| pane.upgrade(cx))
-                    {
-                        pane
-                    } else {
-                        log::error!("unexpected item event after pane was dropped");
-                        return;
-                    };
-
-                    if let Some(item) = item.to_followable_item_handle(cx) {
-                        let is_project_item = item.is_project_item(cx);
-                        let leader_id = workspace.leader_for_pane(&pane);
-
-                        if leader_id.is_some() && item.should_unfollow_on_event(event, cx) {
-                            workspace.unfollow(&pane, cx);
-                        }
-
-                        if item.add_event_to_update_proto(
-                            event,
-                            &mut *pending_update.borrow_mut(),
-                            cx,
-                        ) && !pending_update_scheduled.load(Ordering::SeqCst)
-                        {
-                            pending_update_scheduled.store(true, Ordering::SeqCst);
-                            cx.after_window_update({
-                                let pending_update = pending_update.clone();
-                                let pending_update_scheduled = pending_update_scheduled.clone();
-                                move |this, cx| {
-                                    pending_update_scheduled.store(false, Ordering::SeqCst);
-                                    this.update_followers(
-                                        is_project_item,
-                                        proto::update_followers::Variant::UpdateView(
-                                            proto::UpdateView {
-                                                id: item
-                                                    .remote_id(&this.app_state.client, cx)
-                                                    .map(|id| id.to_proto()),
-                                                variant: pending_update.borrow_mut().take(),
-                                                leader_id,
-                                            },
-                                        ),
-                                        cx,
-                                    );
-                                }
-                            });
-                        }
-                    }
-
-                    for item_event in T::to_item_events(event).into_iter() {
-                        match item_event {
-                            ItemEvent::CloseItem => {
-                                pane.update(cx, |pane, cx| {
-                                    pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx)
-                                })
-                                .detach_and_log_err(cx);
-                                return;
-                            }
-
-                            ItemEvent::UpdateTab => {
-                                pane.update(cx, |_, cx| {
-                                    cx.emit(pane::Event::ChangeItemTitle);
-                                    cx.notify();
-                                });
-                            }
-
-                            ItemEvent::Edit => {
-                                let autosave = settings2::get::<WorkspaceSettings>(cx).autosave;
-                                if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
-                                    let delay = Duration::from_millis(milliseconds);
-                                    let item = item.clone();
-                                    pending_autosave.fire_new(delay, cx, move |workspace, cx| {
-                                        Pane::autosave_item(&item, workspace.project().clone(), cx)
-                                    });
-                                }
-                            }
-
-                            _ => {}
-                        }
-                    }
-                }));
-
-            cx.observe_focus(self, move |workspace, item, focused, cx| {
-                if !focused
-                    && settings2::get::<WorkspaceSettings>(cx).autosave
-                        == AutosaveSetting::OnFocusChange
-                {
-                    Pane::autosave_item(&item, workspace.project.clone(), cx)
-                        .detach_and_log_err(cx);
-                }
-            })
-            .detach();
-
-            let item_id = self.id();
-            cx.observe_release(self, move |workspace, _, _| {
-                workspace.panes_by_item.remove(&item_id);
-                event_subscription.take();
-            })
-            .detach();
-        }
-
-        cx.defer(|workspace, cx| {
-            workspace.serialize_workspace(cx);
-        });
-    }
-
-    fn deactivated(&self, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.deactivated(cx));
-    }
-
-    fn workspace_deactivated(&self, cx: &mut WindowContext) {
-        self.update(cx, |this, cx| this.workspace_deactivated(cx));
-    }
-
-    fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool {
-        self.update(cx, |this, cx| this.navigate(data, cx))
-    }
-
-    fn id(&self) -> usize {
-        self.id()
-    }
-
-    fn window(&self) -> AnyWindowHandle {
-        AnyViewHandle::window(self)
-    }
-
-    fn as_any(&self) -> &AnyViewHandle {
-        self
-    }
-
-    fn is_dirty(&self, cx: &AppContext) -> bool {
-        self.read(cx).is_dirty(cx)
-    }
-
-    fn has_conflict(&self, cx: &AppContext) -> bool {
-        self.read(cx).has_conflict(cx)
-    }
-
-    fn can_save(&self, cx: &AppContext) -> bool {
-        self.read(cx).can_save(cx)
-    }
-
-    fn save(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
-        self.update(cx, |item, cx| item.save(project, cx))
-    }
-
-    fn save_as(
-        &self,
-        project: ModelHandle<Project>,
-        abs_path: PathBuf,
-        cx: &mut WindowContext,
-    ) -> Task<anyhow::Result<()>> {
-        self.update(cx, |item, cx| item.save_as(project, abs_path, cx))
-    }
-
-    fn reload(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
-        self.update(cx, |item, cx| item.reload(project, cx))
-    }
-
-    fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle> {
-        self.read(cx).act_as_type(type_id, self, cx)
-    }
-
-    fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>> {
-        if cx.has_global::<FollowableItemBuilders>() {
-            let builders = cx.global::<FollowableItemBuilders>();
-            let item = self.as_any();
-            Some(builders.get(&item.view_type())?.1(item))
-        } else {
-            None
-        }
-    }
-
-    fn on_release(
-        &self,
-        cx: &mut AppContext,
-        callback: Box<dyn FnOnce(&mut AppContext)>,
-    ) -> gpui2::Subscription {
-        cx.observe_release(self, move |_, cx| callback(cx))
-    }
-
-    fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
-        self.read(cx).as_searchable(self)
-    }
-
-    fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation {
-        self.read(cx).breadcrumb_location()
-    }
-
-    fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
-        self.read(cx).breadcrumbs(theme, cx)
-    }
-
-    fn serialized_item_kind(&self) -> Option<&'static str> {
-        T::serialized_item_kind()
-    }
-
-    fn show_toolbar(&self, cx: &AppContext) -> bool {
-        self.read(cx).show_toolbar()
-    }
-
-    fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
-        self.read(cx).pixel_position_of_cursor(cx)
-    }
-}
-
-impl From<Box<dyn ItemHandle>> for AnyViewHandle {
-    fn from(val: Box<dyn ItemHandle>) -> Self {
-        val.as_any().clone()
-    }
-}
-
-impl From<&Box<dyn ItemHandle>> for AnyViewHandle {
-    fn from(val: &Box<dyn ItemHandle>) -> Self {
-        val.as_any().clone()
-    }
-}
-
-impl Clone for Box<dyn ItemHandle> {
-    fn clone(&self) -> Box<dyn ItemHandle> {
-        self.boxed_clone()
-    }
-}
-
-impl<T: Item> WeakItemHandle for WeakViewHandle<T> {
-    fn id(&self) -> usize {
-        self.id()
-    }
-
-    fn window(&self) -> AnyWindowHandle {
-        self.window()
-    }
-
-    fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
-        self.upgrade(cx).map(|v| Box::new(v) as Box<dyn ItemHandle>)
-    }
-}
-
-pub trait ProjectItem: Item {
-    type Item: project2::Item + gpui2::Entity;
-
-    fn for_project_item(
-        project: ModelHandle<Project>,
-        item: ModelHandle<Self::Item>,
-        cx: &mut ViewContext<Self>,
-    ) -> Self;
-}
-
-pub trait FollowableItem: Item {
-    fn remote_id(&self) -> Option<ViewId>;
-    fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
-    fn from_state_proto(
-        pane: ViewHandle<Pane>,
-        project: ViewHandle<Workspace>,
-        id: ViewId,
-        state: &mut Option<proto::view::Variant>,
-        cx: &mut AppContext,
-    ) -> Option<Task<Result<ViewHandle<Self>>>>;
-    fn add_event_to_update_proto(
-        &self,
-        event: &Self::Event,
-        update: &mut Option<proto::update_view::Variant>,
-        cx: &AppContext,
-    ) -> bool;
-    fn apply_update_proto(
-        &mut self,
-        project: &ModelHandle<Project>,
-        message: proto::update_view::Variant,
-        cx: &mut ViewContext<Self>,
-    ) -> Task<Result<()>>;
-    fn is_project_item(&self, cx: &AppContext) -> bool;
-
-    fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>);
-    fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool;
-}
-
-pub trait FollowableItemHandle: ItemHandle {
-    fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId>;
-    fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext);
-    fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
-    fn add_event_to_update_proto(
-        &self,
-        event: &dyn Any,
-        update: &mut Option<proto::update_view::Variant>,
-        cx: &AppContext,
-    ) -> bool;
-    fn apply_update_proto(
-        &self,
-        project: &ModelHandle<Project>,
-        message: proto::update_view::Variant,
-        cx: &mut WindowContext,
-    ) -> Task<Result<()>>;
-    fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool;
-    fn is_project_item(&self, cx: &AppContext) -> bool;
-}
-
-impl<T: FollowableItem> FollowableItemHandle for ViewHandle<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 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 apply_update_proto(
-        &self,
-        project: &ModelHandle<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 is_project_item(&self, cx: &AppContext) -> bool {
-        self.read(cx).is_project_item(cx)
-    }
-}
-
-#[cfg(any(test, feature = "test-support"))]
-pub mod test {
-    use super::{Item, ItemEvent};
-    use crate::{ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId};
-    use gpui2::{
-        elements::Empty, AnyElement, AppContext, Element, Entity, ModelHandle, Task, View,
-        ViewContext, ViewHandle, WeakViewHandle,
-    };
-    use project2::{Project, ProjectEntryId, ProjectPath, WorktreeId};
-    use smallvec::SmallVec;
-    use std::{any::Any, borrow::Cow, cell::Cell, path::Path};
-
-    pub struct TestProjectItem {
-        pub entry_id: Option<ProjectEntryId>,
-        pub project_path: Option<ProjectPath>,
-    }
-
-    pub struct TestItem {
-        pub workspace_id: WorkspaceId,
-        pub state: String,
-        pub label: String,
-        pub save_count: usize,
-        pub save_as_count: usize,
-        pub reload_count: usize,
-        pub is_dirty: bool,
-        pub is_singleton: bool,
-        pub has_conflict: bool,
-        pub project_items: Vec<ModelHandle<TestProjectItem>>,
-        pub nav_history: Option<ItemNavHistory>,
-        pub tab_descriptions: Option<Vec<&'static str>>,
-        pub tab_detail: Cell<Option<usize>>,
-    }
-
-    impl Entity for TestProjectItem {
-        type Event = ();
-    }
-
-    impl project2::Item for TestProjectItem {
-        fn entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
-            self.entry_id
-        }
-
-        fn project_path(&self, _: &AppContext) -> Option<ProjectPath> {
-            self.project_path.clone()
-        }
-    }
-
-    pub enum TestItemEvent {
-        Edit,
-    }
-
-    impl Clone for TestItem {
-        fn clone(&self) -> Self {
-            Self {
-                state: self.state.clone(),
-                label: self.label.clone(),
-                save_count: self.save_count,
-                save_as_count: self.save_as_count,
-                reload_count: self.reload_count,
-                is_dirty: self.is_dirty,
-                is_singleton: self.is_singleton,
-                has_conflict: self.has_conflict,
-                project_items: self.project_items.clone(),
-                nav_history: None,
-                tab_descriptions: None,
-                tab_detail: Default::default(),
-                workspace_id: self.workspace_id,
-            }
-        }
-    }
-
-    impl TestProjectItem {
-        pub fn new(id: u64, path: &str, cx: &mut AppContext) -> ModelHandle<Self> {
-            let entry_id = Some(ProjectEntryId::from_proto(id));
-            let project_path = Some(ProjectPath {
-                worktree_id: WorktreeId::from_usize(0),
-                path: Path::new(path).into(),
-            });
-            cx.add_model(|_| Self {
-                entry_id,
-                project_path,
-            })
-        }
-
-        pub fn new_untitled(cx: &mut AppContext) -> ModelHandle<Self> {
-            cx.add_model(|_| Self {
-                project_path: None,
-                entry_id: None,
-            })
-        }
-    }
-
-    impl TestItem {
-        pub fn new() -> Self {
-            Self {
-                state: String::new(),
-                label: String::new(),
-                save_count: 0,
-                save_as_count: 0,
-                reload_count: 0,
-                is_dirty: false,
-                has_conflict: false,
-                project_items: Vec::new(),
-                is_singleton: true,
-                nav_history: None,
-                tab_descriptions: None,
-                tab_detail: Default::default(),
-                workspace_id: 0,
-            }
-        }
-
-        pub fn new_deserialized(id: WorkspaceId) -> Self {
-            let mut this = Self::new();
-            this.workspace_id = id;
-            this
-        }
-
-        pub fn with_label(mut self, state: &str) -> Self {
-            self.label = state.to_string();
-            self
-        }
-
-        pub fn with_singleton(mut self, singleton: bool) -> Self {
-            self.is_singleton = singleton;
-            self
-        }
-
-        pub fn with_dirty(mut self, dirty: bool) -> Self {
-            self.is_dirty = dirty;
-            self
-        }
-
-        pub fn with_conflict(mut self, has_conflict: bool) -> Self {
-            self.has_conflict = has_conflict;
-            self
-        }
-
-        pub fn with_project_items(mut self, items: &[ModelHandle<TestProjectItem>]) -> Self {
-            self.project_items.clear();
-            self.project_items.extend(items.iter().cloned());
-            self
-        }
-
-        pub fn set_state(&mut self, state: String, cx: &mut ViewContext<Self>) {
-            self.push_to_nav_history(cx);
-            self.state = state;
-        }
-
-        fn push_to_nav_history(&mut self, cx: &mut ViewContext<Self>) {
-            if let Some(history) = &mut self.nav_history {
-                history.push(Some(Box::new(self.state.clone())), cx);
-            }
-        }
-    }
-
-    impl Entity for TestItem {
-        type Event = TestItemEvent;
-    }
-
-    impl View for TestItem {
-        fn ui_name() -> &'static str {
-            "TestItem"
-        }
-
-        fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
-            Empty::new().into_any()
-        }
-    }
-
-    impl Item for TestItem {
-        fn tab_description(&self, detail: usize, _: &AppContext) -> Option<Cow<str>> {
-            self.tab_descriptions.as_ref().and_then(|descriptions| {
-                let description = *descriptions.get(detail).or_else(|| descriptions.last())?;
-                Some(description.into())
-            })
-        }
-
-        fn tab_content<V: 'static>(
-            &self,
-            detail: Option<usize>,
-            _: &theme2::Tab,
-            _: &AppContext,
-        ) -> AnyElement<V> {
-            self.tab_detail.set(detail);
-            Empty::new().into_any()
-        }
-
-        fn for_each_project_item(
-            &self,
-            cx: &AppContext,
-            f: &mut dyn FnMut(usize, &dyn project2::Item),
-        ) {
-            self.project_items
-                .iter()
-                .for_each(|item| f(item.id(), item.read(cx)))
-        }
-
-        fn is_singleton(&self, _: &AppContext) -> bool {
-            self.is_singleton
-        }
-
-        fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
-            self.nav_history = Some(history);
-        }
-
-        fn navigate(&mut self, state: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
-            let state = *state.downcast::<String>().unwrap_or_default();
-            if state != self.state {
-                self.state = state;
-                true
-            } else {
-                false
-            }
-        }
-
-        fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
-            self.push_to_nav_history(cx);
-        }
-
-        fn clone_on_split(
-            &self,
-            _workspace_id: WorkspaceId,
-            _: &mut ViewContext<Self>,
-        ) -> Option<Self>
-        where
-            Self: Sized,
-        {
-            Some(self.clone())
-        }
-
-        fn is_dirty(&self, _: &AppContext) -> bool {
-            self.is_dirty
-        }
-
-        fn has_conflict(&self, _: &AppContext) -> bool {
-            self.has_conflict
-        }
-
-        fn can_save(&self, cx: &AppContext) -> bool {
-            !self.project_items.is_empty()
-                && self
-                    .project_items
-                    .iter()
-                    .all(|item| item.read(cx).entry_id.is_some())
-        }
-
-        fn save(
-            &mut self,
-            _: ModelHandle<Project>,
-            _: &mut ViewContext<Self>,
-        ) -> Task<anyhow::Result<()>> {
-            self.save_count += 1;
-            self.is_dirty = false;
-            Task::ready(Ok(()))
-        }
-
-        fn save_as(
-            &mut self,
-            _: ModelHandle<Project>,
-            _: std::path::PathBuf,
-            _: &mut ViewContext<Self>,
-        ) -> Task<anyhow::Result<()>> {
-            self.save_as_count += 1;
-            self.is_dirty = false;
-            Task::ready(Ok(()))
-        }
-
-        fn reload(
-            &mut self,
-            _: ModelHandle<Project>,
-            _: &mut ViewContext<Self>,
-        ) -> Task<anyhow::Result<()>> {
-            self.reload_count += 1;
-            self.is_dirty = false;
-            Task::ready(Ok(()))
-        }
-
-        fn to_item_events(_: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
-            [ItemEvent::UpdateTab, ItemEvent::Edit].into()
-        }
-
-        fn serialized_item_kind() -> Option<&'static str> {
-            Some("TestItem")
-        }
-
-        fn deserialize(
-            _project: ModelHandle<Project>,
-            _workspace: WeakViewHandle<Workspace>,
-            workspace_id: WorkspaceId,
-            _item_id: ItemId,
-            cx: &mut ViewContext<Pane>,
-        ) -> Task<anyhow::Result<ViewHandle<Self>>> {
-            let view = cx.add_view(|_cx| Self::new_deserialized(workspace_id));
-            Task::Ready(Some(anyhow::Ok(view)))
-        }
-    }
-}
+// pub trait WeakItemHandle {
+//     fn id(&self) -> usize;
+//     fn window(&self) -> AnyWindowHandle;
+//     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>>;
+// }
+
+// impl dyn ItemHandle {
+//     pub fn downcast<T: View>(&self) -> Option<ViewHandle<T>> {
+//         self.as_any().clone().downcast()
+//     }
+
+//     pub fn act_as<T: View>(&self, cx: &AppContext) -> Option<ViewHandle<T>> {
+//         self.act_as_type(TypeId::of::<T>(), cx)
+//             .and_then(|t| t.clone().downcast())
+//     }
+// }
+
+// impl<T: Item> ItemHandle for ViewHandle<T> {
+//     fn subscribe_to_item_events(
+//         &self,
+//         cx: &mut WindowContext,
+//         handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
+//     ) -> gpui2::Subscription {
+//         cx.subscribe(self, move |_, event, cx| {
+//             for item_event in T::to_item_events(event) {
+//                 handler(item_event, cx)
+//             }
+//         })
+//     }
+
+//     fn tab_tooltip_text<'a>(&self, cx: &'a AppContext) -> Option<Cow<'a, str>> {
+//         self.read(cx).tab_tooltip_text(cx)
+//     }
+
+//     fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>> {
+//         self.read(cx).tab_description(detail, cx)
+//     }
+
+//     fn tab_content(
+//         &self,
+//         detail: Option<usize>,
+//         style: &theme2::Tab,
+//         cx: &AppContext,
+//     ) -> AnyElement<Pane> {
+//         self.read(cx).tab_content(detail, style, cx)
+//     }
+
+//     fn dragged_tab_content(
+//         &self,
+//         detail: Option<usize>,
+//         style: &theme2::Tab,
+//         cx: &AppContext,
+//     ) -> AnyElement<Workspace> {
+//         self.read(cx).tab_content(detail, style, cx)
+//     }
+
+//     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
+//         let this = self.read(cx);
+//         let mut result = None;
+//         if this.is_singleton(cx) {
+//             this.for_each_project_item(cx, &mut |_, item| {
+//                 result = item.project_path(cx);
+//             });
+//         }
+//         result
+//     }
+
+//     fn project_entry_ids(&self, cx: &AppContext) -> SmallVec<[ProjectEntryId; 3]> {
+//         let mut result = SmallVec::new();
+//         self.read(cx).for_each_project_item(cx, &mut |_, item| {
+//             if let Some(id) = item.entry_id(cx) {
+//                 result.push(id);
+//             }
+//         });
+//         result
+//     }
+
+//     fn project_item_model_ids(&self, cx: &AppContext) -> SmallVec<[usize; 3]> {
+//         let mut result = SmallVec::new();
+//         self.read(cx).for_each_project_item(cx, &mut |id, _| {
+//             result.push(id);
+//         });
+//         result
+//     }
+
+//     fn for_each_project_item(
+//         &self,
+//         cx: &AppContext,
+//         f: &mut dyn FnMut(usize, &dyn project2::Item),
+//     ) {
+//         self.read(cx).for_each_project_item(cx, f)
+//     }
+
+//     fn is_singleton(&self, cx: &AppContext) -> bool {
+//         self.read(cx).is_singleton(cx)
+//     }
+
+//     fn boxed_clone(&self) -> Box<dyn ItemHandle> {
+//         Box::new(self.clone())
+//     }
+
+//     fn clone_on_split(
+//         &self,
+//         workspace_id: WorkspaceId,
+//         cx: &mut WindowContext,
+//     ) -> Option<Box<dyn ItemHandle>> {
+//         self.update(cx, |item, cx| {
+//             cx.add_option_view(|cx| item.clone_on_split(workspace_id, cx))
+//         })
+//         .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
+//     }
+
+//     fn added_to_pane(
+//         &self,
+//         workspace: &mut Workspace,
+//         pane: ViewHandle<Pane>,
+//         cx: &mut ViewContext<Workspace>,
+//     ) {
+//         let history = pane.read(cx).nav_history_for_item(self);
+//         self.update(cx, |this, cx| {
+//             this.set_nav_history(history, cx);
+//             this.added_to_workspace(workspace, cx);
+//         });
+
+//         if let Some(followed_item) = self.to_followable_item_handle(cx) {
+//             if let Some(message) = followed_item.to_state_proto(cx) {
+//                 workspace.update_followers(
+//                     followed_item.is_project_item(cx),
+//                     proto::update_followers::Variant::CreateView(proto::View {
+//                         id: followed_item
+//                             .remote_id(&workspace.app_state.client, cx)
+//                             .map(|id| id.to_proto()),
+//                         variant: Some(message),
+//                         leader_id: workspace.leader_for_pane(&pane),
+//                     }),
+//                     cx,
+//                 );
+//             }
+//         }
+
+//         if workspace
+//             .panes_by_item
+//             .insert(self.id(), pane.downgrade())
+//             .is_none()
+//         {
+//             let mut pending_autosave = DelayedDebouncedEditAction::new();
+//             let pending_update = Rc::new(RefCell::new(None));
+//             let pending_update_scheduled = Rc::new(AtomicBool::new(false));
+
+//             let mut event_subscription =
+//                 Some(cx.subscribe(self, move |workspace, item, event, cx| {
+//                     let pane = if let Some(pane) = workspace
+//                         .panes_by_item
+//                         .get(&item.id())
+//                         .and_then(|pane| pane.upgrade(cx))
+//                     {
+//                         pane
+//                     } else {
+//                         log::error!("unexpected item event after pane was dropped");
+//                         return;
+//                     };
+
+//                     if let Some(item) = item.to_followable_item_handle(cx) {
+//                         let is_project_item = item.is_project_item(cx);
+//                         let leader_id = workspace.leader_for_pane(&pane);
+
+//                         if leader_id.is_some() && item.should_unfollow_on_event(event, cx) {
+//                             workspace.unfollow(&pane, cx);
+//                         }
+
+//                         if item.add_event_to_update_proto(
+//                             event,
+//                             &mut *pending_update.borrow_mut(),
+//                             cx,
+//                         ) && !pending_update_scheduled.load(Ordering::SeqCst)
+//                         {
+//                             pending_update_scheduled.store(true, Ordering::SeqCst);
+//                             cx.after_window_update({
+//                                 let pending_update = pending_update.clone();
+//                                 let pending_update_scheduled = pending_update_scheduled.clone();
+//                                 move |this, cx| {
+//                                     pending_update_scheduled.store(false, Ordering::SeqCst);
+//                                     this.update_followers(
+//                                         is_project_item,
+//                                         proto::update_followers::Variant::UpdateView(
+//                                             proto::UpdateView {
+//                                                 id: item
+//                                                     .remote_id(&this.app_state.client, cx)
+//                                                     .map(|id| id.to_proto()),
+//                                                 variant: pending_update.borrow_mut().take(),
+//                                                 leader_id,
+//                                             },
+//                                         ),
+//                                         cx,
+//                                     );
+//                                 }
+//                             });
+//                         }
+//                     }
+
+//                     for item_event in T::to_item_events(event).into_iter() {
+//                         match item_event {
+//                             ItemEvent::CloseItem => {
+//                                 pane.update(cx, |pane, cx| {
+//                                     pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx)
+//                                 })
+//                                 .detach_and_log_err(cx);
+//                                 return;
+//                             }
+
+//                             ItemEvent::UpdateTab => {
+//                                 pane.update(cx, |_, cx| {
+//                                     cx.emit(pane::Event::ChangeItemTitle);
+//                                     cx.notify();
+//                                 });
+//                             }
+
+//                             ItemEvent::Edit => {
+//                                 let autosave = settings2::get::<WorkspaceSettings>(cx).autosave;
+//                                 if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
+//                                     let delay = Duration::from_millis(milliseconds);
+//                                     let item = item.clone();
+//                                     pending_autosave.fire_new(delay, cx, move |workspace, cx| {
+//                                         Pane::autosave_item(&item, workspace.project().clone(), cx)
+//                                     });
+//                                 }
+//                             }
+
+//                             _ => {}
+//                         }
+//                     }
+//                 }));
+
+//             cx.observe_focus(self, move |workspace, item, focused, cx| {
+//                 if !focused
+//                     && settings2::get::<WorkspaceSettings>(cx).autosave
+//                         == AutosaveSetting::OnFocusChange
+//                 {
+//                     Pane::autosave_item(&item, workspace.project.clone(), cx)
+//                         .detach_and_log_err(cx);
+//                 }
+//             })
+//             .detach();
+
+//             let item_id = self.id();
+//             cx.observe_release(self, move |workspace, _, _| {
+//                 workspace.panes_by_item.remove(&item_id);
+//                 event_subscription.take();
+//             })
+//             .detach();
+//         }
+
+//         cx.defer(|workspace, cx| {
+//             workspace.serialize_workspace(cx);
+//         });
+//     }
+
+//     fn deactivated(&self, cx: &mut WindowContext) {
+//         self.update(cx, |this, cx| this.deactivated(cx));
+//     }
+
+//     fn workspace_deactivated(&self, cx: &mut WindowContext) {
+//         self.update(cx, |this, cx| this.workspace_deactivated(cx));
+//     }
+
+//     fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool {
+//         self.update(cx, |this, cx| this.navigate(data, cx))
+//     }
+
+//     fn id(&self) -> usize {
+//         self.id()
+//     }
+
+//     fn window(&self) -> AnyWindowHandle {
+//         AnyViewHandle::window(self)
+//     }
+
+//     fn as_any(&self) -> &AnyViewHandle {
+//         self
+//     }
+
+//     fn is_dirty(&self, cx: &AppContext) -> bool {
+//         self.read(cx).is_dirty(cx)
+//     }
+
+//     fn has_conflict(&self, cx: &AppContext) -> bool {
+//         self.read(cx).has_conflict(cx)
+//     }
+
+//     fn can_save(&self, cx: &AppContext) -> bool {
+//         self.read(cx).can_save(cx)
+//     }
+
+//     fn save(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
+//         self.update(cx, |item, cx| item.save(project, cx))
+//     }
+
+//     fn save_as(
+//         &self,
+//         project: ModelHandle<Project>,
+//         abs_path: PathBuf,
+//         cx: &mut WindowContext,
+//     ) -> Task<anyhow::Result<()>> {
+//         self.update(cx, |item, cx| item.save_as(project, abs_path, cx))
+//     }
+
+//     fn reload(&self, project: ModelHandle<Project>, cx: &mut WindowContext) -> Task<Result<()>> {
+//         self.update(cx, |item, cx| item.reload(project, cx))
+//     }
+
+//     fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle> {
+//         self.read(cx).act_as_type(type_id, self, cx)
+//     }
+
+//     fn to_followable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn FollowableItemHandle>> {
+//         if cx.has_global::<FollowableItemBuilders>() {
+//             let builders = cx.global::<FollowableItemBuilders>();
+//             let item = self.as_any();
+//             Some(builders.get(&item.view_type())?.1(item))
+//         } else {
+//             None
+//         }
+//     }
+
+//     fn on_release(
+//         &self,
+//         cx: &mut AppContext,
+//         callback: Box<dyn FnOnce(&mut AppContext)>,
+//     ) -> gpui2::Subscription {
+//         cx.observe_release(self, move |_, cx| callback(cx))
+//     }
+
+//     fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
+//         self.read(cx).as_searchable(self)
+//     }
+
+//     fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation {
+//         self.read(cx).breadcrumb_location()
+//     }
+
+//     fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
+//         self.read(cx).breadcrumbs(theme, cx)
+//     }
+
+//     fn serialized_item_kind(&self) -> Option<&'static str> {
+//         T::serialized_item_kind()
+//     }
+
+//     fn show_toolbar(&self, cx: &AppContext) -> bool {
+//         self.read(cx).show_toolbar()
+//     }
+
+//     fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
+//         self.read(cx).pixel_position_of_cursor(cx)
+//     }
+// }
+
+// impl From<Box<dyn ItemHandle>> for AnyViewHandle {
+//     fn from(val: Box<dyn ItemHandle>) -> Self {
+//         val.as_any().clone()
+//     }
+// }
+
+// impl From<&Box<dyn ItemHandle>> for AnyViewHandle {
+//     fn from(val: &Box<dyn ItemHandle>) -> Self {
+//         val.as_any().clone()
+//     }
+// }
+
+// impl Clone for Box<dyn ItemHandle> {
+//     fn clone(&self) -> Box<dyn ItemHandle> {
+//         self.boxed_clone()
+//     }
+// }
+
+// impl<T: Item> WeakItemHandle for WeakViewHandle<T> {
+//     fn id(&self) -> usize {
+//         self.id()
+//     }
+
+//     fn window(&self) -> AnyWindowHandle {
+//         self.window()
+//     }
+
+//     fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
+//         self.upgrade(cx).map(|v| Box::new(v) as Box<dyn ItemHandle>)
+//     }
+// }
+
+// pub trait ProjectItem: Item {
+//     type Item: project2::Item + gpui2::Entity;
+
+//     fn for_project_item(
+//         project: ModelHandle<Project>,
+//         item: ModelHandle<Self::Item>,
+//         cx: &mut ViewContext<Self>,
+//     ) -> Self;
+// }
+
+// pub trait FollowableItem: Item {
+//     fn remote_id(&self) -> Option<ViewId>;
+//     fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
+//     fn from_state_proto(
+//         pane: ViewHandle<Pane>,
+//         project: ViewHandle<Workspace>,
+//         id: ViewId,
+//         state: &mut Option<proto::view::Variant>,
+//         cx: &mut AppContext,
+//     ) -> Option<Task<Result<ViewHandle<Self>>>>;
+//     fn add_event_to_update_proto(
+//         &self,
+//         event: &Self::Event,
+//         update: &mut Option<proto::update_view::Variant>,
+//         cx: &AppContext,
+//     ) -> bool;
+//     fn apply_update_proto(
+//         &mut self,
+//         project: &ModelHandle<Project>,
+//         message: proto::update_view::Variant,
+//         cx: &mut ViewContext<Self>,
+//     ) -> Task<Result<()>>;
+//     fn is_project_item(&self, cx: &AppContext) -> bool;
+
+//     fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>);
+//     fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool;
+// }
+
+// pub trait FollowableItemHandle: ItemHandle {
+//     fn remote_id(&self, client: &Arc<Client>, cx: &AppContext) -> Option<ViewId>;
+//     fn set_leader_peer_id(&self, leader_peer_id: Option<PeerId>, cx: &mut WindowContext);
+//     fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
+//     fn add_event_to_update_proto(
+//         &self,
+//         event: &dyn Any,
+//         update: &mut Option<proto::update_view::Variant>,
+//         cx: &AppContext,
+//     ) -> bool;
+//     fn apply_update_proto(
+//         &self,
+//         project: &ModelHandle<Project>,
+//         message: proto::update_view::Variant,
+//         cx: &mut WindowContext,
+//     ) -> Task<Result<()>>;
+//     fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool;
+//     fn is_project_item(&self, cx: &AppContext) -> bool;
+// }
+
+// impl<T: FollowableItem> FollowableItemHandle for ViewHandle<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 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 apply_update_proto(
+//         &self,
+//         project: &ModelHandle<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 is_project_item(&self, cx: &AppContext) -> bool {
+//         self.read(cx).is_project_item(cx)
+//     }
+// }
+
+// #[cfg(any(test, feature = "test-support"))]
+// pub mod test {
+//     use super::{Item, ItemEvent};
+//     use crate::{ItemId, ItemNavHistory, Pane, Workspace, WorkspaceId};
+//     use gpui2::{
+//         elements::Empty, AnyElement, AppContext, Element, Entity, ModelHandle, Task, View,
+//         ViewContext, ViewHandle, WeakViewHandle,
+//     };
+//     use project2::{Project, ProjectEntryId, ProjectPath, WorktreeId};
+//     use smallvec::SmallVec;
+//     use std::{any::Any, borrow::Cow, cell::Cell, path::Path};
+
+//     pub struct TestProjectItem {
+//         pub entry_id: Option<ProjectEntryId>,
+//         pub project_path: Option<ProjectPath>,
+//     }
+
+//     pub struct TestItem {
+//         pub workspace_id: WorkspaceId,
+//         pub state: String,
+//         pub label: String,
+//         pub save_count: usize,
+//         pub save_as_count: usize,
+//         pub reload_count: usize,
+//         pub is_dirty: bool,
+//         pub is_singleton: bool,
+//         pub has_conflict: bool,
+//         pub project_items: Vec<ModelHandle<TestProjectItem>>,
+//         pub nav_history: Option<ItemNavHistory>,
+//         pub tab_descriptions: Option<Vec<&'static str>>,
+//         pub tab_detail: Cell<Option<usize>>,
+//     }
+
+//     impl Entity for TestProjectItem {
+//         type Event = ();
+//     }
+
+//     impl project2::Item for TestProjectItem {
+//         fn entry_id(&self, _: &AppContext) -> Option<ProjectEntryId> {
+//             self.entry_id
+//         }
+
+//         fn project_path(&self, _: &AppContext) -> Option<ProjectPath> {
+//             self.project_path.clone()
+//         }
+//     }
+
+//     pub enum TestItemEvent {
+//         Edit,
+//     }
+
+//     impl Clone for TestItem {
+//         fn clone(&self) -> Self {
+//             Self {
+//                 state: self.state.clone(),
+//                 label: self.label.clone(),
+//                 save_count: self.save_count,
+//                 save_as_count: self.save_as_count,
+//                 reload_count: self.reload_count,
+//                 is_dirty: self.is_dirty,
+//                 is_singleton: self.is_singleton,
+//                 has_conflict: self.has_conflict,
+//                 project_items: self.project_items.clone(),
+//                 nav_history: None,
+//                 tab_descriptions: None,
+//                 tab_detail: Default::default(),
+//                 workspace_id: self.workspace_id,
+//             }
+//         }
+//     }
+
+//     impl TestProjectItem {
+//         pub fn new(id: u64, path: &str, cx: &mut AppContext) -> ModelHandle<Self> {
+//             let entry_id = Some(ProjectEntryId::from_proto(id));
+//             let project_path = Some(ProjectPath {
+//                 worktree_id: WorktreeId::from_usize(0),
+//                 path: Path::new(path).into(),
+//             });
+//             cx.add_model(|_| Self {
+//                 entry_id,
+//                 project_path,
+//             })
+//         }
+
+//         pub fn new_untitled(cx: &mut AppContext) -> ModelHandle<Self> {
+//             cx.add_model(|_| Self {
+//                 project_path: None,
+//                 entry_id: None,
+//             })
+//         }
+//     }
+
+//     impl TestItem {
+//         pub fn new() -> Self {
+//             Self {
+//                 state: String::new(),
+//                 label: String::new(),
+//                 save_count: 0,
+//                 save_as_count: 0,
+//                 reload_count: 0,
+//                 is_dirty: false,
+//                 has_conflict: false,
+//                 project_items: Vec::new(),
+//                 is_singleton: true,
+//                 nav_history: None,
+//                 tab_descriptions: None,
+//                 tab_detail: Default::default(),
+//                 workspace_id: 0,
+//             }
+//         }
+
+//         pub fn new_deserialized(id: WorkspaceId) -> Self {
+//             let mut this = Self::new();
+//             this.workspace_id = id;
+//             this
+//         }
+
+//         pub fn with_label(mut self, state: &str) -> Self {
+//             self.label = state.to_string();
+//             self
+//         }
+
+//         pub fn with_singleton(mut self, singleton: bool) -> Self {
+//             self.is_singleton = singleton;
+//             self
+//         }
+
+//         pub fn with_dirty(mut self, dirty: bool) -> Self {
+//             self.is_dirty = dirty;
+//             self
+//         }
+
+//         pub fn with_conflict(mut self, has_conflict: bool) -> Self {
+//             self.has_conflict = has_conflict;
+//             self
+//         }
+
+//         pub fn with_project_items(mut self, items: &[ModelHandle<TestProjectItem>]) -> Self {
+//             self.project_items.clear();
+//             self.project_items.extend(items.iter().cloned());
+//             self
+//         }
+
+//         pub fn set_state(&mut self, state: String, cx: &mut ViewContext<Self>) {
+//             self.push_to_nav_history(cx);
+//             self.state = state;
+//         }
+
+//         fn push_to_nav_history(&mut self, cx: &mut ViewContext<Self>) {
+//             if let Some(history) = &mut self.nav_history {
+//                 history.push(Some(Box::new(self.state.clone())), cx);
+//             }
+//         }
+//     }
+
+//     impl Entity for TestItem {
+//         type Event = TestItemEvent;
+//     }
+
+//     impl View for TestItem {
+//         fn ui_name() -> &'static str {
+//             "TestItem"
+//         }
+
+//         fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
+//             Empty::new().into_any()
+//         }
+//     }
+
+//     impl Item for TestItem {
+//         fn tab_description(&self, detail: usize, _: &AppContext) -> Option<Cow<str>> {
+//             self.tab_descriptions.as_ref().and_then(|descriptions| {
+//                 let description = *descriptions.get(detail).or_else(|| descriptions.last())?;
+//                 Some(description.into())
+//             })
+//         }
+
+//         fn tab_content<V: 'static>(
+//             &self,
+//             detail: Option<usize>,
+//             _: &theme2::Tab,
+//             _: &AppContext,
+//         ) -> AnyElement<V> {
+//             self.tab_detail.set(detail);
+//             Empty::new().into_any()
+//         }
+
+//         fn for_each_project_item(
+//             &self,
+//             cx: &AppContext,
+//             f: &mut dyn FnMut(usize, &dyn project2::Item),
+//         ) {
+//             self.project_items
+//                 .iter()
+//                 .for_each(|item| f(item.id(), item.read(cx)))
+//         }
+
+//         fn is_singleton(&self, _: &AppContext) -> bool {
+//             self.is_singleton
+//         }
+
+//         fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
+//             self.nav_history = Some(history);
+//         }
+
+//         fn navigate(&mut self, state: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
+//             let state = *state.downcast::<String>().unwrap_or_default();
+//             if state != self.state {
+//                 self.state = state;
+//                 true
+//             } else {
+//                 false
+//             }
+//         }
+
+//         fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
+//             self.push_to_nav_history(cx);
+//         }
+
+//         fn clone_on_split(
+//             &self,
+//             _workspace_id: WorkspaceId,
+//             _: &mut ViewContext<Self>,
+//         ) -> Option<Self>
+//         where
+//             Self: Sized,
+//         {
+//             Some(self.clone())
+//         }
+
+//         fn is_dirty(&self, _: &AppContext) -> bool {
+//             self.is_dirty
+//         }
+
+//         fn has_conflict(&self, _: &AppContext) -> bool {
+//             self.has_conflict
+//         }
+
+//         fn can_save(&self, cx: &AppContext) -> bool {
+//             !self.project_items.is_empty()
+//                 && self
+//                     .project_items
+//                     .iter()
+//                     .all(|item| item.read(cx).entry_id.is_some())
+//         }
+
+//         fn save(
+//             &mut self,
+//             _: ModelHandle<Project>,
+//             _: &mut ViewContext<Self>,
+//         ) -> Task<anyhow::Result<()>> {
+//             self.save_count += 1;
+//             self.is_dirty = false;
+//             Task::ready(Ok(()))
+//         }
+
+//         fn save_as(
+//             &mut self,
+//             _: ModelHandle<Project>,
+//             _: std::path::PathBuf,
+//             _: &mut ViewContext<Self>,
+//         ) -> Task<anyhow::Result<()>> {
+//             self.save_as_count += 1;
+//             self.is_dirty = false;
+//             Task::ready(Ok(()))
+//         }
+
+//         fn reload(
+//             &mut self,
+//             _: ModelHandle<Project>,
+//             _: &mut ViewContext<Self>,
+//         ) -> Task<anyhow::Result<()>> {
+//             self.reload_count += 1;
+//             self.is_dirty = false;
+//             Task::ready(Ok(()))
+//         }
+
+//         fn to_item_events(_: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
+//             [ItemEvent::UpdateTab, ItemEvent::Edit].into()
+//         }
+
+//         fn serialized_item_kind() -> Option<&'static str> {
+//             Some("TestItem")
+//         }
+
+//         fn deserialize(
+//             _project: ModelHandle<Project>,
+//             _workspace: WeakViewHandle<Workspace>,
+//             workspace_id: WorkspaceId,
+//             _item_id: ItemId,
+//             cx: &mut ViewContext<Pane>,
+//         ) -> Task<anyhow::Result<ViewHandle<Self>>> {
+//             let view = cx.add_view(|_cx| Self::new_deserialized(workspace_id));
+//             Task::Ready(Some(anyhow::Ok(view)))
+//         }
+//     }
+// }

crates/workspace2/src/pane.rs 🔗

@@ -120,8 +120,6 @@ impl_actions!(pane, [ActivateItem, CloseActiveItem, CloseAllItems]);
 
 const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
 
-pub type BackgroundActions = fn() -> &'static [(&'static str, &'static dyn Action)];
-
 pub fn init(cx: &mut AppContext) {
     cx.add_action(Pane::toggle_zoom);
     cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
@@ -172,7 +170,6 @@ pub struct Pane {
     toolbar: ViewHandle<Toolbar>,
     tab_bar_context_menu: TabBarContextMenu,
     tab_context_menu: ViewHandle<ContextMenu>,
-    _background_actions: BackgroundActions,
     workspace: WeakViewHandle<Workspace>,
     project: ModelHandle<Project>,
     has_focus: bool,
@@ -306,7 +303,6 @@ impl Pane {
     pub fn new(
         workspace: WeakViewHandle<Workspace>,
         project: ModelHandle<Project>,
-        background_actions: BackgroundActions,
         next_timestamp: Arc<AtomicUsize>,
         cx: &mut ViewContext<Self>,
     ) -> Self {
@@ -339,7 +335,6 @@ impl Pane {
                 handle: context_menu,
             },
             tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)),
-            _background_actions: background_actions,
             workspace,
             project,
             has_focus: false,

crates/workspace2/src/workspace2.rs 🔗

@@ -1,5 +1,5 @@
 // pub mod dock;
-// pub mod item;
+pub mod item;
 // pub mod notifications;
 // pub mod pane;
 // pub mod pane_group;
@@ -11,19 +11,18 @@
 // mod workspace_settings;
 
 // use anyhow::{anyhow, Context, Result};
-// use call::ActiveCall;
-// use client::{
+// use call2::ActiveCall;
+// use client2::{
 //     proto::{self, PeerId},
 //     Client, Status, TypedEnvelope, UserStore,
 // };
 // use collections::{hash_map, HashMap, HashSet};
-// use drag_and_drop::DragAndDrop;
 // use futures::{
 //     channel::{mpsc, oneshot},
 //     future::try_join_all,
 //     FutureExt, StreamExt,
 // };
-// use gpui::{
+// use gpui2::{
 //     actions,
 //     elements::*,
 //     geometry::{
@@ -41,19 +40,8 @@
 // };
 // use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
 // use itertools::Itertools;
-// use language::{LanguageRegistry, Rope};
-// use node_runtime::NodeRuntime;
-// use std::{
-//     any::TypeId,
-//     borrow::Cow,
-//     cmp, env,
-//     future::Future,
-//     path::{Path, PathBuf},
-//     rc::Rc,
-//     str,
-//     sync::{atomic::AtomicUsize, Arc},
-//     time::Duration,
-// };
+// use language2::{LanguageRegistry, Rope};
+// use node_runtime::NodeRuntime;// //
 
 // use crate::{
 //     notifications::{simple_message_notification::MessageNotification, NotificationTracker},
@@ -446,32 +434,31 @@
 //     });
 // }
 
-// pub struct AppState {
-//     pub languages: Arc<LanguageRegistry>,
-//     pub client: Arc<Client>,
-//     pub user_store: ModelHandle<UserStore>,
-//     pub workspace_store: ModelHandle<WorkspaceStore>,
-//     pub fs: Arc<dyn fs::Fs>,
-//     pub build_window_options:
-//         fn(Option<WindowBounds>, Option<uuid::Uuid>, &dyn Platform) -> WindowOptions<'static>,
-//     pub initialize_workspace:
-//         fn(WeakViewHandle<Workspace>, bool, Arc<AppState>, AsyncAppContext) -> Task<Result<()>>,
-//     pub background_actions: BackgroundActions,
-//     pub node_runtime: Arc<dyn NodeRuntime>,
-// }
-
-// pub struct WorkspaceStore {
-//     workspaces: HashSet<WeakViewHandle<Workspace>>,
-//     followers: Vec<Follower>,
-//     client: Arc<Client>,
-//     _subscriptions: Vec<client::Subscription>,
-// }
-
-// #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
-// struct Follower {
-//     project_id: Option<u64>,
-//     peer_id: PeerId,
-// }
+pub struct AppState {
+    pub languages: Arc<LanguageRegistry>,
+    pub client: Arc<Client>,
+    pub user_store: Handle<UserStore>,
+    pub workspace_store: Handle<WorkspaceStore>,
+    pub fs: Arc<dyn fs2::Fs>,
+    pub build_window_options:
+        fn(Option<WindowBounds>, Option<DisplayId>, &MainThread<AppContext>) -> WindowOptions,
+    pub initialize_workspace:
+        fn(WeakHandle<Workspace>, bool, Arc<AppState>, AsyncAppContext) -> Task<anyhow::Result<()>>,
+    pub node_runtime: Arc<dyn NodeRuntime>,
+}
+
+pub struct WorkspaceStore {
+    workspaces: HashSet<WeakHandle<Workspace>>,
+    followers: Vec<Follower>,
+    client: Arc<Client>,
+    _subscriptions: Vec<client2::Subscription>,
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
+struct Follower {
+    project_id: Option<u64>,
+    peer_id: PeerId,
+}
 
 // impl AppState {
 //     #[cfg(any(test, feature = "test-support"))]
@@ -504,7 +491,6 @@
 //             node_runtime: FakeNodeRuntime::new(),
 //             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
 //             build_window_options: |_, _, _| Default::default(),
-//             background_actions: || &[],
 //         })
 //     }
 // }
@@ -560,37 +546,37 @@
 //     ContactRequestedJoin(u64),
 // }
 
-// pub struct Workspace {
-//     weak_self: WeakViewHandle<Self>,
-//     modal: Option<ActiveModal>,
-//     zoomed: Option<AnyWeakViewHandle>,
-//     zoomed_position: Option<DockPosition>,
-//     center: PaneGroup,
-//     left_dock: ViewHandle<Dock>,
-//     bottom_dock: ViewHandle<Dock>,
-//     right_dock: ViewHandle<Dock>,
-//     panes: Vec<ViewHandle<Pane>>,
-//     panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
-//     active_pane: ViewHandle<Pane>,
-//     last_active_center_pane: Option<WeakViewHandle<Pane>>,
-//     last_active_view_id: Option<proto::ViewId>,
-//     status_bar: ViewHandle<StatusBar>,
-//     titlebar_item: Option<AnyViewHandle>,
-//     notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
-//     project: ModelHandle<Project>,
-//     follower_states: HashMap<ViewHandle<Pane>, FollowerState>,
-//     last_leaders_by_pane: HashMap<WeakViewHandle<Pane>, PeerId>,
-//     window_edited: bool,
-//     active_call: Option<(ModelHandle<ActiveCall>, Vec<Subscription>)>,
-//     leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
-//     database_id: WorkspaceId,
-//     app_state: Arc<AppState>,
-//     subscriptions: Vec<Subscription>,
-//     _apply_leader_updates: Task<Result<()>>,
-//     _observe_current_user: Task<Result<()>>,
-//     _schedule_serialize: Option<Task<()>>,
-//     pane_history_timestamp: Arc<AtomicUsize>,
-// }
+pub struct Workspace {
+    weak_self: WeakHandle<Self>,
+    //     modal: Option<ActiveModal>,
+    //     zoomed: Option<AnyWeakViewHandle>,
+    //     zoomed_position: Option<DockPosition>,
+    //     center: PaneGroup,
+    //     left_dock: ViewHandle<Dock>,
+    //     bottom_dock: ViewHandle<Dock>,
+    //     right_dock: ViewHandle<Dock>,
+    //     panes: Vec<ViewHandle<Pane>>,
+    //     panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
+    //     active_pane: ViewHandle<Pane>,
+    //     last_active_center_pane: Option<WeakViewHandle<Pane>>,
+    //     last_active_view_id: Option<proto::ViewId>,
+    //     status_bar: ViewHandle<StatusBar>,
+    //     titlebar_item: Option<AnyViewHandle>,
+    //     notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
+    project: Handle<Project>,
+    //     follower_states: HashMap<ViewHandle<Pane>, FollowerState>,
+    //     last_leaders_by_pane: HashMap<WeakViewHandle<Pane>, PeerId>,
+    //     window_edited: bool,
+    //     active_call: Option<(ModelHandle<ActiveCall>, Vec<Subscription>)>,
+    //     leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
+    //     database_id: WorkspaceId,
+    //     app_state: Arc<AppState>,
+    //     subscriptions: Vec<Subscription>,
+    //     _apply_leader_updates: Task<Result<()>>,
+    //     _observe_current_user: Task<Result<()>>,
+    //     _schedule_serialize: Option<Task<()>>,
+    //     pane_history_timestamp: Arc<AtomicUsize>,
+}
 
 // struct ActiveModal {
 //     view: Box<dyn ModalHandle>,
@@ -669,7 +655,6 @@
 //             Pane::new(
 //                 weak_handle.clone(),
 //                 project.clone(),
-//                 app_state.background_actions,
 //                 pane_history_timestamp.clone(),
 //                 cx,
 //             )
@@ -1976,7 +1961,6 @@
 //             Pane::new(
 //                 self.weak_handle(),
 //                 self.project.clone(),
-//                 self.app_state.background_actions,
 //                 self.pane_history_timestamp.clone(),
 //                 cx,
 //             )
@@ -3536,7 +3520,6 @@
 //             fs: project.read(cx).fs().clone(),
 //             build_window_options: |_, _, _| Default::default(),
 //             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
-//             background_actions: || &[],
 //             node_runtime: FakeNodeRuntime::new(),
 //         });
 //         Self::new(0, project, app_state, cx)
@@ -4117,30 +4100,36 @@
 
 // pub struct WorkspaceCreated(pub WeakViewHandle<Workspace>);
 
-// pub fn activate_workspace_for_project(
-//     cx: &mut AsyncAppContext,
-//     predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
-// ) -> Option<WeakViewHandle<Workspace>> {
-//     for window in cx.windows() {
-//         let handle = window
-//             .update(cx, |cx| {
-//                 if let Some(workspace_handle) = cx.root_view().clone().downcast::<Workspace>() {
-//                     let project = workspace_handle.read(cx).project.clone();
-//                     if project.update(cx, &predicate) {
-//                         cx.activate_window();
-//                         return Some(workspace_handle.clone());
-//                     }
-//                 }
-//                 None
-//             })
-//             .flatten();
-
-//         if let Some(handle) = handle {
-//             return Some(handle.downgrade());
-//         }
-//     }
-//     None
-// }
+pub async fn activate_workspace_for_project(
+    cx: &mut AsyncAppContext,
+    predicate: impl Fn(&Project, &AppContext) -> bool + Send + 'static,
+) -> Option<WeakHandle<Workspace>> {
+    cx.run_on_main(move |cx| {
+        for window in cx.windows() {
+            let handle = cx
+                .update_window(window, |cx| {
+                    if let Some(workspace_handle) = cx.root_view()?.downcast::<Workspace>() {
+                        let project = workspace_handle.read(cx).project.clone();
+                        if project.update(cx, |project, cx| predicate(project, cx)) {
+                            cx.activate_window();
+                            return Some(workspace_handle.clone());
+                        }
+                    }
+                    None
+                })
+                .log_err()
+                .flatten();
+
+            if let Some(handle) = handle {
+                return Some(handle.downgrade());
+            }
+        }
+
+        None
+    })
+    .ok()?
+    .await
+}
 
 // pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
 //     DB.last_workspace().await.log_err().flatten()
@@ -4328,44 +4317,58 @@
 //     None
 // }
 
-// #[allow(clippy::type_complexity)]
-// pub fn open_paths(
-//     abs_paths: &[PathBuf],
-//     app_state: &Arc<AppState>,
-//     requesting_window: Option<WindowHandle<Workspace>>,
-//     cx: &mut AppContext,
-// ) -> Task<
-//     Result<(
-//         WeakViewHandle<Workspace>,
-//         Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
-//     )>,
-// > {
-//     let app_state = app_state.clone();
-//     let abs_paths = abs_paths.to_vec();
-//     cx.spawn(|mut cx| async move {
-//         // Open paths in existing workspace if possible
-//         let existing = activate_workspace_for_project(&mut cx, |project, cx| {
-//             project.contains_paths(&abs_paths, cx)
-//         });
-
-//         if let Some(existing) = existing {
-//             Ok((
-//                 existing.clone(),
-//                 existing
-//                     .update(&mut cx, |workspace, cx| {
-//                         workspace.open_paths(abs_paths, true, cx)
-//                     })?
-//                     .await,
-//             ))
-//         } else {
-//             Ok(cx
-//                 .update(|cx| {
-//                     Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
-//                 })
-//                 .await)
-//         }
-//     })
-// }
+use client2::{proto::PeerId, Client, UserStore};
+use collections::HashSet;
+use gpui2::{
+    AppContext, AsyncAppContext, DisplayId, Handle, MainThread, Task, WeakHandle, WindowBounds,
+    WindowHandle, WindowOptions,
+};
+use item::ItemHandle;
+use language2::LanguageRegistry;
+use node_runtime::NodeRuntime;
+use project2::Project;
+use std::{path::PathBuf, sync::Arc};
+use util::ResultExt;
+
+#[allow(clippy::type_complexity)]
+pub fn open_paths(
+    abs_paths: &[PathBuf],
+    app_state: &Arc<AppState>,
+    requesting_window: Option<WindowHandle<Workspace>>,
+    cx: &mut AppContext,
+) -> Task<
+    anyhow::Result<(
+        WeakHandle<Workspace>,
+        Vec<Option<Result<Box<dyn ItemHandle>, anyhow::Error>>>,
+    )>,
+> {
+    let app_state = app_state.clone();
+    let abs_paths = abs_paths.to_vec();
+    cx.spawn(|mut cx| async move {
+        // Open paths in existing workspace if possible
+        let existing = activate_workspace_for_project(&mut cx, |project, cx| {
+            project.contains_paths(&abs_paths, cx)
+        })
+        .await;
+
+        if let Some(existing) = existing {
+            Ok((
+                existing.clone(),
+                existing
+                    .update(&mut cx, |workspace, cx| {
+                        workspace.open_paths(abs_paths, true, cx)
+                    })?
+                    .await,
+            ))
+        } else {
+            Ok(cx
+                .update(|cx| {
+                    Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
+                })
+                .await)
+        }
+    })
+}
 
 // pub fn open_new(
 //     app_state: &Arc<AppState>,