From 43bb38206f391a3f6f5721f69b01437bc296ad3c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 23 Aug 2021 14:58:37 -0700 Subject: [PATCH] Add generic subscribe and observe methods to contexts Co-Authored-By: Nathan Sobo --- gpui/src/app.rs | 500 ++++++++++++++------------------------- zed/src/editor/buffer.rs | 10 +- 2 files changed, 188 insertions(+), 322 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index a40698a4dc6dfdbcb19e4c1f50c097a5b805ff6a..70116be8a8341fc84aaec8b666c2964f85264af4 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -649,8 +649,8 @@ pub struct MutableAppContext { keystroke_matcher: keymap::Matcher, next_entity_id: usize, next_window_id: usize, - subscriptions: HashMap>, - observations: HashMap>, + subscriptions: HashMap bool>>>, + observations: HashMap bool>>>, presenters_and_platform_windows: HashMap>, Box)>, debug_elements_callbacks: HashMap crate::json::Value>>, @@ -877,91 +877,71 @@ impl MutableAppContext { ); } - pub fn subscribe_to_model(&mut self, handle: &ModelHandle, mut callback: F) + pub fn subscribe(&mut self, handle: &H, mut callback: F) where E: Entity, E::Event: 'static, - F: 'static + FnMut(ModelHandle, &E::Event, &mut Self), + H: Handle, + F: 'static + FnMut(H, &E::Event, &mut Self), { - let emitter_handle = handle.downgrade(); - self.subscribe(handle, move |payload, cx| { - if let Some(emitter_handle) = emitter_handle.upgrade(cx.as_ref()) { - callback(emitter_handle, payload, cx); - } - }); - } - - pub fn subscribe_to_view(&mut self, handle: &ViewHandle, mut callback: F) - where - V: View, - V::Event: 'static, - F: 'static + FnMut(ViewHandle, &V::Event, &mut Self), - { - let emitter_handle = handle.downgrade(); - self.subscribe(handle, move |payload, cx| { - if let Some(emitter_handle) = emitter_handle.upgrade(cx.as_ref()) { - callback(emitter_handle, payload, cx); - } - }); + self.subscribe_internal(handle, move |handle, event, cx| { + callback(handle, event, cx); + true + }) } - pub fn observe_model(&mut self, handle: &ModelHandle, mut callback: F) + fn observe(&mut self, handle: &H, mut callback: F) where E: Entity, E::Event: 'static, - F: 'static + FnMut(ModelHandle, &mut Self), + H: Handle, + F: 'static + FnMut(H, &mut Self), { - let emitter_handle = handle.downgrade(); - self.observe(handle, move |cx| { - if let Some(emitter_handle) = emitter_handle.upgrade(cx.as_ref()) { - callback(emitter_handle, cx); - } - }); + self.observe_internal(handle, move |handle, cx| { + callback(handle, cx); + true + }) } - pub fn observe_view(&mut self, handle: &ViewHandle, mut callback: F) - where - V: View, - V::Event: 'static, - F: 'static + FnMut(ViewHandle, &mut Self), - { - let emitter_handle = handle.downgrade(); - self.observe(handle, move |cx| { - if let Some(emitter_handle) = emitter_handle.upgrade(cx.as_ref()) { - callback(emitter_handle, cx); - } - }); - } - - pub fn subscribe(&mut self, handle: &impl Handle, mut callback: F) + pub fn subscribe_internal(&mut self, handle: &H, mut callback: F) where E: Entity, E::Event: 'static, - F: 'static + FnMut(&E::Event, &mut Self), + H: Handle, + F: 'static + FnMut(H, &E::Event, &mut Self) -> bool, { + let emitter = handle.downgrade(); self.subscriptions .entry(handle.id()) .or_default() - .push(Subscription::Global { - callback: Box::new(move |payload, cx| { + .push(Box::new(move |payload, cx| { + if let Some(emitter) = H::upgrade_from(&emitter, cx.as_ref()) { let payload = payload.downcast_ref().expect("downcast is type safe"); - callback(payload, cx); - }), - }); + callback(emitter, payload, cx) + } else { + false + } + })) } - pub fn observe(&mut self, handle: &impl Handle, callback: F) + fn observe_internal(&mut self, handle: &H, mut callback: F) where E: Entity, E::Event: 'static, - F: 'static + FnMut(&mut Self), + H: Handle, + F: 'static + FnMut(H, &mut Self) -> bool, { + let observed = handle.downgrade(); self.observations .entry(handle.id()) .or_default() - .push(Observation::Global { - callback: Box::new(callback), - }); + .push(Box::new(move |cx| { + if let Some(observed) = H::upgrade_from(&observed, cx) { + callback(observed, cx) + } else { + false + } + })) } pub(crate) fn notify_view(&mut self, window_id: usize, view_id: usize) { @@ -1363,91 +1343,29 @@ impl MutableAppContext { } fn emit_event(&mut self, entity_id: usize, payload: Box) { - if let Some(subscriptions) = self.subscriptions.remove(&entity_id) { - for mut subscription in subscriptions { - let alive = match &mut subscription { - Subscription::Global { callback } => { - callback(payload.as_ref(), self); - true - } - Subscription::FromModel { model_id, callback } => { - if let Some(mut model) = self.cx.models.remove(model_id) { - callback(model.as_any_mut(), payload.as_ref(), self, *model_id); - self.cx.models.insert(*model_id, model); - true - } else { - false - } - } - Subscription::FromView { - window_id, - view_id, - callback, - } => { - if let Some(mut view) = self.cx.views.remove(&(*window_id, *view_id)) { - callback( - view.as_any_mut(), - payload.as_ref(), - self, - *window_id, - *view_id, - ); - self.cx.views.insert((*window_id, *view_id), view); - true - } else { - false - } - } - }; - + if let Some(callbacks) = self.subscriptions.remove(&entity_id) { + for mut callback in callbacks { + let alive = callback(payload.as_ref(), self); if alive { self.subscriptions .entry(entity_id) .or_default() - .push(subscription); + .push(callback); } } } } fn notify_model_observers(&mut self, observed_id: usize) { - if let Some(observations) = self.observations.remove(&observed_id) { + if let Some(callbacks) = self.observations.remove(&observed_id) { if self.cx.models.contains_key(&observed_id) { - for mut observation in observations { - let alive = match &mut observation { - Observation::Global { callback } => { - callback(self); - true - } - Observation::FromModel { model_id, callback } => { - if let Some(mut model) = self.cx.models.remove(model_id) { - callback(model.as_any_mut(), self, *model_id); - self.cx.models.insert(*model_id, model); - true - } else { - false - } - } - Observation::FromView { - window_id, - view_id, - callback, - } => { - if let Some(mut view) = self.cx.views.remove(&(*window_id, *view_id)) { - callback(view.as_any_mut(), self, *window_id, *view_id); - self.cx.views.insert((*window_id, *view_id), view); - true - } else { - false - } - } - }; - + for mut callback in callbacks { + let alive = callback(self); if alive { self.observations .entry(observed_id) .or_default() - .push(observation); + .push(callback); } } } @@ -1463,46 +1381,19 @@ impl MutableAppContext { .insert(observed_view_id); } - if let Some(observations) = self.observations.remove(&observed_view_id) { + if let Some(callbacks) = self.observations.remove(&observed_view_id) { if self .cx .views .contains_key(&(observed_window_id, observed_view_id)) { - for mut observation in observations { - if let Observation::FromView { - window_id: observing_window_id, - view_id: observing_view_id, - callback, - } = &mut observation - { - let alive = if let Some(mut view) = self - .cx - .views - .remove(&(*observing_window_id, *observing_view_id)) - { - (callback)( - view.as_any_mut(), - self, - *observing_window_id, - *observing_view_id, - ); - self.cx - .views - .insert((*observing_window_id, *observing_view_id), view); - true - } else { - false - }; - - if alive { - self.observations - .entry(observed_view_id) - .or_default() - .push(observation); - } - } else { - unreachable!() + for mut callback in callbacks { + let alive = callback(self); + if alive { + self.observations + .entry(observed_view_id) + .or_default() + .push(callback); } } } @@ -1967,26 +1858,6 @@ impl<'a, T: Entity> ModelContext<'a, T> { self.app.add_model(build_model) } - pub fn subscribe(&mut self, handle: &ModelHandle, mut callback: F) - where - S::Event: 'static, - F: 'static + FnMut(&mut T, &S::Event, &mut ModelContext), - { - self.app - .subscriptions - .entry(handle.model_id) - .or_default() - .push(Subscription::FromModel { - model_id: self.model_id, - callback: Box::new(move |model, payload, app, model_id| { - let model = model.downcast_mut().expect("downcast is type safe"); - let payload = payload.downcast_ref().expect("downcast is type safe"); - let mut cx = ModelContext::new(app, model_id); - callback(model, payload, &mut cx); - }), - }); - } - pub fn emit(&mut self, payload: T::Event) { self.app.pending_effects.push_back(Effect::Event { entity_id: self.model_id, @@ -1994,36 +1865,51 @@ impl<'a, T: Entity> ModelContext<'a, T> { }); } - pub fn observe(&mut self, handle: &ModelHandle, mut callback: F) - where - S: Entity, - F: 'static + FnMut(&mut T, ModelHandle, &mut ModelContext), - { - let observed_handle = handle.downgrade(); + pub fn notify(&mut self) { self.app - .observations - .entry(handle.model_id) - .or_default() - .push(Observation::FromModel { + .pending_effects + .push_back(Effect::ModelNotification { model_id: self.model_id, - callback: Box::new(move |model, app, model_id| { - if let Some(observed) = observed_handle.upgrade(app) { - let model = model.downcast_mut().expect("downcast is type safe"); - let mut cx = ModelContext::new(app, model_id); - callback(model, observed, &mut cx); - } - }), }); } - pub fn notify(&mut self) { + pub fn subscribe(&mut self, handle: &ModelHandle, mut callback: F) + where + S::Event: 'static, + F: 'static + FnMut(&mut T, ModelHandle, &S::Event, &mut ModelContext), + { + let subscriber = self.handle().downgrade(); self.app - .pending_effects - .push_back(Effect::ModelNotification { - model_id: self.model_id, + .subscribe_internal(handle, move |emitter, event, cx| { + if let Some(subscriber) = subscriber.upgrade(cx) { + subscriber.update(cx, |subscriber, cx| { + callback(subscriber, emitter, event, cx); + }); + true + } else { + false + } }); } + pub fn observe(&mut self, handle: &ModelHandle, mut callback: F) + where + S: Entity, + F: 'static + FnMut(&mut T, ModelHandle, &mut ModelContext), + { + let observer = self.handle().downgrade(); + self.app.observe_internal(handle, move |observed, cx| { + if let Some(observer) = observer.upgrade(cx) { + observer.update(cx, |observer, cx| { + callback(observer, observed, cx); + }); + true + } else { + false + } + }); + } + pub fn handle(&self) -> ModelHandle { ModelHandle::new(self.model_id, &self.app.cx.ref_counts) } @@ -2211,107 +2097,85 @@ impl<'a, T: View> ViewContext<'a, T> { self.app.add_option_view(self.window_id, build_view) } - pub fn subscribe_to_model(&mut self, handle: &ModelHandle, mut callback: F) + pub fn subscribe_to_model(&mut self, handle: &ModelHandle, callback: F) where E: Entity, E::Event: 'static, F: 'static + FnMut(&mut T, ModelHandle, &E::Event, &mut ViewContext), { - let emitter_handle = handle.downgrade(); - self.subscribe(handle, move |model, payload, cx| { - if let Some(emitter_handle) = emitter_handle.upgrade(cx.as_ref()) { - callback(model, emitter_handle, payload, cx); - } - }); + self.subscribe(handle, callback) } - pub fn subscribe_to_view(&mut self, handle: &ViewHandle, mut callback: F) + pub fn subscribe_to_view(&mut self, handle: &ViewHandle, callback: F) where V: View, V::Event: 'static, F: 'static + FnMut(&mut T, ViewHandle, &V::Event, &mut ViewContext), { - let emitter_handle = handle.downgrade(); - self.subscribe(handle, move |view, payload, cx| { - if let Some(emitter_handle) = emitter_handle.upgrade(cx.as_ref()) { - callback(view, emitter_handle, payload, cx); - } - }); + self.subscribe(handle, callback) } - pub fn subscribe(&mut self, handle: &impl Handle, mut callback: F) + pub fn observe_model(&mut self, handle: &ModelHandle, callback: F) where - E: Entity, - E::Event: 'static, - F: 'static + FnMut(&mut T, &E::Event, &mut ViewContext), + S: Entity, + F: 'static + FnMut(&mut T, ModelHandle, &mut ViewContext), { - self.app - .subscriptions - .entry(handle.id()) - .or_default() - .push(Subscription::FromView { - window_id: self.window_id, - view_id: self.view_id, - callback: Box::new(move |entity, payload, app, window_id, view_id| { - let entity = entity.downcast_mut().expect("downcast is type safe"); - let payload = payload.downcast_ref().expect("downcast is type safe"); - let mut cx = ViewContext::new(app, window_id, view_id); - callback(entity, payload, &mut cx); - }), - }); + self.observe(handle, callback) } - pub fn emit(&mut self, payload: T::Event) { - self.app.pending_effects.push_back(Effect::Event { - entity_id: self.view_id, - payload: Box::new(payload), - }); + pub fn observe_view(&mut self, handle: &ViewHandle, callback: F) + where + S: View, + F: 'static + FnMut(&mut T, ViewHandle, &mut ViewContext), + { + self.observe(handle, callback) } - pub fn observe_model(&mut self, handle: &ModelHandle, mut callback: F) + pub fn subscribe(&mut self, handle: &H, mut callback: F) where - S: Entity, - F: 'static + FnMut(&mut T, ModelHandle, &mut ViewContext), + E: Entity, + E::Event: 'static, + H: Handle, + F: 'static + FnMut(&mut T, H, &E::Event, &mut ViewContext), { - let observed_handle = handle.downgrade(); + let subscriber = self.handle().downgrade(); self.app - .observations - .entry(handle.id()) - .or_default() - .push(Observation::FromView { - window_id: self.window_id, - view_id: self.view_id, - callback: Box::new(move |view, app, window_id, view_id| { - if let Some(observed) = observed_handle.upgrade(app) { - let view = view.downcast_mut().expect("downcast is type safe"); - let mut cx = ViewContext::new(app, window_id, view_id); - callback(view, observed, &mut cx); - } - }), + .subscribe_internal(handle, move |emitter, event, cx| { + if let Some(subscriber) = subscriber.upgrade(cx) { + subscriber.update(cx, |subscriber, cx| { + callback(subscriber, emitter, event, cx); + }); + true + } else { + false + } }); } - pub fn observe_view(&mut self, handle: &ViewHandle, mut callback: F) + fn observe(&mut self, handle: &H, mut callback: F) where - S: View, - F: 'static + FnMut(&mut T, ViewHandle, &mut ViewContext), + E: Entity, + H: Handle, + F: 'static + FnMut(&mut T, H, &mut ViewContext), { - let observed_handle = handle.downgrade(); - self.app - .observations - .entry(handle.id()) - .or_default() - .push(Observation::FromView { - window_id: self.window_id, - view_id: self.view_id, - callback: Box::new(move |view, app, observing_window_id, observing_view_id| { - if let Some(observed) = observed_handle.upgrade(app) { - let view = view.downcast_mut().expect("downcast is type safe"); - let mut cx = ViewContext::new(app, observing_window_id, observing_view_id); - callback(view, observed, &mut cx); - } - }), - }); + let observer = self.handle().downgrade(); + self.app.observe_internal(handle, move |observed, cx| { + if let Some(observer) = observer.upgrade(cx) { + observer.update(cx, |observer, cx| { + callback(observer, observed, cx); + }); + true + } else { + false + } + }); + } + + pub fn emit(&mut self, payload: T::Event) { + self.app.pending_effects.push_back(Effect::Event { + entity_id: self.view_id, + payload: Box::new(payload), + }); } pub fn notify(&mut self) { @@ -2433,8 +2297,13 @@ impl UpdateView for ViewContext<'_, V> { } pub trait Handle { + type Weak: 'static; fn id(&self) -> usize; fn location(&self) -> EntityLocation; + fn downgrade(&self) -> Self::Weak; + fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option + where + Self: Sized; } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] @@ -2495,13 +2364,13 @@ impl ModelHandle { let (tx, mut rx) = mpsc::channel(1024); let mut cx = cx.cx.borrow_mut(); - cx.observe_model(self, { + cx.observe(self, { let mut tx = tx.clone(); move |_, _| { tx.blocking_send(()).ok(); } }); - cx.subscribe_to_model(self, { + cx.subscribe(self, { let mut tx = tx.clone(); move |_, _, _| { tx.blocking_send(()).ok(); @@ -2592,7 +2461,9 @@ impl Drop for ModelHandle { } } -impl Handle for ModelHandle { +impl Handle for ModelHandle { + type Weak = WeakModelHandle; + fn id(&self) -> usize { self.model_id } @@ -2600,7 +2471,19 @@ impl Handle for ModelHandle { fn location(&self) -> EntityLocation { EntityLocation::Model(self.model_id) } + + fn downgrade(&self) -> Self::Weak { + self.downgrade() + } + + fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option + where + Self: Sized, + { + weak.upgrade(cx) + } } + pub struct WeakModelHandle { model_id: usize, model_type: PhantomData, @@ -2718,9 +2601,9 @@ impl ViewHandle { } }); - cx.subscribe(self, { + cx.subscribe_to_view(self, { let mut tx = tx.clone(); - move |_, _, _| { + move |_, _, _, _| { tx.blocking_send(()).ok(); } }) @@ -2801,7 +2684,9 @@ impl Drop for ViewHandle { } } -impl Handle for ViewHandle { +impl Handle for ViewHandle { + type Weak = WeakViewHandle; + fn id(&self) -> usize { self.view_id } @@ -2809,6 +2694,17 @@ impl Handle for ViewHandle { fn location(&self) -> EntityLocation { EntityLocation::View(self.window_id, self.view_id) } + + fn downgrade(&self) -> Self::Weak { + self.downgrade() + } + + fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option + where + Self: Sized, + { + weak.upgrade(cx) + } } pub struct AnyViewHandle { @@ -3097,36 +2993,6 @@ impl RefCounts { } } -enum Subscription { - Global { - callback: Box, - }, - FromModel { - model_id: usize, - callback: Box, - }, - FromView { - window_id: usize, - view_id: usize, - callback: Box, - }, -} - -enum Observation { - Global { - callback: Box, - }, - FromModel { - model_id: usize, - callback: Box, - }, - FromView { - window_id: usize, - view_id: usize, - callback: Box, - }, -} - #[cfg(test)] mod tests { use super::*; @@ -3151,7 +3017,7 @@ mod tests { cx.observe(other, |me, _, _| { me.events.push("notified".into()); }); - cx.subscribe(other, |me, event, _| { + cx.subscribe(other, |me, _, event, _| { me.events.push(format!("observed event {}", event)); }); } @@ -3209,10 +3075,10 @@ mod tests { let handle_2b = handle_2.clone(); handle_1.update(cx, |_, c| { - c.subscribe(&handle_2, move |model: &mut Model, event, c| { + c.subscribe(&handle_2, move |model: &mut Model, _, event, c| { model.events.push(*event); - c.subscribe(&handle_2b, |model, event, _| { + c.subscribe(&handle_2b, |model, _, event, _| { model.events.push(*event * 2); }); }); @@ -3519,7 +3385,7 @@ mod tests { cx.subscribe_to_model(&observed_model, |_, _, _, _| {}); }); observing_model.update(cx, |_, cx| { - cx.subscribe(&observed_model, |_, _, _| {}); + cx.subscribe(&observed_model, |_, _, _, _| {}); }); cx.update(|| { diff --git a/zed/src/editor/buffer.rs b/zed/src/editor/buffer.rs index eb57328a1e3affecebff7a0d4b09a6da8c930704..624c84697aebab163a5457f87a8d247f64a4997d 100644 --- a/zed/src/editor/buffer.rs +++ b/zed/src/editor/buffer.rs @@ -2940,11 +2940,11 @@ mod tests { let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx)); let buffer_ops = buffer1.update(cx, |buffer, cx| { let buffer_1_events = buffer_1_events.clone(); - cx.subscribe(&buffer1, move |_, event, _| { + cx.subscribe(&buffer1, move |_, _, event, _| { buffer_1_events.borrow_mut().push(event.clone()) }); let buffer_2_events = buffer_2_events.clone(); - cx.subscribe(&buffer2, move |_, event, _| { + cx.subscribe(&buffer2, move |_, _, event, _| { buffer_2_events.borrow_mut().push(event.clone()) }); @@ -3380,7 +3380,7 @@ mod tests { buffer1.update(&mut cx, |buffer, cx| { cx.subscribe(&buffer1, { let events = events.clone(); - move |_, event, _| events.borrow_mut().push(event.clone()) + move |_, _, event, _| events.borrow_mut().push(event.clone()) }); assert!(!buffer.is_dirty()); @@ -3436,7 +3436,7 @@ mod tests { buffer2.update(&mut cx, |_, cx| { cx.subscribe(&buffer2, { let events = events.clone(); - move |_, event, _| events.borrow_mut().push(event.clone()) + move |_, _, event, _| events.borrow_mut().push(event.clone()) }); }); @@ -3456,7 +3456,7 @@ mod tests { buffer3.update(&mut cx, |_, cx| { cx.subscribe(&buffer3, { let events = events.clone(); - move |_, event, _| events.borrow_mut().push(event.clone()) + move |_, _, event, _| events.borrow_mut().push(event.clone()) }); });