WIP: Try to store an Rc<RefCell> inside entity handles

This commit is contained in:
Nathan Sobo 2025-07-08 21:27:09 -06:00
parent df57754baf
commit 6256e80933
10 changed files with 123 additions and 130 deletions

View file

@ -402,7 +402,7 @@ impl AppContext for ExampleContext {
self.app.new(build_entity) self.app.new(build_entity)
} }
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<gpui::Reservation<T>> { fn reserve_entity(&mut self) -> Self::Result<gpui::Reservation<T>> {
self.app.reserve_entity() self.app.reserve_entity()
} }

View file

@ -38,9 +38,9 @@ use crate::{
EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global, KeyBinding, KeyContext, EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global, KeyBinding, KeyContext,
Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
PlatformDisplay, PlatformKeyboardLayout, Point, PromptBuilder, PromptButton, PromptHandle, PlatformDisplay, PlatformKeyboardLayout, Point, PromptBuilder, PromptButton, PromptHandle,
PromptLevel, Render, RenderImage, RenderablePromptHandle, Reservation, ScreenCaptureSource, PromptLevel, Render, RenderImage, RenderablePromptHandle, ScreenCaptureSource, SubscriberSet,
SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance, Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance, WindowHandle, WindowId,
WindowHandle, WindowId, WindowInvalidator, WindowInvalidator,
colors::{Colors, GlobalColors}, colors::{Colors, GlobalColors},
current_platform, hash, init_app_menus, current_platform, hash, init_app_menus,
}; };
@ -930,11 +930,11 @@ impl App {
break; break;
} }
for (entity_id, mut entity) in dropped { for (entity_id, entity) in dropped {
self.observers.remove(&entity_id); self.observers.remove(&entity_id);
self.event_listeners.remove(&entity_id); self.event_listeners.remove(&entity_id);
for release_callback in self.release_listeners.remove(&entity_id) { for release_callback in self.release_listeners.remove(&entity_id) {
release_callback(entity.as_mut(), self); release_callback(entity.borrow_mut().deref_mut(), self);
} }
} }
} }
@ -1746,9 +1746,9 @@ impl AppContext for App {
/// [`Entity`] handle will be returned, which can be used to access the entity in a context. /// [`Entity`] handle will be returned, which can be used to access the entity in a context.
fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T> { fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T> {
self.update(|cx| { self.update(|cx| {
let slot = cx.entities.reserve(); let entity_id = cx.entities.reserve();
let handle = slot.clone(); let entity = build_entity(&mut Context::new_context(cx, entity_id, None));
let entity = build_entity(&mut Context::new_context(cx, slot.downgrade())); let handle = cx.entities.insert(entity_id, entity);
cx.push_effect(Effect::EntityCreated { cx.push_effect(Effect::EntityCreated {
entity: handle.clone().into_any(), entity: handle.clone().into_any(),
@ -1756,24 +1756,22 @@ impl AppContext for App {
window: cx.window_update_stack.last().cloned(), window: cx.window_update_stack.last().cloned(),
}); });
cx.entities.insert(slot, entity);
handle handle
}) })
} }
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>> { fn reserve_entity(&mut self) -> Self::Result<EntityId> {
Reservation(self.entities.reserve()) self.entities.reserve()
} }
fn insert_entity<T: 'static>( fn insert_entity<T: 'static>(
&mut self, &mut self,
reservation: Reservation<T>, entity_id: EntityId,
build_entity: impl FnOnce(&mut Context<T>) -> T, build_entity: impl FnOnce(&mut Context<T>) -> T,
) -> Self::Result<Entity<T>> { ) -> Self::Result<Entity<T>> {
self.update(|cx| { self.update(|cx| {
let slot = reservation.0; let entity = build_entity(&mut Context::new_context(cx, entity_id, None));
let entity = build_entity(&mut Context::new_context(cx, slot.downgrade())); cx.entities.insert(entity_id, entity)
cx.entities.insert(slot, entity)
}) })
} }
@ -1785,13 +1783,11 @@ impl AppContext for App {
update: impl FnOnce(&mut T, &mut Context<T>) -> R, update: impl FnOnce(&mut T, &mut Context<T>) -> R,
) -> R { ) -> R {
self.update(|cx| { self.update(|cx| {
let mut entity = cx.entities.lease(handle); let mut entity = cx.entities.get(handle.entity_id());
let result = update( update(
&mut entity, entity.borrow_mut().downcast_mut().unwrap(),
&mut Context::new_context(cx, handle.downgrade()), &mut Context::new_context(cx, handle.entity_id(), Some(handle.downgrade())),
); )
cx.entities.end_lease(entity);
result
}) })
} }

View file

@ -1,7 +1,8 @@
use crate::{ use crate::{
AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext,
Entity, EventEmitter, Focusable, ForegroundExecutor, Global, PromptButton, PromptLevel, Render, Entity, EntityId, EventEmitter, Focusable, ForegroundExecutor, Global, PromptButton,
Reservation, Result, Subscription, Task, VisualContext, Window, WindowHandle, PromptLevel, Render, Result, Subscription, Task, VisualContext, Window, WindowHandle,
app::NewEntityListener,
}; };
use anyhow::Context as _; use anyhow::Context as _;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -32,7 +33,7 @@ impl AppContext for AsyncApp {
Ok(app.new(build_entity)) Ok(app.new(build_entity))
} }
fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> { fn reserve_entity(&mut self) -> Result<EntityId> {
let app = self.app.upgrade().context("app was released")?; let app = self.app.upgrade().context("app was released")?;
let mut app = app.borrow_mut(); let mut app = app.borrow_mut();
Ok(app.reserve_entity()) Ok(app.reserve_entity())
@ -40,12 +41,12 @@ impl AppContext for AsyncApp {
fn insert_entity<T: 'static>( fn insert_entity<T: 'static>(
&mut self, &mut self,
reservation: Reservation<T>, entity_id: EntityId,
build_entity: impl FnOnce(&mut Context<T>) -> T, build_entity: impl FnOnce(&mut Context<T>) -> T,
) -> Result<Entity<T>> { ) -> Result<Entity<T>> {
let app = self.app.upgrade().context("app was released")?; let app = self.app.upgrade().context("app was released")?;
let mut app = app.borrow_mut(); let mut app = app.borrow_mut();
Ok(app.insert_entity(reservation, build_entity)) Ok(app.insert_entity(entity_id, build_entity))
} }
fn update_entity<T: 'static, R>( fn update_entity<T: 'static, R>(
@ -342,17 +343,17 @@ impl AppContext for AsyncWindowContext {
self.window.update(self, |_, _, cx| cx.new(build_entity)) self.window.update(self, |_, _, cx| cx.new(build_entity))
} }
fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> { fn reserve_entity(&mut self) -> Result<EntityId> {
self.window.update(self, |_, _, cx| cx.reserve_entity()) self.window.update(self, |_, _, cx| cx.reserve_entity())
} }
fn insert_entity<T: 'static>( fn insert_entity<T: 'static>(
&mut self, &mut self,
reservation: Reservation<T>, entity_id: EntityId,
build_entity: impl FnOnce(&mut Context<T>) -> T, build_entity: impl FnOnce(&mut Context<T>) -> T,
) -> Self::Result<Entity<T>> { ) -> Self::Result<Entity<T>> {
self.window self.window
.update(self, |_, _, cx| cx.insert_entity(reservation, build_entity)) .update(self, |_, _, cx| cx.insert_entity(entity_id, build_entity))
} }
fn update_entity<T: 'static, R>( fn update_entity<T: 'static, R>(

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
AnyView, AnyWindowHandle, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, EventEmitter, AnyView, AnyWindowHandle, AppContext, AsyncApp, DispatchPhase, Effect, EntityId, EventEmitter,
FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, Reservation, SubscriberSet, FocusHandle, FocusOutEvent, Focusable, Global, KeystrokeObserver, SubscriberSet, Subscription,
Subscription, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle, Task, WeakEntity, WeakFocusHandle, Window, WindowHandle,
}; };
use anyhow::Result; use anyhow::Result;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -22,17 +22,26 @@ pub struct Context<'a, T> {
#[deref] #[deref]
#[deref_mut] #[deref_mut]
app: &'a mut App, app: &'a mut App,
entity_state: WeakEntity<T>, entity_id: EntityId,
entity_state: Option<WeakEntity<T>>,
} }
impl<'a, T: 'static> Context<'a, T> { impl<'a, T: 'static> Context<'a, T> {
pub(crate) fn new_context(app: &'a mut App, entity_state: WeakEntity<T>) -> Self { pub(crate) fn new_context(
Self { app, entity_state } app: &'a mut App,
entity_id: EntityId,
entity_state: Option<WeakEntity<T>>,
) -> Self {
Self {
app,
entity_id,
entity_state,
}
} }
/// The entity id of the entity backing this context. /// The entity id of the entity backing this context.
pub fn entity_id(&self) -> EntityId { pub fn entity_id(&self) -> EntityId {
self.entity_state.entity_id self.entity_id
} }
/// Returns a handle to the entity belonging to this context. /// Returns a handle to the entity belonging to this context.
@ -44,7 +53,7 @@ impl<'a, T: 'static> Context<'a, T> {
/// Returns a weak handle to the entity belonging to this context. /// Returns a weak handle to the entity belonging to this context.
pub fn weak_entity(&self) -> WeakEntity<T> { pub fn weak_entity(&self) -> WeakEntity<T> {
self.entity_state.clone() self.entity_state.as_ref().unwrap().clone()
} }
/// Arranges for the given function to be called whenever [`Context::notify`] is /// Arranges for the given function to be called whenever [`Context::notify`] is
@ -112,7 +121,7 @@ impl<'a, T: 'static> Context<'a, T> {
T: 'static, T: 'static,
{ {
let (subscription, activate) = self.app.release_listeners.insert( let (subscription, activate) = self.app.release_listeners.insert(
self.entity_state.entity_id, self.entity_id,
Box::new(move |this, cx| { Box::new(move |this, cx| {
let this = this.downcast_mut().expect("invalid entity type"); let this = this.downcast_mut().expect("invalid entity type");
on_release(this, cx); on_release(this, cx);
@ -193,7 +202,7 @@ impl<'a, T: 'static> Context<'a, T> {
/// Tell GPUI that this entity has changed and observers of it should be notified. /// Tell GPUI that this entity has changed and observers of it should be notified.
pub fn notify(&mut self) { pub fn notify(&mut self) {
self.app.notify(self.entity_state.entity_id); self.app.notify(self.entity_id);
} }
/// Spawn the future returned by the given function. /// Spawn the future returned by the given function.
@ -692,7 +701,7 @@ impl<T> Context<'_, T> {
Evt: 'static, Evt: 'static,
{ {
self.app.pending_effects.push_back(Effect::Emit { self.app.pending_effects.push_back(Effect::Emit {
emitter: self.entity_state.entity_id, emitter: self.entity_id,
event_type: TypeId::of::<Evt>(), event_type: TypeId::of::<Evt>(),
event: Box::new(event), event: Box::new(event),
}); });
@ -706,16 +715,16 @@ impl<T> AppContext for Context<'_, T> {
self.app.new(build_entity) self.app.new(build_entity)
} }
fn reserve_entity<U: 'static>(&mut self) -> Reservation<U> { fn reserve_entity(&mut self) -> EntityId {
self.app.reserve_entity() self.app.reserve_entity()
} }
fn insert_entity<U: 'static>( fn insert_entity<U: 'static>(
&mut self, &mut self,
reservation: Reservation<U>, entity_id: EntityId,
build_entity: impl FnOnce(&mut Context<U>) -> U, build_entity: impl FnOnce(&mut Context<U>) -> U,
) -> Self::Result<Entity<U>> { ) -> Self::Result<Entity<U>> {
self.app.insert_entity(reservation, build_entity) self.app.insert_entity(entity_id, build_entity)
} }
fn update_entity<U: 'static, R>( fn update_entity<U: 'static, R>(

View file

@ -13,6 +13,7 @@ use std::{
marker::PhantomData, marker::PhantomData,
mem, mem,
num::NonZeroU64, num::NonZeroU64,
rc::{self, Rc},
sync::{ sync::{
Arc, Weak, Arc, Weak,
atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst}, atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},
@ -55,9 +56,9 @@ impl Display for EntityId {
} }
pub(crate) struct EntityMap { pub(crate) struct EntityMap {
entities: SecondaryMap<EntityId, Box<dyn Any>>, entities: SecondaryMap<EntityId, Rc<RefCell<dyn Any>>>,
pub accessed_entities: RefCell<FxHashSet<EntityId>>, pub accessed_entities: RefCell<FxHashSet<EntityId>>,
ref_counts: Arc<RwLock<EntityRefCounts>>, pub(crate) ref_counts: Arc<RwLock<EntityRefCounts>>,
} }
struct EntityRefCounts { struct EntityRefCounts {
@ -85,47 +86,28 @@ impl EntityMap {
} }
/// Reserve a slot for an entity, which you can subsequently use with `insert`. /// Reserve a slot for an entity, which you can subsequently use with `insert`.
pub fn reserve<T: 'static>(&self) -> Slot<T> { pub fn reserve(&self) -> EntityId {
let id = self.ref_counts.write().counts.insert(1.into()); self.ref_counts.write().counts.insert(1.into())
Slot(Entity::new(id, Arc::downgrade(&self.ref_counts)))
} }
/// Insert an entity into a slot obtained by calling `reserve`. /// Insert an entity into a slot obtained by calling `reserve`.
pub fn insert<T>(&mut self, slot: Slot<T>, entity: T) -> Entity<T> pub fn insert<T>(&mut self, entity_id: EntityId, entity: T) -> Entity<T>
where where
T: 'static, T: 'static,
{ {
let mut accessed_entities = self.accessed_entities.borrow_mut(); let mut accessed_entities = self.accessed_entities.borrow_mut();
accessed_entities.insert(slot.entity_id); accessed_entities.insert(entity_id);
let handle = slot.0; let entity_data = Rc::new(RefCell::new(entity));
self.entities.insert(handle.entity_id, Box::new(entity)); self.entities.insert(entity_id, entity_data.clone());
handle
Entity::new(entity_id, entity_data, Arc::downgrade(&self.ref_counts))
} }
/// Move an entity to the stack. pub fn get(&self, entity_id: EntityId) -> Rc<RefCell<dyn Any>> {
#[track_caller]
pub fn lease<'a, T>(&mut self, pointer: &'a Entity<T>) -> Lease<'a, T> {
self.assert_valid_context(pointer);
let mut accessed_entities = self.accessed_entities.borrow_mut(); let mut accessed_entities = self.accessed_entities.borrow_mut();
accessed_entities.insert(pointer.entity_id); accessed_entities.insert(entity_id);
self.entities.get(entity_id).unwrap().clone()
let entity = Some(
self.entities
.remove(pointer.entity_id)
.unwrap_or_else(|| double_lease_panic::<T>("update")),
);
Lease {
entity,
pointer,
entity_type: PhantomData,
}
}
/// Returns an entity after moving it to the stack.
pub fn end_lease<T>(&mut self, mut lease: Lease<T>) {
self.entities
.insert(lease.pointer.entity_id, lease.entity.take().unwrap());
} }
pub fn read<T: 'static>(&self, entity: &Entity<T>) -> &T { pub fn read<T: 'static>(&self, entity: &Entity<T>) -> &T {
@ -133,15 +115,16 @@ impl EntityMap {
let mut accessed_entities = self.accessed_entities.borrow_mut(); let mut accessed_entities = self.accessed_entities.borrow_mut();
accessed_entities.insert(entity.entity_id); accessed_entities.insert(entity.entity_id);
self.entities // self.entities
.get(entity.entity_id) // .get(entity.entity_id)
.and_then(|entity| entity.downcast_ref()) // .and_then(|entity| entity.borrow().downcast_ref())
.unwrap_or_else(|| double_lease_panic::<T>("read")) // .unwrap_or_else(|| double_lease_panic::<T>("read"))
todo!("interface will need to change here")
} }
fn assert_valid_context(&self, entity: &AnyEntity) { fn assert_valid_context(&self, entity: &AnyEntity) {
debug_assert!( debug_assert!(
Weak::ptr_eq(&entity.entity_map, &Arc::downgrade(&self.ref_counts)), Weak::ptr_eq(&entity.ref_counts, &Arc::downgrade(&self.ref_counts)),
"used a entity with the wrong context" "used a entity with the wrong context"
); );
} }
@ -156,7 +139,7 @@ impl EntityMap {
self.accessed_entities.borrow_mut().clear(); self.accessed_entities.borrow_mut().clear();
} }
pub fn take_dropped(&mut self) -> Vec<(EntityId, Box<dyn Any>)> { pub fn take_dropped(&mut self) -> Vec<(EntityId, Rc<RefCell<dyn Any>>)> {
let mut ref_counts = self.ref_counts.write(); let mut ref_counts = self.ref_counts.write();
let dropped_entity_ids = mem::take(&mut ref_counts.dropped_entity_ids); let dropped_entity_ids = mem::take(&mut ref_counts.dropped_entity_ids);
let mut accessed_entities = self.accessed_entities.borrow_mut(); let mut accessed_entities = self.accessed_entities.borrow_mut();
@ -222,19 +205,26 @@ pub(crate) struct Slot<T>(Entity<T>);
pub struct AnyEntity { pub struct AnyEntity {
pub(crate) entity_id: EntityId, pub(crate) entity_id: EntityId,
pub(crate) entity_type: TypeId, pub(crate) entity_type: TypeId,
entity_map: Weak<RwLock<EntityRefCounts>>, entity_data: Rc<RefCell<dyn Any>>,
ref_counts: Weak<RwLock<EntityRefCounts>>,
#[cfg(any(test, feature = "leak-detection"))] #[cfg(any(test, feature = "leak-detection"))]
handle_id: HandleId, handle_id: HandleId,
} }
impl AnyEntity { impl AnyEntity {
fn new(id: EntityId, entity_type: TypeId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self { fn new(
id: EntityId,
entity_type: TypeId,
entity_data: Rc<RefCell<dyn Any>>,
ref_counts: Weak<RwLock<EntityRefCounts>>,
) -> Self {
Self { Self {
entity_id: id, entity_id: id,
entity_type, entity_type,
entity_map: entity_map.clone(), entity_data,
ref_counts: ref_counts.clone(),
#[cfg(any(test, feature = "leak-detection"))] #[cfg(any(test, feature = "leak-detection"))]
handle_id: entity_map handle_id: ref_counts
.upgrade() .upgrade()
.unwrap() .unwrap()
.write() .write()
@ -258,7 +248,8 @@ impl AnyEntity {
AnyWeakEntity { AnyWeakEntity {
entity_id: self.entity_id, entity_id: self.entity_id,
entity_type: self.entity_type, entity_type: self.entity_type,
entity_ref_counts: self.entity_map.clone(), entity_data: Rc::downgrade(&self.entity_data),
entity_ref_counts: self.ref_counts.clone(),
} }
} }
@ -278,7 +269,7 @@ impl AnyEntity {
impl Clone for AnyEntity { impl Clone for AnyEntity {
fn clone(&self) -> Self { fn clone(&self) -> Self {
if let Some(entity_map) = self.entity_map.upgrade() { if let Some(entity_map) = self.ref_counts.upgrade() {
let entity_map = entity_map.read(); let entity_map = entity_map.read();
let count = entity_map let count = entity_map
.counts .counts
@ -291,10 +282,11 @@ impl Clone for AnyEntity {
Self { Self {
entity_id: self.entity_id, entity_id: self.entity_id,
entity_type: self.entity_type, entity_type: self.entity_type,
entity_map: self.entity_map.clone(), entity_data: self.entity_data.clone(),
ref_counts: self.ref_counts.clone(),
#[cfg(any(test, feature = "leak-detection"))] #[cfg(any(test, feature = "leak-detection"))]
handle_id: self handle_id: self
.entity_map .ref_counts
.upgrade() .upgrade()
.unwrap() .unwrap()
.write() .write()
@ -306,7 +298,7 @@ impl Clone for AnyEntity {
impl Drop for AnyEntity { impl Drop for AnyEntity {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(entity_map) = self.entity_map.upgrade() { if let Some(entity_map) = self.ref_counts.upgrade() {
let entity_map = entity_map.upgradable_read(); let entity_map = entity_map.upgradable_read();
let count = entity_map let count = entity_map
.counts .counts
@ -322,7 +314,7 @@ impl Drop for AnyEntity {
} }
#[cfg(any(test, feature = "leak-detection"))] #[cfg(any(test, feature = "leak-detection"))]
if let Some(entity_map) = self.entity_map.upgrade() { if let Some(entity_map) = self.ref_counts.upgrade() {
entity_map entity_map
.write() .write()
.leak_detector .leak_detector
@ -386,12 +378,16 @@ unsafe impl<T> Sync for Entity<T> {}
impl<T> Sealed for Entity<T> {} impl<T> Sealed for Entity<T> {}
impl<T: 'static> Entity<T> { impl<T: 'static> Entity<T> {
fn new(id: EntityId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self fn new(
id: EntityId,
entity_data: Rc<RefCell<T>>,
ref_counts: Weak<RwLock<EntityRefCounts>>,
) -> Self
where where
T: 'static, T: 'static,
{ {
Self { Self {
any_entity: AnyEntity::new(id, TypeId::of::<T>(), entity_map), any_entity: AnyEntity::new(id, TypeId::of::<T>(), entity_data, ref_counts),
entity_type: PhantomData, entity_type: PhantomData,
} }
} }
@ -504,6 +500,7 @@ impl<T: 'static> PartialOrd for Entity<T> {
pub struct AnyWeakEntity { pub struct AnyWeakEntity {
pub(crate) entity_id: EntityId, pub(crate) entity_id: EntityId,
entity_type: TypeId, entity_type: TypeId,
entity_data: rc::Weak<RefCell<dyn Any>>,
entity_ref_counts: Weak<RwLock<EntityRefCounts>>, entity_ref_counts: Weak<RwLock<EntityRefCounts>>,
} }
@ -538,7 +535,8 @@ impl AnyWeakEntity {
Some(AnyEntity { Some(AnyEntity {
entity_id: self.entity_id, entity_id: self.entity_id,
entity_type: self.entity_type, entity_type: self.entity_type,
entity_map: self.entity_ref_counts.clone(), entity_data: self.entity_data.upgrade()?,
ref_counts: self.entity_ref_counts.clone(),
#[cfg(any(test, feature = "leak-detection"))] #[cfg(any(test, feature = "leak-detection"))]
handle_id: self handle_id: self
.entity_ref_counts .entity_ref_counts
@ -592,6 +590,7 @@ impl AnyWeakEntity {
// read in the first place, so we're good! // read in the first place, so we're good!
entity_id: entity_id.into(), entity_id: entity_id.into(),
entity_type: TypeId::of::<()>(), entity_type: TypeId::of::<()>(),
entity_data: Rc::downgrade(&(Rc::new(RefCell::new(())) as Rc<RefCell<dyn Any>>)),
entity_ref_counts: Weak::new(), entity_ref_counts: Weak::new(),
} }
} }
@ -836,10 +835,10 @@ mod test {
// Tests that slots are not re-used before take_dropped. // Tests that slots are not re-used before take_dropped.
let mut entity_map = EntityMap::new(); let mut entity_map = EntityMap::new();
let slot = entity_map.reserve::<TestEntity>(); let slot = entity_map.reserve();
entity_map.insert(slot, TestEntity { i: 1 }); entity_map.insert(slot, TestEntity { i: 1 });
let slot = entity_map.reserve::<TestEntity>(); let slot = entity_map.reserve();
entity_map.insert(slot, TestEntity { i: 2 }); entity_map.insert(slot, TestEntity { i: 2 });
let dropped = entity_map.take_dropped(); let dropped = entity_map.take_dropped();
@ -848,7 +847,7 @@ mod test {
assert_eq!( assert_eq!(
dropped dropped
.into_iter() .into_iter()
.map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i) .map(|(_, entity)| entity.borrow().downcast_ref::<TestEntity>().unwrap().i)
.collect::<Vec<i32>>(), .collect::<Vec<i32>>(),
vec![1, 2], vec![1, 2],
); );
@ -859,7 +858,7 @@ mod test {
// Tests that weak handles are not upgraded before take_dropped // Tests that weak handles are not upgraded before take_dropped
let mut entity_map = EntityMap::new(); let mut entity_map = EntityMap::new();
let slot = entity_map.reserve::<TestEntity>(); let slot = entity_map.reserve();
let handle = entity_map.insert(slot, TestEntity { i: 1 }); let handle = entity_map.insert(slot, TestEntity { i: 1 });
let weak = handle.downgrade(); let weak = handle.downgrade();
drop(handle); drop(handle);
@ -873,7 +872,7 @@ mod test {
assert_eq!( assert_eq!(
dropped dropped
.into_iter() .into_iter()
.map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i) .map(|(_, entity)| entity.borrow().downcast_ref::<TestEntity>().unwrap().i)
.collect::<Vec<i32>>(), .collect::<Vec<i32>>(),
vec![1], vec![1],
); );

View file

@ -1,9 +1,9 @@
use crate::{ use crate::{
Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace, Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace,
BackgroundExecutor, BorrowAppContext, Bounds, Capslock, ClipboardItem, DrawPhase, Drawable, BackgroundExecutor, BorrowAppContext, Bounds, Capslock, ClipboardItem, DrawPhase, Drawable,
Element, Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers, Element, Empty, EntityId, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds, TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds,
WindowHandle, WindowOptions, WindowHandle, WindowOptions,
}; };
@ -40,14 +40,14 @@ impl AppContext for TestAppContext {
app.new(build_entity) app.new(build_entity)
} }
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> { fn reserve_entity(&mut self) -> Self::Result<EntityId> {
let mut app = self.app.borrow_mut(); let mut app = self.app.borrow_mut();
app.reserve_entity() app.reserve_entity()
} }
fn insert_entity<T: 'static>( fn insert_entity<T: 'static>(
&mut self, &mut self,
reservation: crate::Reservation<T>, reservation: EntityId,
build_entity: impl FnOnce(&mut Context<T>) -> T, build_entity: impl FnOnce(&mut Context<T>) -> T,
) -> Self::Result<Entity<T>> { ) -> Self::Result<Entity<T>> {
let mut app = self.app.borrow_mut(); let mut app = self.app.borrow_mut();
@ -891,13 +891,13 @@ impl AppContext for VisualTestContext {
self.cx.new(build_entity) self.cx.new(build_entity)
} }
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> { fn reserve_entity(&mut self) -> Self::Result<EntityId> {
self.cx.reserve_entity() self.cx.reserve_entity()
} }
fn insert_entity<T: 'static>( fn insert_entity<T: 'static>(
&mut self, &mut self,
reservation: crate::Reservation<T>, reservation: EntityId,
build_entity: impl FnOnce(&mut Context<T>) -> T, build_entity: impl FnOnce(&mut Context<T>) -> T,
) -> Self::Result<Entity<T>> { ) -> Self::Result<Entity<T>> {
self.cx.insert_entity(reservation, build_entity) self.cx.insert_entity(reservation, build_entity)

View file

@ -175,16 +175,15 @@ pub trait AppContext {
build_entity: impl FnOnce(&mut Context<T>) -> T, build_entity: impl FnOnce(&mut Context<T>) -> T,
) -> Self::Result<Entity<T>>; ) -> Self::Result<Entity<T>>;
/// Reserve a slot for a entity to be inserted later. /// Reserve an EntityId for a entity to be inserted later.
/// The returned [Reservation] allows you to obtain the [EntityId] for the future entity. fn reserve_entity(&mut self) -> Self::Result<EntityId>;
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>>;
/// Insert a new entity in the app context based on a [Reservation] previously obtained from [`reserve_entity`]. /// Insert a new entity in the app context based on a [EntityId] previously obtained from [`reserve_entity`].
/// ///
/// [`reserve_entity`]: Self::reserve_entity /// [`reserve_entity`]: Self::reserve_entity
fn insert_entity<T: 'static>( fn insert_entity<T: 'static>(
&mut self, &mut self,
reservation: Reservation<T>, reservation: EntityId,
build_entity: impl FnOnce(&mut Context<T>) -> T, build_entity: impl FnOnce(&mut Context<T>) -> T,
) -> Self::Result<Entity<T>>; ) -> Self::Result<Entity<T>>;
@ -231,17 +230,6 @@ pub trait AppContext {
G: Global; G: Global;
} }
/// Returned by [Context::reserve_entity] to later be passed to [Context::insert_entity].
/// Allows you to obtain the [EntityId] for a entity before it is created.
pub struct Reservation<T>(pub(crate) Slot<T>);
impl<T: 'static> Reservation<T> {
/// Returns the [EntityId] that will be associated with the entity once it is inserted.
pub fn entity_id(&self) -> EntityId {
self.0.entity_id()
}
}
/// This trait is used for the different visual contexts in GPUI that /// This trait is used for the different visual contexts in GPUI that
/// require a window to be present. /// require a window to be present.
pub trait VisualContext: AppContext { pub trait VisualContext: AppContext {

View file

@ -30,16 +30,16 @@ pub fn derive_app_context(input: TokenStream) -> TokenStream {
self.#app_variable.new(build_entity) self.#app_variable.new(build_entity)
} }
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<gpui::Reservation<T>> { fn reserve_entity(&mut self) -> Self::Result<EntityId> {
self.#app_variable.reserve_entity() self.#app_variable.reserve_entity()
} }
fn insert_entity<T: 'static>( fn insert_entity<T: 'static>(
&mut self, &mut self,
reservation: gpui::Reservation<T>, entity_id: EntityId,
build_entity: impl FnOnce(&mut gpui::Context<'_, T>) -> T, build_entity: impl FnOnce(&mut gpui::Context<'_, T>) -> T,
) -> Self::Result<gpui::Entity<T>> { ) -> Self::Result<gpui::Entity<T>> {
self.#app_variable.insert_entity(reservation, build_entity) self.#app_variable.insert_entity(entity_id, build_entity)
} }
fn update_entity<T, R>( fn update_entity<T, R>(

View file

@ -1,6 +1,6 @@
#[test] #[test]
fn test_derive_context() { fn test_derive_context() {
use gpui::{App, Window}; use gpui::{App, EntityId, Window};
use gpui_macros::{AppContext, VisualContext}; use gpui_macros::{AppContext, VisualContext};
#[derive(AppContext, VisualContext)] #[derive(AppContext, VisualContext)]

View file

@ -967,7 +967,7 @@ impl Buffer {
language_registry: Option<Arc<LanguageRegistry>>, language_registry: Option<Arc<LanguageRegistry>>,
cx: &mut App, cx: &mut App,
) -> impl Future<Output = BufferSnapshot> + use<> { ) -> impl Future<Output = BufferSnapshot> + use<> {
let entity_id = cx.reserve_entity::<Self>().entity_id(); let entity_id = cx.reserve_entity();
let buffer_id = entity_id.as_non_zero_u64().into(); let buffer_id = entity_id.as_non_zero_u64().into();
async move { async move {
let text = let text =
@ -992,7 +992,7 @@ impl Buffer {
} }
pub fn build_empty_snapshot(cx: &mut App) -> BufferSnapshot { pub fn build_empty_snapshot(cx: &mut App) -> BufferSnapshot {
let entity_id = cx.reserve_entity::<Self>().entity_id(); let entity_id = cx.reserve_entity();
let buffer_id = entity_id.as_non_zero_u64().into(); let buffer_id = entity_id.as_non_zero_u64().into();
let text = let text =
TextBuffer::new_normalized(0, buffer_id, Default::default(), Rope::new()).snapshot(); TextBuffer::new_normalized(0, buffer_id, Default::default(), Rope::new()).snapshot();
@ -1015,7 +1015,7 @@ impl Buffer {
language_registry: Option<Arc<LanguageRegistry>>, language_registry: Option<Arc<LanguageRegistry>>,
cx: &mut App, cx: &mut App,
) -> BufferSnapshot { ) -> BufferSnapshot {
let entity_id = cx.reserve_entity::<Self>().entity_id(); let entity_id = cx.reserve_entity();
let buffer_id = entity_id.as_non_zero_u64().into(); let buffer_id = entity_id.as_non_zero_u64().into();
let text = TextBuffer::new_normalized(0, buffer_id, Default::default(), text).snapshot(); let text = TextBuffer::new_normalized(0, buffer_id, Default::default(), text).snapshot();
let mut syntax = SyntaxMap::new(&text).snapshot(); let mut syntax = SyntaxMap::new(&text).snapshot();