Add the entity trait and implement for models, views, subscriptions, and observations

Mikayla created

Change summary

crates/call2/src/call2.rs             |  4 
crates/client2/src/user.rs            |  6 +-
crates/copilot2/src/copilot2.rs       |  4 
crates/gpui2/src/app.rs               |  2 
crates/gpui2/src/app/async_context.rs |  8 +-
crates/gpui2/src/app/entity_map.rs    | 52 ++++++++++++++++-----
crates/gpui2/src/app/model_context.rs | 69 ++++++++++++++++------------
crates/gpui2/src/app/test_context.rs  |  4 
crates/gpui2/src/gpui2.rs             | 23 ++++++++-
crates/gpui2/src/view.rs              | 44 +++++++++++++----
crates/gpui2/src/window.rs            | 58 +++++++++++++----------
crates/project2/src/project2.rs       |  8 +-
crates/project2/src/terminals.rs      |  2 
13 files changed, 183 insertions(+), 101 deletions(-)

Detailed changes

crates/call2/src/call2.rs 🔗

@@ -67,8 +67,8 @@ impl ActiveCall {
             incoming_call: watch::channel(),
 
             _subscriptions: vec![
-                client.add_request_handler(cx.weak_handle(), Self::handle_incoming_call),
-                client.add_message_handler(cx.weak_handle(), Self::handle_call_canceled),
+                client.add_request_handler(cx.weak_model(), Self::handle_incoming_call),
+                client.add_message_handler(cx.weak_model(), Self::handle_call_canceled),
             ],
             client,
             user_store,

crates/client2/src/user.rs 🔗

@@ -122,9 +122,9 @@ impl UserStore {
         let (mut current_user_tx, current_user_rx) = watch::channel();
         let (update_contacts_tx, mut update_contacts_rx) = mpsc::unbounded();
         let rpc_subscriptions = vec![
-            client.add_message_handler(cx.weak_handle(), Self::handle_update_contacts),
-            client.add_message_handler(cx.weak_handle(), Self::handle_update_invite_info),
-            client.add_message_handler(cx.weak_handle(), Self::handle_show_contacts),
+            client.add_message_handler(cx.weak_model(), Self::handle_update_contacts),
+            client.add_message_handler(cx.weak_model(), Self::handle_update_invite_info),
+            client.add_message_handler(cx.weak_model(), Self::handle_show_contacts),
         ];
         Self {
             users: Default::default(),

crates/copilot2/src/copilot2.rs 🔗

@@ -7,8 +7,8 @@ use async_tar::Archive;
 use collections::{HashMap, HashSet};
 use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
 use gpui2::{
-    AppContext, AsyncAppContext, Context, EntityId, EventEmitter, Model, ModelContext, Task,
-    WeakModel,
+    AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model, ModelContext,
+    Task, WeakModel,
 };
 use language2::{
     language_settings::{all_language_settings, language_settings},

crates/gpui2/src/app.rs 🔗

@@ -726,7 +726,7 @@ impl Context for AppContext {
 
     /// Update the entity referenced by the given model. The function is passed a mutable reference to the
     /// entity along with a `ModelContext` for the entity.
-    fn update_entity<T: 'static, R>(
+    fn update_model<T: 'static, R>(
         &mut self,
         model: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,

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

@@ -32,7 +32,7 @@ impl Context for AsyncAppContext {
         Ok(lock.build_model(build_model))
     }
 
-    fn update_entity<T: 'static, R>(
+    fn update_model<T: 'static, R>(
         &mut self,
         handle: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
@@ -42,7 +42,7 @@ impl Context for AsyncAppContext {
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
         let mut lock = app.lock(); // Need this to compile
-        Ok(lock.update_entity(handle, update))
+        Ok(lock.update_model(handle, update))
     }
 }
 
@@ -230,13 +230,13 @@ impl Context for AsyncWindowContext {
             .update_window(self.window, |cx| cx.build_model(build_model))
     }
 
-    fn update_entity<T: 'static, R>(
+    fn update_model<T: 'static, R>(
         &mut self,
         handle: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
     ) -> Result<R> {
         self.app
-            .update_window(self.window, |cx| cx.update_entity(handle, update))
+            .update_window(self.window, |cx| cx.update_model(handle, update))
     }
 }
 

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

@@ -1,4 +1,4 @@
-use crate::{AnyBox, AppContext, Context};
+use crate::{private::Sealed, AnyBox, AppContext, Context, Entity};
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};
@@ -253,6 +253,32 @@ pub struct Model<T> {
 
 unsafe impl<T> Send for Model<T> {}
 unsafe impl<T> Sync for Model<T> {}
+impl<T> Sealed for Model<T> {}
+
+impl<T: 'static> Entity<T> for Model<T> {
+    type Weak = WeakModel<T>;
+
+    fn entity_id(&self) -> EntityId {
+        self.any_model.entity_id
+    }
+
+    fn downgrade(&self) -> Self::Weak {
+        WeakModel {
+            any_model: self.any_model.downgrade(),
+            entity_type: self.entity_type,
+        }
+    }
+
+    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        Some(Model {
+            any_model: weak.any_model.upgrade()?,
+            entity_type: weak.entity_type,
+        })
+    }
+}
 
 impl<T: 'static> Model<T> {
     fn new(id: EntityId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self
@@ -265,11 +291,12 @@ impl<T: 'static> Model<T> {
         }
     }
 
+    /// Downgrade the this to a weak model reference
     pub fn downgrade(&self) -> WeakModel<T> {
-        WeakModel {
-            any_model: self.any_model.downgrade(),
-            entity_type: self.entity_type,
-        }
+        // Delegate to the trait implementation to keep behavior in one place.
+        // This method was included to improve method resolution in the presence of
+        // the Model's deref
+        Entity::downgrade(self)
     }
 
     /// Convert this into a dynamically typed model.
@@ -294,7 +321,7 @@ impl<T: 'static> Model<T> {
     where
         C: Context,
     {
-        cx.update_entity(self, update)
+        cx.update_model(self, update)
     }
 }
 
@@ -334,7 +361,7 @@ impl<T> Eq for Model<T> {}
 
 impl<T> PartialEq<WeakModel<T>> for Model<T> {
     fn eq(&self, other: &WeakModel<T>) -> bool {
-        self.entity_id() == other.entity_id()
+        self.any_model.entity_id() == other.entity_id()
     }
 }
 
@@ -415,11 +442,10 @@ impl<T> Clone for WeakModel<T> {
 }
 
 impl<T: 'static> WeakModel<T> {
+    /// Upgrade this weak model reference into a strong model reference
     pub fn upgrade(&self) -> Option<Model<T>> {
-        Some(Model {
-            any_model: self.any_model.upgrade()?,
-            entity_type: self.entity_type,
-        })
+        // Delegate to the trait implementation to keep behavior in one place.
+        Model::upgrade_from(self)
     }
 
     /// Update the entity referenced by this model with the given function if
@@ -441,7 +467,7 @@ impl<T: 'static> WeakModel<T> {
         crate::Flatten::flatten(
             self.upgrade()
                 .ok_or_else(|| anyhow!("entity release"))
-                .map(|this| cx.update_entity(&this, update)),
+                .map(|this| cx.update_model(&this, update)),
         )
     }
 }
@@ -462,6 +488,6 @@ impl<T> Eq for WeakModel<T> {}
 
 impl<T> PartialEq<Model<T>> for WeakModel<T> {
     fn eq(&self, other: &Model<T>) -> bool {
-        self.entity_id() == other.entity_id()
+        self.entity_id() == other.any_model.entity_id()
     }
 }

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

@@ -1,6 +1,6 @@
 use crate::{
-    AppContext, AsyncAppContext, Context, Effect, EntityId, EventEmitter, MainThread, Model,
-    Reference, Subscription, Task, WeakModel,
+    AppContext, AsyncAppContext, Context, Effect, Entity, EntityId, EventEmitter, MainThread,
+    Model, Reference, Subscription, Task, WeakModel,
 };
 use derive_more::{Deref, DerefMut};
 use futures::FutureExt;
@@ -31,29 +31,32 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     }
 
     pub fn handle(&self) -> Model<T> {
-        self.weak_handle()
+        self.weak_model()
             .upgrade()
             .expect("The entity must be alive if we have a model context")
     }
 
-    pub fn weak_handle(&self) -> WeakModel<T> {
+    pub fn weak_model(&self) -> WeakModel<T> {
         self.model_state.clone()
     }
 
-    pub fn observe<T2: 'static>(
+    pub fn observe<T2, E>(
         &mut self,
-        handle: &Model<T2>,
-        mut on_notify: impl FnMut(&mut T, Model<T2>, &mut ModelContext<'_, T>) + Send + 'static,
+        entity: &E,
+        mut on_notify: impl FnMut(&mut T, E, &mut ModelContext<'_, T>) + Send + 'static,
     ) -> Subscription
     where
         T: 'static + Send,
+        T2: 'static,
+        E: Entity<T2>,
     {
-        let this = self.weak_handle();
-        let handle = handle.downgrade();
+        let this = self.weak_model();
+        let entity_id = entity.entity_id();
+        let handle = entity.downgrade();
         self.app.observers.insert(
-            handle.entity_id,
+            entity_id,
             Box::new(move |cx| {
-                if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
+                if let Some((this, handle)) = this.upgrade().zip(E::upgrade_from(&handle)) {
                     this.update(cx, |this, cx| on_notify(this, handle, cx));
                     true
                 } else {
@@ -63,21 +66,24 @@ impl<'a, T: 'static> ModelContext<'a, T> {
         )
     }
 
-    pub fn subscribe<E: 'static + EventEmitter>(
+    pub fn subscribe<T2, E>(
         &mut self,
-        handle: &Model<E>,
-        mut on_event: impl FnMut(&mut T, Model<E>, &E::Event, &mut ModelContext<'_, T>) + Send + 'static,
+        entity: &E,
+        mut on_event: impl FnMut(&mut T, E, &T2::Event, &mut ModelContext<'_, T>) + Send + 'static,
     ) -> Subscription
     where
         T: 'static + Send,
+        T2: 'static + EventEmitter,
+        E: Entity<T2>,
     {
-        let this = self.weak_handle();
-        let handle = handle.downgrade();
+        let this = self.weak_model();
+        let entity_id = entity.entity_id();
+        let entity = entity.downgrade();
         self.app.event_listeners.insert(
-            handle.entity_id,
+            entity_id,
             Box::new(move |event, cx| {
-                let event: &E::Event = event.downcast_ref().expect("invalid event type");
-                if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
+                let event: &T2::Event = event.downcast_ref().expect("invalid event type");
+                if let Some((this, handle)) = this.upgrade().zip(E::upgrade_from(&entity)) {
                     this.update(cx, |this, cx| on_event(this, handle, event, cx));
                     true
                 } else {
@@ -103,17 +109,20 @@ impl<'a, T: 'static> ModelContext<'a, T> {
         )
     }
 
-    pub fn observe_release<E: 'static>(
+    pub fn observe_release<T2, E>(
         &mut self,
-        handle: &Model<E>,
-        mut on_release: impl FnMut(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + 'static,
+        entity: &E,
+        mut on_release: impl FnMut(&mut T, &mut T2, &mut ModelContext<'_, T>) + Send + 'static,
     ) -> Subscription
     where
         T: Any + Send,
+        T2: 'static,
+        E: Entity<T2>,
     {
-        let this = self.weak_handle();
+        let entity_id = entity.entity_id();
+        let this = self.weak_model();
         self.app.release_listeners.insert(
-            handle.entity_id,
+            entity_id,
             Box::new(move |entity, cx| {
                 let entity = entity.downcast_mut().expect("invalid entity type");
                 if let Some(this) = this.upgrade() {
@@ -130,7 +139,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
     where
         T: 'static + Send,
     {
-        let handle = self.weak_handle();
+        let handle = self.weak_model();
         self.global_observers.insert(
             TypeId::of::<G>(),
             Box::new(move |cx| handle.update(cx, |view, cx| f(view, cx)).is_ok()),
@@ -145,7 +154,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
         Fut: 'static + Future<Output = ()> + Send,
         T: 'static + Send,
     {
-        let handle = self.weak_handle();
+        let handle = self.weak_model();
         self.app.quit_observers.insert(
             (),
             Box::new(move |cx| {
@@ -191,7 +200,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
         Fut: Future<Output = R> + Send + 'static,
         R: Send + 'static,
     {
-        let this = self.weak_handle();
+        let this = self.weak_model();
         self.app.spawn(|cx| f(this, cx))
     }
 
@@ -203,7 +212,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
         Fut: Future<Output = R> + 'static,
         R: Send + 'static,
     {
-        let this = self.weak_handle();
+        let this = self.weak_model();
         self.app.spawn_on_main(|cx| f(this, cx))
     }
 }
@@ -235,12 +244,12 @@ impl<'a, T> Context for ModelContext<'a, T> {
         self.app.build_model(build_model)
     }
 
-    fn update_entity<U: 'static, R>(
+    fn update_model<U: 'static, R>(
         &mut self,
         handle: &Model<U>,
         update: impl FnOnce(&mut U, &mut Self::ModelContext<'_, U>) -> R,
     ) -> R {
-        self.app.update_entity(handle, update)
+        self.app.update_model(handle, update)
     }
 }
 

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

@@ -26,13 +26,13 @@ impl Context for TestAppContext {
         lock.build_model(build_model)
     }
 
-    fn update_entity<T: 'static, R>(
+    fn update_model<T: 'static, R>(
         &mut self,
         handle: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
     ) -> Self::Result<R> {
         let mut lock = self.app.lock();
-        lock.update_entity(handle, update)
+        lock.update_model(handle, update)
     }
 }
 

crates/gpui2/src/gpui2.rs 🔗

@@ -24,6 +24,12 @@ mod util;
 mod view;
 mod window;
 
+mod private {
+    /// A mechanism for restricting implementations of a trait to only those in GPUI.
+    /// See: https://predr.ag/blog/definitive-guide-to-sealed-traits-in-rust/
+    pub trait Sealed {}
+}
+
 pub use action::*;
 pub use anyhow::Result;
 pub use app::*;
@@ -39,6 +45,7 @@ pub use image_cache::*;
 pub use interactive::*;
 pub use keymap::*;
 pub use platform::*;
+use private::Sealed;
 pub use refineable::*;
 pub use scene::*;
 pub use serde;
@@ -80,7 +87,7 @@ pub trait Context {
     where
         T: 'static + Send;
 
-    fn update_entity<T: 'static, R>(
+    fn update_model<T: 'static, R>(
         &mut self,
         handle: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
@@ -104,6 +111,16 @@ pub trait VisualContext: Context {
     ) -> Self::Result<R>;
 }
 
+pub trait Entity<T>: Sealed {
+    type Weak: 'static + Send;
+
+    fn entity_id(&self) -> EntityId;
+    fn downgrade(&self) -> Self::Weak;
+    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    where
+        Self: Sized;
+}
+
 pub enum GlobalKey {
     Numeric(usize),
     View(EntityId),
@@ -149,12 +166,12 @@ impl<C: Context> Context for MainThread<C> {
         })
     }
 
-    fn update_entity<T: 'static, R>(
+    fn update_model<T: 'static, R>(
         &mut self,
         handle: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
     ) -> Self::Result<R> {
-        self.0.update_entity(handle, |entity, cx| {
+        self.0.update_model(handle, |entity, cx| {
             let cx = unsafe {
                 mem::transmute::<
                     &mut C::ModelContext<'_, T>,

crates/gpui2/src/view.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
-    AnyBox, AnyElement, AnyModel, AppContext, AvailableSpace, BorrowWindow, Bounds, Component,
-    Element, ElementId, EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext,
-    WeakModel, WindowContext,
+    private::Sealed, AnyBox, AnyElement, AnyModel, AppContext, AvailableSpace, BorrowWindow,
+    Bounds, Component, Element, ElementId, Entity, EntityId, LayoutId, Model, Pixels, Size,
+    ViewContext, VisualContext, WeakModel, WindowContext,
 };
 use anyhow::{Context, Result};
 use std::{any::TypeId, marker::PhantomData, sync::Arc};
@@ -16,19 +16,42 @@ pub struct View<V> {
     pub(crate) model: Model<V>,
 }
 
+impl<V> Sealed for View<V> {}
+
 impl<V: Render> View<V> {
     pub fn into_any(self) -> AnyView {
         AnyView(Arc::new(self))
     }
 }
 
-impl<V: 'static> View<V> {
-    pub fn downgrade(&self) -> WeakView<V> {
+impl<V: 'static> Entity<V> for View<V> {
+    type Weak = WeakView<V>;
+
+    fn entity_id(&self) -> EntityId {
+        self.model.entity_id
+    }
+
+    fn downgrade(&self) -> Self::Weak {
         WeakView {
             model: self.model.downgrade(),
         }
     }
 
+    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        let model = weak.model.upgrade()?;
+        Some(View { model })
+    }
+}
+
+impl<V: 'static> View<V> {
+    /// Convert this strong view reference into a weak view reference.
+    pub fn downgrade(&self) -> WeakView<V> {
+        Entity::downgrade(self)
+    }
+
     pub fn update<C, R>(
         &self,
         cx: &mut C,
@@ -111,8 +134,7 @@ pub struct WeakView<V> {
 
 impl<V: 'static> WeakView<V> {
     pub fn upgrade(&self) -> Option<View<V>> {
-        let model = self.model.upgrade()?;
-        Some(View { model })
+        Entity::upgrade_from(self)
     }
 
     pub fn update<R>(
@@ -200,7 +222,7 @@ where
     }
 
     fn entity_id(&self) -> EntityId {
-        self.model.entity_id
+        Entity::entity_id(self)
     }
 
     fn model(&self) -> AnyModel {
@@ -208,7 +230,7 @@ where
     }
 
     fn initialize(&self, cx: &mut WindowContext) -> AnyBox {
-        cx.with_element_id(self.entity_id(), |_global_id, cx| {
+        cx.with_element_id(ViewObject::entity_id(self), |_global_id, cx| {
             self.update(cx, |state, cx| {
                 let mut any_element = Box::new(AnyElement::new(state.render(cx)));
                 any_element.initialize(state, cx);
@@ -218,7 +240,7 @@ where
     }
 
     fn layout(&self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId {
-        cx.with_element_id(self.entity_id(), |_global_id, cx| {
+        cx.with_element_id(ViewObject::entity_id(self), |_global_id, cx| {
             self.update(cx, |state, cx| {
                 let element = element.downcast_mut::<AnyElement<V>>().unwrap();
                 element.layout(state, cx)
@@ -227,7 +249,7 @@ where
     }
 
     fn paint(&self, _: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext) {
-        cx.with_element_id(self.entity_id(), |_global_id, cx| {
+        cx.with_element_id(ViewObject::entity_id(self), |_global_id, cx| {
             self.update(cx, |state, cx| {
                 let element = element.downcast_mut::<AnyElement<V>>().unwrap();
                 element.paint(state, cx);

crates/gpui2/src/window.rs 🔗

@@ -1,8 +1,8 @@
 use crate::{
     px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
     Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect,
-    EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla,
-    ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
+    Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId,
+    Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
     MainThread, MainThreadOnly, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
     MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow,
     Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams,
@@ -1253,7 +1253,7 @@ impl Context for WindowContext<'_, '_> {
         self.entities.insert(slot, model)
     }
 
-    fn update_entity<T: 'static, R>(
+    fn update_model<T: 'static, R>(
         &mut self,
         model: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
@@ -1568,23 +1568,25 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
         self.window_cx.on_next_frame(move |cx| view.update(cx, f));
     }
 
-    pub fn observe<E>(
+    pub fn observe<V2, E>(
         &mut self,
-        handle: &Model<E>,
-        mut on_notify: impl FnMut(&mut V, Model<E>, &mut ViewContext<'_, '_, V>) + Send + 'static,
+        entity: &E,
+        mut on_notify: impl FnMut(&mut V, E, &mut ViewContext<'_, '_, V>) + Send + 'static,
     ) -> Subscription
     where
-        E: 'static,
+        V2: 'static,
         V: Any + Send,
+        E: Entity<V2>,
     {
         let view = self.view();
-        let handle = handle.downgrade();
+        let entity_id = entity.entity_id();
+        let entity = entity.downgrade();
         let window_handle = self.window.handle;
         self.app.observers.insert(
-            handle.entity_id,
+            entity_id,
             Box::new(move |cx| {
                 cx.update_window(window_handle.id, |cx| {
-                    if let Some(handle) = handle.upgrade() {
+                    if let Some(handle) = E::upgrade_from(&entity) {
                         view.update(cx, |this, cx| on_notify(this, handle, cx))
                             .is_ok()
                     } else {
@@ -1596,21 +1598,24 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
         )
     }
 
-    pub fn subscribe<E: EventEmitter>(
+    pub fn subscribe<V2, E>(
         &mut self,
-        handle: &Model<E>,
-        mut on_event: impl FnMut(&mut V, Model<E>, &E::Event, &mut ViewContext<'_, '_, V>)
-            + Send
-            + 'static,
-    ) -> Subscription {
+        entity: &E,
+        mut on_event: impl FnMut(&mut V, E, &V2::Event, &mut ViewContext<'_, '_, V>) + Send + 'static,
+    ) -> Subscription
+    where
+        V2: EventEmitter,
+        E: Entity<V2>,
+    {
         let view = self.view();
-        let handle = handle.downgrade();
+        let entity_id = entity.entity_id();
+        let handle = entity.downgrade();
         let window_handle = self.window.handle;
         self.app.event_listeners.insert(
-            handle.entity_id,
+            entity_id,
             Box::new(move |event, cx| {
                 cx.update_window(window_handle.id, |cx| {
-                    if let Some(handle) = handle.upgrade() {
+                    if let Some(handle) = E::upgrade_from(&handle) {
                         let event = event.downcast_ref().expect("invalid event type");
                         view.update(cx, |this, cx| on_event(this, handle, event, cx))
                             .is_ok()
@@ -1638,18 +1643,21 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
         )
     }
 
-    pub fn observe_release<T: 'static>(
+    pub fn observe_release<V2, E>(
         &mut self,
-        handle: &Model<T>,
-        mut on_release: impl FnMut(&mut V, &mut T, &mut ViewContext<'_, '_, V>) + Send + 'static,
+        entity: &E,
+        mut on_release: impl FnMut(&mut V, &mut V2, &mut ViewContext<'_, '_, V>) + Send + 'static,
     ) -> Subscription
     where
         V: Any + Send,
+        V2: 'static,
+        E: Entity<V2>,
     {
         let view = self.view();
+        let entity_id = entity.entity_id();
         let window_handle = self.window.handle;
         self.app.release_listeners.insert(
-            handle.entity_id,
+            entity_id,
             Box::new(move |entity, cx| {
                 let entity = entity.downcast_mut().expect("invalid entity type");
                 let _ = cx.update_window(window_handle.id, |cx| {
@@ -1864,12 +1872,12 @@ impl<'a, 'w, V> Context for ViewContext<'a, 'w, V> {
         self.window_cx.build_model(build_model)
     }
 
-    fn update_entity<T: 'static, R>(
+    fn update_model<T: 'static, R>(
         &mut self,
         model: &Model<T>,
         update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
     ) -> R {
-        self.window_cx.update_entity(model, update)
+        self.window_cx.update_model(model, update)
     }
 }
 

crates/project2/src/project2.rs 🔗

@@ -26,8 +26,8 @@ use futures::{
 };
 use globset::{Glob, GlobSet, GlobSetBuilder};
 use gpui2::{
-    AnyModel, AppContext, AsyncAppContext, Context, EventEmitter, Executor, Model, ModelContext,
-    Task, WeakModel,
+    AnyModel, AppContext, AsyncAppContext, Context, Entity, EventEmitter, Executor, Model,
+    ModelContext, Task, WeakModel,
 };
 use itertools::Itertools;
 use language2::{
@@ -2491,7 +2491,7 @@ impl Project {
             delay
         } else {
             if first_insertion {
-                let this = cx.weak_handle();
+                let this = cx.weak_model();
                 cx.defer(move |cx| {
                     if let Some(this) = this.upgrade() {
                         this.update(cx, |this, cx| {
@@ -8650,7 +8650,7 @@ fn subscribe_for_copilot_events(
                         // Another event wants to re-add the server that was already added and subscribed to, avoid doing it again.
                         if !copilot_server.has_notification_handler::<copilot2::request::LogMessage>() {
                             let new_server_id = copilot_server.server_id();
-                            let weak_project = cx.weak_handle();
+                            let weak_project = cx.weak_model();
                             let copilot_log_subscription = copilot_server
                                 .on_notification::<copilot2::request::LogMessage, _>(
                                     move |params, mut cx| {

crates/project2/src/terminals.rs 🔗

@@ -1,5 +1,5 @@
 use crate::Project;
-use gpui2::{AnyWindowHandle, Context, Model, ModelContext, WeakModel};
+use gpui2::{AnyWindowHandle, Context, Entity, Model, ModelContext, WeakModel};
 use settings2::Settings;
 use std::path::{Path, PathBuf};
 use terminal2::{