diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 4511ffe407..4f2d5179db 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -1015,7 +1015,6 @@ pub mod tests { .map(|i| i.parse().expect("invalid `OPERATIONS` variable")) .unwrap_or(10); - let _test_platform = &cx.test_platform; let mut tab_size = rng.gen_range(1..=4); let buffer_start_excerpt_header_height = rng.gen_range(1..=5); let excerpt_header_height = rng.gen_range(1..=5); diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 638396abc5..f84ae809d3 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -45,11 +45,13 @@ use util::{ /// Temporary(?) wrapper around [`RefCell`] to help us debug any double borrows. /// Strongly consider removing after stabilization. +#[doc(hidden)] pub struct AppCell { app: RefCell, } impl AppCell { + #[doc(hidden)] #[track_caller] pub fn borrow(&self) -> AppRef { if option_env!("TRACK_THREAD_BORROWS").is_some() { @@ -59,6 +61,7 @@ impl AppCell { AppRef(self.app.borrow()) } + #[doc(hidden)] #[track_caller] pub fn borrow_mut(&self) -> AppRefMut { if option_env!("TRACK_THREAD_BORROWS").is_some() { @@ -69,6 +72,7 @@ impl AppCell { } } +#[doc(hidden)] #[derive(Deref, DerefMut)] pub struct AppRef<'a>(Ref<'a, AppContext>); @@ -81,6 +85,7 @@ impl<'a> Drop for AppRef<'a> { } } +#[doc(hidden)] #[derive(Deref, DerefMut)] pub struct AppRefMut<'a>(RefMut<'a, AppContext>); @@ -93,6 +98,8 @@ impl<'a> Drop for AppRefMut<'a> { } } +/// A reference to a GPUI application, typically constructed in the `main` function of your app. +/// You won't interact with this type much outside of initial configuration and startup. pub struct App(Rc); /// Represents an application before it is fully launched. Once your app is @@ -136,6 +143,8 @@ impl App { self } + /// Invokes a handler when an already-running application is launched. + /// On macOS, this can occur when the application icon is double-clicked or the app is launched via the dock. pub fn on_reopen(&self, mut callback: F) -> &Self where F: 'static + FnMut(&mut AppContext), @@ -149,18 +158,22 @@ impl App { self } + /// Returns metadata associated with the application pub fn metadata(&self) -> AppMetadata { self.0.borrow().app_metadata.clone() } + /// Returns a handle to the [BackgroundExecutor] associated with this app, which can be used to spawn futures in the background. pub fn background_executor(&self) -> BackgroundExecutor { self.0.borrow().background_executor.clone() } + /// Returns a handle to the [ForegroundExecutor] associated with this app, which can be used to spawn futures in the foreground. pub fn foreground_executor(&self) -> ForegroundExecutor { self.0.borrow().foreground_executor.clone() } + /// Returns a reference to the [TextSystem] associated with this app. pub fn text_system(&self) -> Arc { self.0.borrow().text_system.clone() } @@ -174,12 +187,6 @@ type QuitHandler = Box LocalBoxFuture<'static, () type ReleaseListener = Box; type NewViewListener = Box; -// struct FrameConsumer { -// next_frame_callbacks: Vec, -// task: Task<()>, -// display_linker -// } - pub struct AppContext { pub(crate) this: Weak, pub(crate) platform: Rc, @@ -314,10 +321,12 @@ impl AppContext { } } + /// Gracefully quit the application via the platform's standard routine. pub fn quit(&mut self) { self.platform.quit(); } + /// Get metadata about the app and platform. pub fn app_metadata(&self) -> AppMetadata { self.app_metadata.clone() } @@ -340,6 +349,7 @@ impl AppContext { result } + /// Arrange a callback to be invoked when the given model or view calls `notify` on its respective context. pub fn observe( &mut self, entity: &E, @@ -355,7 +365,7 @@ impl AppContext { }) } - pub fn observe_internal( + pub(crate) fn observe_internal( &mut self, entity: &E, mut on_notify: impl FnMut(E, &mut AppContext) -> bool + 'static, @@ -380,15 +390,17 @@ impl AppContext { subscription } - pub fn subscribe( + /// Arrange for the given callback to be invoked whenever the given model or view emits an event of a given type. + /// The callback is provided a handle to the emitting entity and a reference to the emitted event. + pub fn subscribe( &mut self, entity: &E, - mut on_event: impl FnMut(E, &Evt, &mut AppContext) + 'static, + mut on_event: impl FnMut(E, &Event, &mut AppContext) + 'static, ) -> Subscription where - T: 'static + EventEmitter, + T: 'static + EventEmitter, E: Entity, - Evt: 'static, + Event: 'static, { self.subscribe_internal(entity, move |entity, event, cx| { on_event(entity, event, cx); @@ -426,6 +438,9 @@ impl AppContext { subscription } + /// Returns handles to all open windows in the application. + /// Each handle could be downcast to a handle typed for the root view of that window. + /// To find all windows of a given type, you could filter on pub fn windows(&self) -> Vec { self.windows .values() diff --git a/crates/gpui/src/app/async_context.rs b/crates/gpui/src/app/async_context.rs index 475ef76ef2..6afb356e5e 100644 --- a/crates/gpui/src/app/async_context.rs +++ b/crates/gpui/src/app/async_context.rs @@ -82,6 +82,7 @@ impl Context for AsyncAppContext { } impl AsyncAppContext { + /// Schedules all windows in the application to be redrawn. pub fn refresh(&mut self) -> Result<()> { let app = self .app @@ -92,14 +93,17 @@ impl AsyncAppContext { Ok(()) } + /// Get an executor which can be used to spawn futures in the background. pub fn background_executor(&self) -> &BackgroundExecutor { &self.background_executor } + /// Get an executor which can be used to spawn futures in the foreground. pub fn foreground_executor(&self) -> &ForegroundExecutor { &self.foreground_executor } + /// Invoke the given function in the context of the app, then flush any effects produced during its invocation. pub fn update(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result { let app = self .app @@ -109,6 +113,7 @@ impl AsyncAppContext { Ok(f(&mut lock)) } + /// Open a window with the given options based on the root view returned by the given function. pub fn open_window( &self, options: crate::WindowOptions, @@ -125,6 +130,7 @@ impl AsyncAppContext { Ok(lock.open_window(options, build_root_view)) } + /// Schedule a future to be polled in the background. pub fn spawn(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task where Fut: Future + 'static, diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 7c95f75d96..de31339b8d 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -15,11 +15,15 @@ use std::{future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration}; /// an implementation of `Context` with additional methods that are useful in tests. #[derive(Clone)] pub struct TestAppContext { + #[doc(hidden)] pub app: Rc, + #[doc(hidden)] pub background_executor: BackgroundExecutor, + #[doc(hidden)] pub foreground_executor: ForegroundExecutor, + #[doc(hidden)] pub dispatcher: TestDispatcher, - pub test_platform: Rc, + test_platform: Rc, text_system: Arc, } @@ -80,6 +84,7 @@ impl Context for TestAppContext { } impl TestAppContext { + /// Creates a new `TestAppContext`. Usually you can rely on `#[gpui::test]` to do this for you. pub fn new(dispatcher: TestDispatcher) -> Self { let arc_dispatcher = Arc::new(dispatcher.clone()); let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); @@ -138,8 +143,8 @@ impl TestAppContext { f(&*cx) } - // Adds a new window. The Window will always be backed by a `TestWindow` which - // can be retrieved with `self.test_window(handle)` + /// Adds a new window. The Window will always be backed by a `TestWindow` which + /// can be retrieved with `self.test_window(handle)` pub fn add_window(&mut self, build_window: F) -> WindowHandle where F: FnOnce(&mut ViewContext) -> V, @@ -149,7 +154,7 @@ impl TestAppContext { cx.open_window(WindowOptions::default(), |cx| cx.new_view(build_window)) } - // Adds a new window with no content. + /// Adds a new window with no content. pub fn add_empty_window(&mut self) -> AnyWindowHandle { let mut cx = self.app.borrow_mut(); cx.open_window(WindowOptions::default(), |cx| cx.new_view(|_| EmptyView {})) @@ -226,16 +231,20 @@ impl TestAppContext { self.foreground_executor.spawn(f(self.to_async())) } + /// true if the given global is defined pub fn has_global(&self) -> bool { let app = self.app.borrow(); app.has_global::() } + /// runs the given closure with a reference to the global + /// panics if `has_global` would return false. pub fn read_global(&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( &self, read: impl FnOnce(&G, &AppContext) -> R, @@ -244,11 +253,13 @@ impl TestAppContext { Some(read(lock.try_global()?, &lock)) } + /// sets the global in this context. pub fn set_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( &mut self, update: impl FnOnce(&mut G, &mut AppContext) -> R, @@ -522,10 +533,10 @@ impl View { } } -/// A VisualTestContext is the test-equivalent of a `WindowContext`. It allows you to -/// run window-specific test code. use derive_more::{Deref, DerefMut}; #[derive(Deref, DerefMut, Clone)] +/// A VisualTestContext is the test-equivalent of a `WindowContext`. It allows you to +/// run window-specific test code. pub struct VisualTestContext { #[deref] #[deref_mut] @@ -534,6 +545,7 @@ pub struct VisualTestContext { } impl<'a> VisualTestContext { + /// Provides the `WindowContext` for the duration of the closure. pub fn update(&mut self, f: impl FnOnce(&mut WindowContext) -> R) -> R { self.cx.update_window(self.window, |_, cx| f(cx)).unwrap() } @@ -723,6 +735,7 @@ impl VisualContext for VisualTestContext { } impl AnyWindowHandle { + /// Creates the given view in this window. pub fn build_view( &self, cx: &mut TestAppContext, @@ -732,6 +745,7 @@ impl AnyWindowHandle { } } +/// An EmptyView for testing. pub struct EmptyView {} impl Render for EmptyView { diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index ec3d7a0ff0..c4452a593a 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -15,6 +15,7 @@ use std::{ time::Duration, }; +/// TestPlatform implements the Platform trait for use in tests. pub struct TestPlatform { background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, diff --git a/crates/gpui_macros/src/gpui_macros.rs b/crates/gpui_macros/src/gpui_macros.rs index f0cd59908d..1187d96ca3 100644 --- a/crates/gpui_macros/src/gpui_macros.rs +++ b/crates/gpui_macros/src/gpui_macros.rs @@ -7,26 +7,56 @@ mod test; use proc_macro::TokenStream; #[proc_macro] +/// register_action! can be used to register an action with the GPUI runtime. +/// You should typically use `gpui::actions!` or `gpui::impl_actions!` instead, +/// but this can be used for fine grained customization. pub fn register_action(ident: TokenStream) -> TokenStream { register_action::register_action_macro(ident) } #[proc_macro_derive(IntoElement)] +// #[derive(IntoElement)] is used to create a Component out of anything that implements +// the `RenderOnce` trait. pub fn derive_into_element(input: TokenStream) -> TokenStream { derive_into_element::derive_into_element(input) } #[proc_macro_derive(Render)] +#[doc(hidden)] pub fn derive_render(input: TokenStream) -> TokenStream { derive_render::derive_render(input) } +// Used by gpui to generate the style helpers. #[proc_macro] +#[doc(hidden)] pub fn style_helpers(input: TokenStream) -> TokenStream { style_helpers::style_helpers(input) } #[proc_macro_attribute] +/// #[gpui::test] can be used to annotate test functions that run with GPUI support. +/// it supports both synchronous and asynchronous tests, and can provide you with +/// as many `TestAppContext` instances as you need. +/// The output contains a `#[test]` annotation so this can be used with any existing +/// test harness (`cargo test` or `cargo-nextest`). +/// +/// ``` +/// #[gpui::test] +/// async fn test_foo(mut cx: &TestAppContext) { } +/// ``` +/// +/// In addition to passing a TestAppContext, you can also ask for a `StdRnd` instance. +/// this will be seeded with the `SEED` environment variable and is used internally by +/// the ForegroundExecutor and BackgroundExecutor to run tasks deterministically in tests. +/// Using the same `StdRng` for behaviour in your test will allow you to exercise a wide +/// variety of scenarios and interleavings just by changing the seed. +/// +/// #[gpui::test] also takes three different arguments: +/// - `#[gpui::test(interations=10)]` will run the test ten times with a different initial SEED. +/// - `#[gpui::test(retries=3)]` will run the test up to four times if it fails to try and make it pass. +/// - `#[gpui::test(on_failure="crate::test::report_failure")]` will call the specified function after the +/// tests fail so that you can write out more detail about the failure. pub fn test(args: TokenStream, function: TokenStream) -> TokenStream { test::test(args, function) }