gpui: Add Global marker trait (#7095)

This should prevent a class of bugs where one queries the wrong type of
global, which results in oddities at runtime.

Release Notes:

- N/A

---------

Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
Piotr Osiewicz
2024-01-30 14:08:20 -05:00
committed by GitHub
co-authored by Marshall Marshall Bowers
parent 7bfa584eb6
commit e6ebe7974d
59 changed files with 449 additions and 237 deletions
+16 -16
View File
@@ -17,7 +17,7 @@ use time::UtcOffset;
use crate::{
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, KeyBinding, Keymap,
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap,
Keystroke, LayoutId, Menu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render,
SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId,
@@ -823,13 +823,13 @@ impl AppContext {
}
/// Check whether a global of the given type has been assigned.
pub fn has_global<G: 'static>(&self) -> bool {
pub fn has_global<G: Global>(&self) -> bool {
self.globals_by_type.contains_key(&TypeId::of::<G>())
}
/// Access the global of the given type. Panics if a global for that type has not been assigned.
#[track_caller]
pub fn global<G: 'static>(&self) -> &G {
pub fn global<G: Global>(&self) -> &G {
self.globals_by_type
.get(&TypeId::of::<G>())
.map(|any_state| any_state.downcast_ref::<G>().unwrap())
@@ -838,7 +838,7 @@ impl AppContext {
}
/// Access the global of the given type if a value has been assigned.
pub fn try_global<G: 'static>(&self) -> Option<&G> {
pub fn try_global<G: Global>(&self) -> Option<&G> {
self.globals_by_type
.get(&TypeId::of::<G>())
.map(|any_state| any_state.downcast_ref::<G>().unwrap())
@@ -846,7 +846,7 @@ impl AppContext {
/// Access the global of the given type mutably. Panics if a global for that type has not been assigned.
#[track_caller]
pub fn global_mut<G: 'static>(&mut self) -> &mut G {
pub fn global_mut<G: Global>(&mut self) -> &mut G {
let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_type
@@ -858,7 +858,7 @@ impl AppContext {
/// Access the global of the given type mutably. A default value is assigned if a global of this type has not
/// yet been assigned.
pub fn default_global<G: 'static + Default>(&mut self) -> &mut G {
pub fn default_global<G: Global + Default>(&mut self) -> &mut G {
let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_type
@@ -869,7 +869,7 @@ impl AppContext {
}
/// Sets the value of the global of the given type.
pub fn set_global<G: Any>(&mut self, global: G) {
pub fn set_global<G: Global>(&mut self, global: G) {
let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_type.insert(global_type, Box::new(global));
@@ -882,7 +882,7 @@ impl AppContext {
}
/// Remove the global of the given type from the app context. Does not notify global observers.
pub fn remove_global<G: Any>(&mut self) -> G {
pub fn remove_global<G: Global>(&mut self) -> G {
let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type });
*self
@@ -895,7 +895,7 @@ impl AppContext {
/// Updates the global of the given type with a closure. Unlike `global_mut`, this method provides
/// your closure with mutable access to the `AppContext` and the global simultaneously.
pub fn update_global<G: 'static, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
pub fn update_global<G: Global, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R {
self.update(|cx| {
let mut global = cx.lease_global::<G>();
let result = f(&mut global, cx);
@@ -905,7 +905,7 @@ impl AppContext {
}
/// Register a callback to be invoked when a global of the given type is updated.
pub fn observe_global<G: 'static>(
pub fn observe_global<G: Global>(
&mut self,
mut f: impl FnMut(&mut Self) + 'static,
) -> Subscription {
@@ -921,7 +921,7 @@ impl AppContext {
}
/// Move the global of the given type to the stack.
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
pub(crate) fn lease_global<G: Global>(&mut self) -> GlobalLease<G> {
GlobalLease::new(
self.globals_by_type
.remove(&TypeId::of::<G>())
@@ -931,7 +931,7 @@ impl AppContext {
}
/// Restore the global of the given type after it is moved to the stack.
pub(crate) fn end_global_lease<G: 'static>(&mut self, lease: GlobalLease<G>) {
pub(crate) fn end_global_lease<G: Global>(&mut self, lease: GlobalLease<G>) {
let global_type = TypeId::of::<G>();
self.push_effect(Effect::NotifyGlobalObservers { global_type });
self.globals_by_type.insert(global_type, lease.global);
@@ -1293,12 +1293,12 @@ pub(crate) enum Effect {
}
/// Wraps a global variable value during `update_global` while the value has been moved to the stack.
pub(crate) struct GlobalLease<G: 'static> {
pub(crate) struct GlobalLease<G: Global> {
global: Box<dyn Any>,
global_type: PhantomData<G>,
}
impl<G: 'static> GlobalLease<G> {
impl<G: Global> GlobalLease<G> {
fn new(global: Box<dyn Any>) -> Self {
GlobalLease {
global,
@@ -1307,7 +1307,7 @@ impl<G: 'static> GlobalLease<G> {
}
}
impl<G: 'static> Deref for GlobalLease<G> {
impl<G: Global> Deref for GlobalLease<G> {
type Target = G;
fn deref(&self) -> &Self::Target {
@@ -1315,7 +1315,7 @@ impl<G: 'static> Deref for GlobalLease<G> {
}
}
impl<G: 'static> DerefMut for GlobalLease<G> {
impl<G: Global> DerefMut for GlobalLease<G> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.global.downcast_mut().unwrap()
}
+7 -7
View File
@@ -1,6 +1,6 @@
use crate::{
AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, DismissEvent,
FocusableView, ForegroundExecutor, Model, ModelContext, Render, Result, Task, View,
FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render, Result, Task, View,
ViewContext, VisualContext, WindowContext, WindowHandle,
};
use anyhow::{anyhow, Context as _};
@@ -144,7 +144,7 @@ impl AsyncAppContext {
/// Determine whether global state of the specified type has been assigned.
/// Returns an error if the `AppContext` has been dropped.
pub fn has_global<G: 'static>(&self) -> Result<bool> {
pub fn has_global<G: Global>(&self) -> Result<bool> {
let app = self
.app
.upgrade()
@@ -157,7 +157,7 @@ impl AsyncAppContext {
///
/// Panics if no global state of the specified type has been assigned.
/// Returns an error if the `AppContext` has been dropped.
pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
let app = self
.app
.upgrade()
@@ -172,7 +172,7 @@ impl AsyncAppContext {
/// if no state of the specified type has been assigned.
///
/// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped.
pub fn try_read_global<G: 'static, R>(
pub fn try_read_global<G: Global, R>(
&self,
read: impl FnOnce(&G, &AppContext) -> R,
) -> Option<R> {
@@ -183,7 +183,7 @@ impl AsyncAppContext {
/// A convenience method for [AppContext::update_global]
/// for updating the global state of the specified type.
pub fn update_global<G: 'static, R>(
pub fn update_global<G: Global, R>(
&mut self,
update: impl FnOnce(&mut G, &mut AppContext) -> R,
) -> Result<R> {
@@ -235,7 +235,7 @@ impl AsyncWindowContext {
}
/// A convenience method for [`AppContext::global`].
pub fn read_global<G: 'static, R>(
pub fn read_global<G: Global, R>(
&mut self,
read: impl FnOnce(&G, &WindowContext) -> R,
) -> Result<R> {
@@ -249,7 +249,7 @@ impl AsyncWindowContext {
update: impl FnOnce(&mut G, &mut WindowContext) -> R,
) -> Result<R>
where
G: 'static,
G: Global,
{
self.window.update(self, |_, cx| cx.update_global(update))
}
+2 -2
View File
@@ -1,6 +1,6 @@
use crate::{
AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId,
EventEmitter, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle,
EventEmitter, Global, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle,
};
use anyhow::Result;
use derive_more::{Deref, DerefMut};
@@ -193,7 +193,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
/// Updates the given global
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where
G: 'static,
G: Global,
{
let mut global = self.app.lease_global::<G>();
let result = f(&mut global, self);
+7 -7
View File
@@ -1,8 +1,8 @@
use crate::{
Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter,
ForegroundExecutor, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform, Point,
Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View,
ForegroundExecutor, Global, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform,
Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View,
ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
};
use anyhow::{anyhow, bail};
@@ -256,20 +256,20 @@ impl TestAppContext {
}
/// true if the given global is defined
pub fn has_global<G: 'static>(&self) -> bool {
pub fn has_global<G: Global>(&self) -> bool {
let app = self.app.borrow();
app.has_global::<G>()
}
/// runs the given closure with a reference to the global
/// panics if `has_global` would return false.
pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
let app = self.app.borrow();
read(app.global(), &app)
}
/// runs the given closure with a reference to the global (if set)
pub fn try_read_global<G: 'static, R>(
pub fn try_read_global<G: Global, R>(
&self,
read: impl FnOnce(&G, &AppContext) -> R,
) -> Option<R> {
@@ -278,13 +278,13 @@ impl TestAppContext {
}
/// sets the global in this context.
pub fn set_global<G: 'static>(&mut self, global: G) {
pub fn set_global<G: Global>(&mut self, global: G) {
let mut lock = self.app.borrow_mut();
lock.set_global(global);
}
/// updates the global in this context. (panics if `has_global` would return false)
pub fn update_global<G: 'static, R>(
pub fn update_global<G: Global, R>(
&mut self,
update: impl FnOnce(&mut G, &mut AppContext) -> R,
) -> R {
+7 -5
View File
@@ -17,11 +17,11 @@
use crate::{
point, px, size, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, Bounds,
ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, IntoElement,
IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render, ScrollWheelEvent,
SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task, View, Visibility,
WindowContext,
ClickEvent, DispatchPhase, Element, ElementContext, ElementId, FocusHandle, Global,
IntoElement, IsZero, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
ScrollWheelEvent, SharedString, Size, StackingOrder, Style, StyleRefinement, Styled, Task,
View, Visibility, WindowContext,
};
use collections::HashMap;
@@ -2070,6 +2070,8 @@ impl ElementClickedState {
#[derive(Default)]
pub(crate) struct GroupBounds(HashMap<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
impl Global for GroupBounds {}
impl GroupBounds {
pub fn get(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
cx.default_global::<Self>()
+7 -2
View File
@@ -258,14 +258,14 @@ pub trait EventEmitter<E: Any>: 'static {}
/// can be used interchangeably.
pub trait BorrowAppContext {
/// Set a global value on the context.
fn set_global<T: 'static>(&mut self, global: T);
fn set_global<T: Global>(&mut self, global: T);
}
impl<C> BorrowAppContext for C
where
C: BorrowMut<AppContext>,
{
fn set_global<G: 'static>(&mut self, global: G) {
fn set_global<G: Global>(&mut self, global: G) {
self.borrow_mut().set_global(global)
}
}
@@ -287,3 +287,8 @@ impl<T> Flatten<T> for Result<T> {
self
}
}
/// A marker trait for types that can be stored in GPUI's global state.
///
/// Implement this on types you want to store in the context as a global.
pub trait Global: 'static {}
+4 -2
View File
@@ -3,8 +3,8 @@ use std::{iter, mem, ops::Range};
use crate::{
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
CursorStyle, DefiniteLength, Edges, EdgesRefinement, ElementContext, Font, FontFeatures,
FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size,
SizeRefinement, Styled, TextRun,
FontStyle, FontWeight, Global, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SharedString, Size, SizeRefinement, Styled, TextRun,
};
use collections::HashSet;
use refineable::Refineable;
@@ -20,6 +20,8 @@ pub use taffy::style::{
/// GPUI.
pub struct DebugBelow;
impl Global for DebugBelow {}
/// The CSS styling that can be applied to an element via the `Styled` trait
#[derive(Clone, Refineable, Debug)]
#[refineable(Debug)]
+5 -5
View File
@@ -2,7 +2,7 @@ use crate::{
px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId,
DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchMode,
Global, GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchMode,
KeymatchResult, Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton,
MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
PlatformWindow, Point, PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet,
@@ -708,7 +708,7 @@ impl<'a> WindowContext<'a> {
/// access both to the global and the context.
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where
G: 'static,
G: Global,
{
let mut global = self.app.lease_global::<G>();
let result = f(&mut global, self);
@@ -1441,7 +1441,7 @@ impl<'a> WindowContext<'a> {
/// Register the given handler to be invoked whenever the global of the given type
/// is updated.
pub fn observe_global<G: 'static>(
pub fn observe_global<G: Global>(
&mut self,
f: impl Fn(&mut WindowContext<'_>) + 'static,
) -> Subscription {
@@ -2198,7 +2198,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
/// Updates the global state of the given type.
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
where
G: 'static,
G: Global,
{
let mut global = self.app.lease_global::<G>();
let result = f(&mut global, self);
@@ -2207,7 +2207,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
}
/// Register a callback to be invoked when the given global state changes.
pub fn observe_global<G: 'static>(
pub fn observe_global<G: Global>(
&mut self,
mut f: impl FnMut(&mut V, &mut ViewContext<'_, V>) + 'static,
) -> Subscription {