From 327a2f99673d3b54c24265cf74d9df876b2adbdd Mon Sep 17 00:00:00 2001 From: Mikayla Date: Mon, 30 Oct 2023 17:43:07 -0700 Subject: [PATCH 1/3] Add the entity trait and implement for models, views, subscriptions, and observations --- 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(-) diff --git a/crates/call2/src/call2.rs b/crates/call2/src/call2.rs index d8678b7ed46d155ece9373a161bd59b0322e7bce..fd09dc31803c63081dea1ebcbe8dc07442d82eba 100644 --- a/crates/call2/src/call2.rs +++ b/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, diff --git a/crates/client2/src/user.rs b/crates/client2/src/user.rs index a8be4b6401d518b406d54b867f689e5bc35b3639..2a8cf34af4aea28ee00d23cb448a5d860a0c682d 100644 --- a/crates/client2/src/user.rs +++ b/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(), diff --git a/crates/copilot2/src/copilot2.rs b/crates/copilot2/src/copilot2.rs index c3107a2f4731897199eb80677f5bc8857c7beb39..083c491656579efe1b9b0cb1df96c0052b8bcd74 100644 --- a/crates/copilot2/src/copilot2.rs +++ b/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}, diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 1d2c17d35757fb83a1997ca5ede8192e854ad69c..8e7a652b4bb3a0976844d0d7f2bcdb74ae2e7e00 100644 --- a/crates/gpui2/src/app.rs +++ b/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( + fn update_model( &mut self, model: &Model, update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs index 71417f2a5e8452f8daef1f40de01aaaa61991203..042a75848e467fd452db80447e477a5c42179965 100644 --- a/crates/gpui2/src/app/async_context.rs +++ b/crates/gpui2/src/app/async_context.rs @@ -32,7 +32,7 @@ impl Context for AsyncAppContext { Ok(lock.build_model(build_model)) } - fn update_entity( + fn update_model( &mut self, handle: &Model, 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( + fn update_model( &mut self, handle: &Model, update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> Result { self.app - .update_window(self.window, |cx| cx.update_entity(handle, update)) + .update_window(self.window, |cx| cx.update_model(handle, update)) } } diff --git a/crates/gpui2/src/app/entity_map.rs b/crates/gpui2/src/app/entity_map.rs index 7e0c5626a52f614ee80b32b78f1db3a70e5ebb01..7b1bc0d000142244a1d3abcdd88204ec7e5cf9c9 100644 --- a/crates/gpui2/src/app/entity_map.rs +++ b/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 { unsafe impl Send for Model {} unsafe impl Sync for Model {} +impl Sealed for Model {} + +impl Entity for Model { + type Weak = WeakModel; + + 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 + where + Self: Sized, + { + Some(Model { + any_model: weak.any_model.upgrade()?, + entity_type: weak.entity_type, + }) + } +} impl Model { fn new(id: EntityId, entity_map: Weak>) -> Self @@ -265,11 +291,12 @@ impl Model { } } + /// Downgrade the this to a weak model reference pub fn downgrade(&self) -> WeakModel { - 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 Model { where C: Context, { - cx.update_entity(self, update) + cx.update_model(self, update) } } @@ -334,7 +361,7 @@ impl Eq for Model {} impl PartialEq> for Model { fn eq(&self, other: &WeakModel) -> bool { - self.entity_id() == other.entity_id() + self.any_model.entity_id() == other.entity_id() } } @@ -415,11 +442,10 @@ impl Clone for WeakModel { } impl WeakModel { + /// Upgrade this weak model reference into a strong model reference pub fn upgrade(&self) -> Option> { - 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 WeakModel { 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 Eq for WeakModel {} impl PartialEq> for WeakModel { fn eq(&self, other: &Model) -> bool { - self.entity_id() == other.entity_id() + self.entity_id() == other.any_model.entity_id() } } diff --git a/crates/gpui2/src/app/model_context.rs b/crates/gpui2/src/app/model_context.rs index 463652886b2767c8c155f068c057afd2befe9851..8a4576c052b6c4355576f2959028b568dae8692d 100644 --- a/crates/gpui2/src/app/model_context.rs +++ b/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 { - 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 { + pub fn weak_model(&self) -> WeakModel { self.model_state.clone() } - pub fn observe( + pub fn observe( &mut self, - handle: &Model, - mut on_notify: impl FnMut(&mut T, Model, &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, { - 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( + pub fn subscribe( &mut self, - handle: &Model, - mut on_event: impl FnMut(&mut T, Model, &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, { - 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( + pub fn observe_release( &mut self, - handle: &Model, - 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, { - 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::(), 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 + 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 + 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 + '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( + fn update_model( &mut self, handle: &Model, update: impl FnOnce(&mut U, &mut Self::ModelContext<'_, U>) -> R, ) -> R { - self.app.update_entity(handle, update) + self.app.update_model(handle, update) } } diff --git a/crates/gpui2/src/app/test_context.rs b/crates/gpui2/src/app/test_context.rs index dc5896fe06ac9912ff052ce459e1fc62c609e911..2b09a95a34b4b60fe5b5792f2dc2bf0333eb7d63 100644 --- a/crates/gpui2/src/app/test_context.rs +++ b/crates/gpui2/src/app/test_context.rs @@ -26,13 +26,13 @@ impl Context for TestAppContext { lock.build_model(build_model) } - fn update_entity( + fn update_model( &mut self, handle: &Model, update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> Self::Result { let mut lock = self.app.lock(); - lock.update_entity(handle, update) + lock.update_model(handle, update) } } diff --git a/crates/gpui2/src/gpui2.rs b/crates/gpui2/src/gpui2.rs index 85300c1a4ab4809dfc4e2093562026005e8323b6..8625866a441159a3b89ce3dade64e3190c074678 100644 --- a/crates/gpui2/src/gpui2.rs +++ b/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( + fn update_model( &mut self, handle: &Model, update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, @@ -104,6 +111,16 @@ pub trait VisualContext: Context { ) -> Self::Result; } +pub trait Entity: Sealed { + type Weak: 'static + Send; + + fn entity_id(&self) -> EntityId; + fn downgrade(&self) -> Self::Weak; + fn upgrade_from(weak: &Self::Weak) -> Option + where + Self: Sized; +} + pub enum GlobalKey { Numeric(usize), View(EntityId), @@ -149,12 +166,12 @@ impl Context for MainThread { }) } - fn update_entity( + fn update_model( &mut self, handle: &Model, update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R, ) -> Self::Result { - self.0.update_entity(handle, |entity, cx| { + self.0.update_model(handle, |entity, cx| { let cx = unsafe { mem::transmute::< &mut C::ModelContext<'_, T>, diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index eef58193613c4d87ec29487cb7f9efc2d9f26e5c..08e64261e1756ae384d05eb92bef150869dbac10 100644 --- a/crates/gpui2/src/view.rs +++ b/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 { pub(crate) model: Model, } +impl Sealed for View {} + impl View { pub fn into_any(self) -> AnyView { AnyView(Arc::new(self)) } } -impl View { - pub fn downgrade(&self) -> WeakView { +impl Entity for View { + type Weak = WeakView; + + 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 + where + Self: Sized, + { + let model = weak.model.upgrade()?; + Some(View { model }) + } +} + +impl View { + /// Convert this strong view reference into a weak view reference. + pub fn downgrade(&self) -> WeakView { + Entity::downgrade(self) + } + pub fn update( &self, cx: &mut C, @@ -111,8 +134,7 @@ pub struct WeakView { impl WeakView { pub fn upgrade(&self) -> Option> { - let model = self.model.upgrade()?; - Some(View { model }) + Entity::upgrade_from(self) } pub fn update( @@ -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::>().unwrap(); element.layout(state, cx) @@ -227,7 +249,7 @@ where } fn paint(&self, _: Bounds, 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::>().unwrap(); element.paint(state, cx); diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index d5e18e14392de98d82e9c70f29c5c040d8bd56b7..7898ac34fce4f8162b47c7f4083f11f58bbea62e 100644 --- a/crates/gpui2/src/window.rs +++ b/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( + fn update_model( &mut self, model: &Model, 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( + pub fn observe( &mut self, - handle: &Model, - mut on_notify: impl FnMut(&mut V, Model, &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, { 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( + pub fn subscribe( &mut self, - handle: &Model, - mut on_event: impl FnMut(&mut V, Model, &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, + { 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( + pub fn observe_release( &mut self, - handle: &Model, - 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, { 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( + fn update_model( &mut self, model: &Model, 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) } } diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index c2ee1718663d37523cb3c56e34ce4fb4fe1f95c6..3f3c5f1308cc76fef9453f66d22b51efcb3dd86c 100644 --- a/crates/project2/src/project2.rs +++ b/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::() { 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::( move |params, mut cx| { diff --git a/crates/project2/src/terminals.rs b/crates/project2/src/terminals.rs index 5cd62d5ae61cf145c13fa7b6c6cf0f446bb96ff8..ce89914dc6adbe36b33f6426df309630fe3933fb 100644 --- a/crates/project2/src/terminals.rs +++ b/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::{ From 6f1197e00c2650a68296f519d85f8a5039660b92 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Mon, 30 Oct 2023 18:00:37 -0700 Subject: [PATCH 2/3] Change model to downcast with ownership --- crates/gpui2/src/app/entity_map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui2/src/app/entity_map.rs b/crates/gpui2/src/app/entity_map.rs index 7b1bc0d000142244a1d3abcdd88204ec7e5cf9c9..840b0831cda82727d7498bfaeb26103a3a42f900 100644 --- a/crates/gpui2/src/app/entity_map.rs +++ b/crates/gpui2/src/app/entity_map.rs @@ -172,7 +172,7 @@ impl AnyModel { } } - pub fn downcast(&self) -> Option> { + pub fn downcast(self) -> Option> { if TypeId::of::() == self.entity_type { Some(Model { any_model: self.clone(), From f5b13071f17c075ac38c4efcf8f0ca18636bb13c Mon Sep 17 00:00:00 2001 From: Mikayla Date: Mon, 30 Oct 2023 18:01:26 -0700 Subject: [PATCH 3/3] experiment with a way to recover the any entities when downcasting fails --- crates/gpui2/src/app/entity_map.rs | 16 ++++++++++++---- crates/gpui2/src/view.rs | 21 +++++++++++++++++++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/crates/gpui2/src/app/entity_map.rs b/crates/gpui2/src/app/entity_map.rs index 840b0831cda82727d7498bfaeb26103a3a42f900..bbeabd3e4fa3ef115742e3b2bb117fa8661437e3 100644 --- a/crates/gpui2/src/app/entity_map.rs +++ b/crates/gpui2/src/app/entity_map.rs @@ -172,14 +172,14 @@ impl AnyModel { } } - pub fn downcast(self) -> Option> { + pub fn downcast(self) -> Result, AnyModel> { if TypeId::of::() == self.entity_type { - Some(Model { - any_model: self.clone(), + Ok(Model { + any_model: self, entity_type: PhantomData, }) } else { - None + Err(self) } } } @@ -243,6 +243,14 @@ impl PartialEq for AnyModel { impl Eq for AnyModel {} +impl std::fmt::Debug for AnyModel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AnyModel") + .field("entity_id", &self.entity_id.as_u64()) + .finish() + } +} + #[derive(Deref, DerefMut)] pub struct Model { #[deref] diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index 08e64261e1756ae384d05eb92bef150869dbac10..3c3ad034b45232ca131acb78dfb2642ece21f282 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -211,6 +211,7 @@ trait ViewObject: Send + Sync { fn initialize(&self, cx: &mut WindowContext) -> AnyBox; fn layout(&self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId; fn paint(&self, bounds: Bounds, element: &mut AnyBox, cx: &mut WindowContext); + fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; } impl ViewObject for View @@ -256,14 +257,24 @@ where }); }); } + + fn debug(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct(&format!("AnyView<{}>", std::any::type_name::())) + .field("entity_id", &ViewObject::entity_id(self).as_u64()) + .finish() + } } #[derive(Clone)] pub struct AnyView(Arc); impl AnyView { - pub fn downcast(self) -> Option> { - self.0.model().downcast().map(|model| View { model }) + pub fn downcast(self) -> Result, AnyView> { + self.0 + .model() + .downcast() + .map(|model| View { model }) + .map_err(|_| self) } pub(crate) fn entity_type(&self) -> TypeId { @@ -326,6 +337,12 @@ impl Element<()> for AnyView { } } +impl std::fmt::Debug for AnyView { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.debug(f) + } +} + struct EraseAnyViewState { view: AnyView, parent_view_state_type: PhantomData,