Outline a bunch of methods in gpui that leaked SubscriberSet types (#7430)

This takes down LLVM IR size of theme_selector from 316k to ~250k. Note
that I do not care about theme_selector in particular, though it acts as
a benchmark for smaller crates to me ("how much static overhead in
compile time does gpui have").

The title is a bit dramatic, so just to shed some light: by leaking a
type I mean forcing downstream crates to codegen it's methods/know about
it's drop code. Since SubscriberSet is no longer used directly in the
generic (==inlineable) methods, users no longer have to codegen `insert`
and co.

Release Notes:

- N/A
This commit is contained in:
Piotr Osiewicz 2024-02-06 11:10:15 +01:00 committed by GitHub
parent c591681bad
commit 1446fb7632
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 70 additions and 53 deletions

View file

@ -380,6 +380,11 @@ impl AppContext {
}) })
} }
pub(crate) fn new_observer(&mut self, key: EntityId, value: Handler) -> Subscription {
let (subscription, activate) = self.observers.insert(key, value);
self.defer(move |_| activate());
subscription
}
pub(crate) fn observe_internal<W, E>( pub(crate) fn observe_internal<W, E>(
&mut self, &mut self,
entity: &E, entity: &E,
@ -391,7 +396,7 @@ impl AppContext {
{ {
let entity_id = entity.entity_id(); let entity_id = entity.entity_id();
let handle = entity.downgrade(); let handle = entity.downgrade();
let (subscription, activate) = self.observers.insert( self.new_observer(
entity_id, entity_id,
Box::new(move |cx| { Box::new(move |cx| {
if let Some(handle) = E::upgrade_from(&handle) { if let Some(handle) = E::upgrade_from(&handle) {
@ -400,9 +405,7 @@ impl AppContext {
false false
} }
}), }),
); )
self.defer(move |_| activate());
subscription
} }
/// Arrange for the given callback to be invoked whenever the given model or view emits an event of a given type. /// Arrange for the given callback to be invoked whenever the given model or view emits an event of a given type.
@ -423,6 +426,15 @@ impl AppContext {
}) })
} }
pub(crate) fn new_subscription(
&mut self,
key: EntityId,
value: (TypeId, Listener),
) -> Subscription {
let (subscription, activate) = self.event_listeners.insert(key, value);
self.defer(move |_| activate());
subscription
}
pub(crate) fn subscribe_internal<T, E, Evt>( pub(crate) fn subscribe_internal<T, E, Evt>(
&mut self, &mut self,
entity: &E, entity: &E,
@ -435,7 +447,7 @@ impl AppContext {
{ {
let entity_id = entity.entity_id(); let entity_id = entity.entity_id();
let entity = entity.downgrade(); let entity = entity.downgrade();
let (subscription, activate) = self.event_listeners.insert( self.new_subscription(
entity_id, entity_id,
( (
TypeId::of::<Evt>(), TypeId::of::<Evt>(),
@ -448,9 +460,7 @@ impl AppContext {
} }
}), }),
), ),
); )
self.defer(move |_| activate());
subscription
} }
/// Returns handles to all open windows in the application. /// Returns handles to all open windows in the application.
@ -930,13 +940,22 @@ impl AppContext {
self.globals_by_type.insert(global_type, lease.global); self.globals_by_type.insert(global_type, lease.global);
} }
pub(crate) fn new_view_observer(
&mut self,
key: TypeId,
value: NewViewListener,
) -> Subscription {
let (subscription, activate) = self.new_view_observers.insert(key, value);
activate();
subscription
}
/// Arrange for the given function to be invoked whenever a view of the specified type is created. /// Arrange for the given function to be invoked whenever a view of the specified type is created.
/// The function will be passed a mutable reference to the view along with an appropriate context. /// The function will be passed a mutable reference to the view along with an appropriate context.
pub fn observe_new_views<V: 'static>( pub fn observe_new_views<V: 'static>(
&mut self, &mut self,
on_new: impl 'static + Fn(&mut V, &mut ViewContext<V>), on_new: impl 'static + Fn(&mut V, &mut ViewContext<V>),
) -> Subscription { ) -> Subscription {
let (subscription, activate) = self.new_view_observers.insert( self.new_view_observer(
TypeId::of::<V>(), TypeId::of::<V>(),
Box::new(move |any_view: AnyView, cx: &mut WindowContext| { Box::new(move |any_view: AnyView, cx: &mut WindowContext| {
any_view any_view
@ -946,9 +965,7 @@ impl AppContext {
on_new(view_state, cx); on_new(view_state, cx);
}) })
}), }),
); )
activate();
subscription
} }
/// Observe the release of a model or view. The callback is invoked after the model or view /// Observe the release of a model or view. The callback is invoked after the model or view
@ -980,9 +997,15 @@ impl AppContext {
&mut self, &mut self,
f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static, f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
) -> Subscription { ) -> Subscription {
let (subscription, activate) = self.keystroke_observers.insert((), Box::new(f)); fn inner(
activate(); keystroke_observers: &mut SubscriberSet<(), KeystrokeObserver>,
subscription handler: KeystrokeObserver,
) -> Subscription {
let (subscription, activate) = keystroke_observers.insert((), handler);
activate();
subscription
}
inner(&mut self.keystroke_observers, Box::new(f))
} }
pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) { pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {

View file

@ -451,6 +451,12 @@ impl Window {
pending_input: None, pending_input: None,
} }
} }
fn new_focus_listener(
&mut self,
value: AnyWindowFocusListener,
) -> (Subscription, impl FnOnce()) {
self.focus_listeners.insert((), value)
}
} }
/// Indicates which region of the window is visible. Content falling outside of this mask will not be /// Indicates which region of the window is visible. Content falling outside of this mask will not be
@ -635,7 +641,7 @@ impl<'a> WindowContext<'a> {
let entity_id = entity.entity_id(); let entity_id = entity.entity_id();
let entity = entity.downgrade(); let entity = entity.downgrade();
let window_handle = self.window.handle; let window_handle = self.window.handle;
let (subscription, activate) = self.app.event_listeners.insert( self.app.new_subscription(
entity_id, entity_id,
( (
TypeId::of::<Evt>(), TypeId::of::<Evt>(),
@ -653,9 +659,7 @@ impl<'a> WindowContext<'a> {
.unwrap_or(false) .unwrap_or(false)
}), }),
), ),
); )
self.app.defer(move |_| activate());
subscription
} }
/// Creates an [`AsyncWindowContext`], which has a static lifetime and can be held across /// Creates an [`AsyncWindowContext`], which has a static lifetime and can be held across
@ -1747,13 +1751,15 @@ impl VisualContext for WindowContext<'_> {
let entity = build_view_state(&mut cx); let entity = build_view_state(&mut cx);
cx.entities.insert(slot, entity); cx.entities.insert(slot, entity);
cx.new_view_observers // Non-generic part to avoid leaking SubscriberSet to invokers of `new_view`.
.clone() fn notify_observers(cx: &mut WindowContext, tid: TypeId, view: AnyView) {
.retain(&TypeId::of::<V>(), |observer| { cx.new_view_observers.clone().retain(&tid, |observer| {
let any_view = AnyView::from(view.clone()); let any_view = view.clone();
(observer)(any_view, self); (observer)(any_view, cx);
true true
}); });
}
notify_observers(self, TypeId::of::<V>(), AnyView::from(view.clone()));
view view
} }
@ -1955,7 +1961,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
let entity_id = entity.entity_id(); let entity_id = entity.entity_id();
let entity = entity.downgrade(); let entity = entity.downgrade();
let window_handle = self.window.handle; let window_handle = self.window.handle;
let (subscription, activate) = self.app.observers.insert( self.app.new_observer(
entity_id, entity_id,
Box::new(move |cx| { Box::new(move |cx| {
window_handle window_handle
@ -1969,9 +1975,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
}) })
.unwrap_or(false) .unwrap_or(false)
}), }),
); )
self.app.defer(move |_| activate());
subscription
} }
/// Subscribe to events emitted by another model or view. /// Subscribe to events emitted by another model or view.
@ -1991,7 +1995,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
let entity_id = entity.entity_id(); let entity_id = entity.entity_id();
let handle = entity.downgrade(); let handle = entity.downgrade();
let window_handle = self.window.handle; let window_handle = self.window.handle;
let (subscription, activate) = self.app.event_listeners.insert( self.app.new_subscription(
entity_id, entity_id,
( (
TypeId::of::<Evt>(), TypeId::of::<Evt>(),
@ -2009,9 +2013,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
.unwrap_or(false) .unwrap_or(false)
}), }),
), ),
); )
self.app.defer(move |_| activate());
subscription
} }
/// Register a callback to be invoked when the view is released. /// Register a callback to be invoked when the view is released.
@ -2136,9 +2138,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
) -> Subscription { ) -> Subscription {
let view = self.view.downgrade(); let view = self.view.downgrade();
let focus_id = handle.id; let focus_id = handle.id;
let (subscription, activate) = self.window.focus_listeners.insert( let (subscription, activate) =
(), self.window.new_focus_listener(Box::new(move |event, cx| {
Box::new(move |event, cx| {
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
if event.previous_focus_path.last() != Some(&focus_id) if event.previous_focus_path.last() != Some(&focus_id)
&& event.current_focus_path.last() == Some(&focus_id) && event.current_focus_path.last() == Some(&focus_id)
@ -2147,9 +2148,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
} }
}) })
.is_ok() .is_ok()
}), }));
); self.app.defer(|_| activate());
self.app.defer(move |_| activate());
subscription subscription
} }
@ -2162,9 +2162,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
) -> Subscription { ) -> Subscription {
let view = self.view.downgrade(); let view = self.view.downgrade();
let focus_id = handle.id; let focus_id = handle.id;
let (subscription, activate) = self.window.focus_listeners.insert( let (subscription, activate) =
(), self.window.new_focus_listener(Box::new(move |event, cx| {
Box::new(move |event, cx| {
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
if !event.previous_focus_path.contains(&focus_id) if !event.previous_focus_path.contains(&focus_id)
&& event.current_focus_path.contains(&focus_id) && event.current_focus_path.contains(&focus_id)
@ -2173,8 +2172,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
} }
}) })
.is_ok() .is_ok()
}), }));
);
self.app.defer(move |_| activate()); self.app.defer(move |_| activate());
subscription subscription
} }
@ -2188,9 +2186,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
) -> Subscription { ) -> Subscription {
let view = self.view.downgrade(); let view = self.view.downgrade();
let focus_id = handle.id; let focus_id = handle.id;
let (subscription, activate) = self.window.focus_listeners.insert( let (subscription, activate) =
(), self.window.new_focus_listener(Box::new(move |event, cx| {
Box::new(move |event, cx| {
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
if event.previous_focus_path.last() == Some(&focus_id) if event.previous_focus_path.last() == Some(&focus_id)
&& event.current_focus_path.last() != Some(&focus_id) && event.current_focus_path.last() != Some(&focus_id)
@ -2199,8 +2196,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
} }
}) })
.is_ok() .is_ok()
}), }));
);
self.app.defer(move |_| activate()); self.app.defer(move |_| activate());
subscription subscription
} }
@ -2231,9 +2227,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
) -> Subscription { ) -> Subscription {
let view = self.view.downgrade(); let view = self.view.downgrade();
let focus_id = handle.id; let focus_id = handle.id;
let (subscription, activate) = self.window.focus_listeners.insert( let (subscription, activate) =
(), self.window.new_focus_listener(Box::new(move |event, cx| {
Box::new(move |event, cx| {
view.update(cx, |view, cx| { view.update(cx, |view, cx| {
if event.previous_focus_path.contains(&focus_id) if event.previous_focus_path.contains(&focus_id)
&& !event.current_focus_path.contains(&focus_id) && !event.current_focus_path.contains(&focus_id)
@ -2242,8 +2237,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
} }
}) })
.is_ok() .is_ok()
}), }));
);
self.app.defer(move |_| activate()); self.app.defer(move |_| activate());
subscription subscription
} }