Skip to main content

gpui_macros/
gpui_macros.rs

1//! Procedural macros for GPUI.
2//!
3//! `gpui` re-exports these macros, so use them as `gpui::test`,
4//! `#[derive(gpui::Action)]`, and so on. Application crates do not need a
5//! direct dependency on this crate.
6//!
7//! Derives:
8//!
9//! - `Action`: implements `gpui::Action` for a type that you can bind to keys.
10//! - `IntoElement`, `Render`: implement the traits that let a type enter the element tree.
11//! - `Styled`, `ParentElement`, `InteractiveElement`, `StatefulInteractiveElement`:
12//!   implement the builder traits for a type that holds a field for them.
13//! - `AppContext`, `VisualContext`: forward context methods from a wrapper around
14//!   `&mut App` or `&mut Window`.
15//!
16//! Attributes and function-like macros:
17//!
18//! - `#[gpui::test]`, `#[gpui::property_test]` and `#[gpui::bench]` define
19//!   tests and benchmarks that run with GPUI support.
20//! - `register_action!` registers a hand-written `Action` impl.
21//! - The `*_style_methods!` macros generate the builder methods of `Styled`.
22
23mod bench;
24mod derive_action;
25mod derive_app_context;
26mod derive_element_traits;
27mod derive_into_element;
28mod derive_render;
29mod derive_visual_context;
30mod property_test;
31mod register_action;
32mod styles;
33mod test;
34
35#[cfg(any(feature = "inspector", debug_assertions))]
36mod derive_inspector_reflection;
37
38use proc_macro::TokenStream;
39use syn::{DeriveInput, Ident};
40
41/// Derives `gpui::Action` for a type and registers it with the runtime.
42///
43/// Use it for any type that a keymap or the command palette should be able to
44/// name. The type must implement `Clone` and `PartialEq`. Unless the type is a
45/// unit struct or `no_json` is set, it must also implement `serde::Deserialize`
46/// and `schemars::JsonSchema`, so that keymaps can build it from JSON.
47///
48/// The doc comment on the type becomes the action's documentation. The
49/// `#[action(...)]` attribute sets the name, namespace, aliases and
50/// deprecation. See `gpui::Action` for the full attribute reference.
51///
52/// Use [`register_action!`] if you implement `gpui::Action` by hand.
53#[proc_macro_derive(Action, attributes(action))]
54pub fn derive_action(input: TokenStream) -> TokenStream {
55    derive_action::derive_action(input)
56}
57
58/// Registers a type that implements `gpui::Action` by hand.
59///
60/// Use this when `#[derive(Action)]` doesn't fit, such as when you need a
61/// custom `build` or `name`. Pass the type's name, which must be in scope in
62/// the module that calls the macro. Call it once per type, in the same crate
63/// that defines the type, because the macro adds an inherent `impl` block to
64/// the type.
65///
66/// `register_action!(MyAction);`
67#[proc_macro]
68pub fn register_action(ident: TokenStream) -> TokenStream {
69    register_action::register_action(ident)
70}
71
72/// Derives `gpui::IntoElement` for a type that implements `gpui::RenderOnce`.
73///
74/// The generated `into_element` wraps the value in a `gpui::ViewElement`, which
75/// renders it as part of the element tree. This lets you pass the value
76/// directly as a child of another element.
77#[proc_macro_derive(IntoElement)]
78pub fn derive_into_element(input: TokenStream) -> TokenStream {
79    derive_into_element::derive_into_element(input)
80}
81
82/// Derives `gpui::Styled` for a struct with a `#[style]` field.
83///
84/// The field must have the type `gpui::StyleRefinement`. The derived `style`
85/// method returns that field, so the builder methods such as `flex` and `gap`
86/// write to it. Mark at most one field.
87#[proc_macro_derive(Styled, attributes(style))]
88pub fn derive_styled(input: TokenStream) -> TokenStream {
89    derive_element_traits::derive_styled(input)
90}
91
92/// Derives `gpui::ParentElement` for a struct with a `#[children]` field.
93///
94/// The field must be a collection that has an `extend` method that accepts
95/// `gpui::AnyElement` values, typically a `Vec<AnyElement>`. Its `child` and
96/// `children` methods add to it. Mark at most one field.
97#[proc_macro_derive(ParentElement, attributes(children))]
98pub fn derive_parent_element(input: TokenStream) -> TokenStream {
99    derive_element_traits::derive_parent_element(input)
100}
101
102/// Derives `gpui::InteractiveElement` for a struct with an `#[interactivity]`
103/// field.
104///
105/// The field must have the type `gpui::Interactivity`. Mark at most one field.
106#[proc_macro_derive(InteractiveElement, attributes(interactivity))]
107pub fn derive_interactive_element(input: TokenStream) -> TokenStream {
108    derive_element_traits::derive_interactive_element(input)
109}
110
111/// Derives `gpui::StatefulInteractiveElement`, which adds methods such as `role`.
112///
113/// The derive writes an empty impl, so the trait's default methods become available.
114/// The type must also implement `gpui::InteractiveElement`, which you can derive with
115/// `#[derive(InteractiveElement)]`.
116#[proc_macro_derive(StatefulInteractiveElement)]
117pub fn derive_stateful_interactive_element(input: TokenStream) -> TokenStream {
118    derive_element_traits::derive_stateful_interactive_element(input)
119}
120
121/// Derives `gpui::Render` with a `render` method that returns `gpui::Empty`.
122///
123/// Use it for a view that holds state but draws nothing. Write `impl Render`
124/// by hand for any view that draws something.
125#[proc_macro_derive(Render)]
126#[doc(hidden)]
127pub fn derive_render(input: TokenStream) -> TokenStream {
128    derive_render::derive_render(input)
129}
130
131/// Derives `gpui::AppContext` for a struct that holds a `&mut gpui::App`.
132///
133/// Mark the field that holds the `&mut App` with `#[app]`. The derived methods
134/// forward to that field, so the struct can be used wherever an `AppContext`
135/// is accepted. The attribute is required.
136///
137/// Without the attribute the derive fails to compile:
138///
139/// ```compile_fail
140/// # #[macro_use] extern crate gpui_macros;
141/// # #[macro_use] extern crate gpui;
142/// #[derive(AppContext)]
143/// struct MyContext<'a> {
144///     app: &'a mut gpui::App
145/// }
146/// ```
147#[proc_macro_derive(AppContext, attributes(app))]
148pub fn derive_app_context(input: TokenStream) -> TokenStream {
149    derive_app_context::derive_app_context(input)
150}
151
152/// Derives `gpui::VisualContext` for a struct that holds a `&mut gpui::App`
153/// and a `&mut gpui::Window`.
154///
155/// Mark the `&mut App` field with `#[app]` and the `&mut Window` field with
156/// `#[window]`. Both attributes are required. The derived methods forward to
157/// those fields.
158///
159/// Failing to add either attribute causes a compile error:
160///
161/// ```compile_fail
162/// # #[macro_use] extern crate gpui_macros;
163/// # #[macro_use] extern crate gpui;
164/// #[derive(VisualContext)]
165/// struct MyContext<'a, 'b> {
166///     #[app]
167///     app: &'a mut gpui::App,
168///     window: &'b mut gpui::Window
169/// }
170/// ```
171///
172/// ```compile_fail
173/// # #[macro_use] extern crate gpui_macros;
174/// # #[macro_use] extern crate gpui;
175/// #[derive(VisualContext)]
176/// struct MyContext<'a, 'b> {
177///     app: &'a mut gpui::App,
178///     #[window]
179///     window: &'b mut gpui::Window
180/// }
181/// ```
182#[proc_macro_derive(VisualContext, attributes(window, app))]
183pub fn derive_visual_context(input: TokenStream) -> TokenStream {
184    derive_visual_context::derive_visual_context(input)
185}
186
187/// Generates the style helper methods on `Styled`. Used inside `gpui`.
188#[proc_macro]
189#[doc(hidden)]
190pub fn style_helpers(input: TokenStream) -> TokenStream {
191    styles::style_helpers(input)
192}
193
194/// Generates the style transition builder and its application code. Used inside `gpui`.
195#[proc_macro]
196#[doc(hidden)]
197pub fn style_transitions(input: TokenStream) -> TokenStream {
198    styles::style_transitions(input)
199}
200
201/// Generates the `visible` and `invisible` methods for `Styled`.
202///
203/// Call it in the body of the `Styled` trait. The optional
204/// `{ visibility: pub }` argument sets the visibility of the generated methods.
205#[proc_macro]
206pub fn visibility_style_methods(input: TokenStream) -> TokenStream {
207    styles::visibility_style_methods(input)
208}
209
210/// Generates the margin methods for `Styled`, such as `m_4` and `mx_auto`.
211///
212/// Call it in the body of the `Styled` trait. The optional
213/// `{ visibility: pub }` argument sets the visibility of the generated methods.
214#[proc_macro]
215pub fn margin_style_methods(input: TokenStream) -> TokenStream {
216    styles::margin_style_methods(input)
217}
218
219/// Generates the padding methods for `Styled`, such as `p_4` and `px_2`.
220///
221/// Call it in the body of the `Styled` trait. The optional
222/// `{ visibility: pub }` argument sets the visibility of the generated methods.
223#[proc_macro]
224pub fn padding_style_methods(input: TokenStream) -> TokenStream {
225    styles::padding_style_methods(input)
226}
227
228/// Generates the position methods for `Styled`, such as `relative` and `absolute`.
229///
230/// Call it in the body of the `Styled` trait. The optional
231/// `{ visibility: pub }` argument sets the visibility of the generated methods.
232#[proc_macro]
233pub fn position_style_methods(input: TokenStream) -> TokenStream {
234    styles::position_style_methods(input)
235}
236
237/// Generates the overflow methods for `Styled`, such as `overflow_hidden`.
238///
239/// Call it in the body of the `Styled` trait. The optional
240/// `{ visibility: pub }` argument sets the visibility of the generated methods.
241#[proc_macro]
242pub fn overflow_style_methods(input: TokenStream) -> TokenStream {
243    styles::overflow_style_methods(input)
244}
245
246/// Generates the cursor methods for `Styled`, such as `cursor_pointer`.
247///
248/// Call it in the body of the `Styled` trait. The optional
249/// `{ visibility: pub }` argument sets the visibility of the generated methods.
250#[proc_macro]
251pub fn cursor_style_methods(input: TokenStream) -> TokenStream {
252    styles::cursor_style_methods(input)
253}
254
255/// Generates the border methods for `Styled`, such as `border_1` and `border_color`.
256///
257/// Call it in the body of the `Styled` trait. The optional
258/// `{ visibility: pub }` argument sets the visibility of the generated methods.
259#[proc_macro]
260pub fn border_style_methods(input: TokenStream) -> TokenStream {
261    styles::border_style_methods(input)
262}
263
264/// Generates the box-shadow and corner methods for `Styled`.
265///
266/// Call it in the body of the `Styled` trait. The optional
267/// `{ visibility: pub }` argument sets the visibility of the generated methods.
268#[proc_macro]
269pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
270    styles::box_shadow_style_methods(input)
271}
272
273/// Marks a function as a GPUI test. The generated function is a `#[test]`, so
274/// it runs under `cargo test` or `cargo nextest`.
275///
276/// The function can be synchronous or `async`. Its parameters decide what it
277/// receives:
278///
279/// - `&mut TestAppContext` (or `&TestAppContext`): a fresh test context. Use one
280///   per app you need. Any number is allowed.
281/// - `&mut App`: a synchronous test only. The test gets the app directly.
282/// - `BackgroundExecutor`: an `async` test only. An executor for the test's
283///   scheduler.
284/// - `StdRng`: a random number generator seeded with the current run's seed.
285///   Use it to vary inputs. Each run sees the same values for the same seed, so
286///   changing the seed explores other interleavings.
287///
288/// Any other parameter type is a compile error.
289///
290/// An `async` test takes a context, for example
291/// `async fn test_counter(cx: &mut TestAppContext)`, and runs inside the test
292/// scheduler.
293///
294/// Your crate needs `rand` as a dev-dependency, because the generated code
295/// calls `rand::SeedableRng` to seed a `StdRng` parameter.
296///
297/// # Arguments
298///
299/// - `#[gpui::test]` with no arguments runs once, with seed `0` or the value of
300///   the `SEED` environment variable.
301/// - `#[gpui::test(seed = 10)]` runs once with seed `10`.
302/// - `#[gpui::test(seeds(10, 20, 30))]` runs once for each of seeds `10`, `20`
303///   and `30`.
304/// - `#[gpui::test(iterations = 5)]` runs with seeds `0` to `4`.
305/// - `#[gpui::test(retries = 3)]` retries a failing seed up to three more times,
306///   so a seed runs at most four times. The retries are per seed.
307/// - `#[gpui::test(on_failure = "crate::test::report_failure")]` calls the named
308///   function with no arguments once a seed has failed on its final attempt,
309///   before the test panics. Use it to write out more details about the failure.
310///   The path must be a `fn()`.
311///
312/// `iterations` combines with `seed` and `seeds`. The iteration seeds run first,
313/// then the explicit seeds. For example, `#[gpui::test(iterations = 5, seed = 10)]`
314/// runs seeds `0, 1, 2, 3, 4, 10`.
315///
316/// When a run has more than one seed, the macro prints each seed to stderr. It
317/// prints the failing seed too, along with how to rerun it.
318///
319/// # Environment Variables
320///
321/// - `SEED`: runs only that seed (or `SEED` through `SEED + iterations - 1` when
322///   `iterations` is set). It overrides every explicit seed.
323/// - `ITERATIONS`: overrides the value of the `iterations` argument.
324#[proc_macro_attribute]
325pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
326    test::test(args, function)
327}
328
329/// Turns a function into a Criterion benchmark that runs with GPUI support.
330///
331/// The function takes a `&mut gpui::BenchAppContext`, and it is not `async`.
332/// Put the work to measure in the body, and the macro measures it with
333/// Criterion. Benchmarks with an `inputs` argument take the input as their
334/// first parameter, before the context: `fn bench(input: T, cx: &mut BenchAppContext)`.
335///
336/// The macro generates a function that takes `&mut criterion::Criterion`, so
337/// register it with `criterion_group!` and `criterion_main!` in the benchmark
338/// target. Each benchmark prints a GPUI report when it finishes.
339///
340/// # Arguments
341///
342/// - `fps = N` sets the frame rate that the report budgets against. `N` must be
343///   greater than zero. The default comes from `gpui::BenchReport`.
344/// - `inputs = EXPR` runs the benchmark once for each item of `EXPR`, using
345///   Criterion's `bench_with_input`.
346/// - `input_name = "..."` sets the name of each input in the report. Requires
347///   `inputs`. The default is the function name.
348/// - `group = "..."` sets the name of the benchmark group. Requires `inputs`.
349///   The default is the function name.
350/// - `sample_size = N` sets the number of Criterion samples for the group.
351///   Requires `inputs`, and `N` must be greater than zero.
352///
353/// # Dependencies
354///
355/// The benchmark crate needs `criterion` and `gpui_platform` as dev-dependencies,
356/// because the generated code calls both. Enable the `bench-support` feature on
357/// `gpui`, which provides `BenchAppContext`.
358///
359/// A benchmark that takes a `StdRng` parameter (by value, in any position) is run
360/// once per seed, with the RNG seeded from it. Seeds are chosen exactly as for
361/// `#[gpui::test]`: `seed = N`, `seeds(...)`, and `iterations = N` take the same
362/// meaning, and the `SEED` and `ITERATIONS` environment variables override them the
363/// same way, so one `SEED` reproduces a test or a benchmark. Each seed is its own
364/// Criterion benchmark, named `<input>/seed-<seed>` (or `<function>/seed-<seed>`
365/// without inputs), and the RNG is rebuilt for every Criterion routine call so all
366/// samples measure the same seed. The generated code seeds it through gpui's own
367/// `rand`, so the crate needs `rand` only to name `StdRng` in the signature.
368///
369/// ```ignore
370/// #[gpui::bench(inputs = families(), iterations = 6)]
371/// fn full_refresh(family: &Family, mut rng: StdRng, cx: &mut BenchAppContext) {
372///     let tree = family.sample(&mut rng);
373///     // ...
374/// }
375/// ```
376///
377/// The benchmark crate must add `criterion` and `gpui_platform` (with its
378/// `test-support` feature) to its dev-dependencies and enable gpui's `bench`
379/// feature, since the generated code references all three.
380#[proc_macro_attribute]
381pub fn bench(args: TokenStream, function: TokenStream) -> TokenStream {
382    bench::bench(args, function)
383}
384
385/// A variant of `#[gpui::test]` that runs a test with many generated inputs.
386///
387/// A property test checks a claim for many generated inputs. For example, a
388/// function with `x: i32` and `y: i32` runs for generated pairs of integers;
389/// the test body should hold for every pair.
390///
391/// Standard GPUI randomized tests provide you with an instance of `StdRng` to
392/// generate random data in a controlled manner. Property-based tests have some
393/// advantages, however:
394/// - Shrinking reduces a failing input to a smaller case that still fails.
395/// - The harness generates values, so the test body contains only the property.
396/// - Failing cases are saved to a file and replayed before new cases.
397///
398/// Property tests work best when all inputs can be generated up-front and kept
399/// in a simple data structure. Sometimes, this isn't possible - for example, if
400/// a test needs to make a random decision based on the current state of some
401/// structure. In this case, a standard GPUI randomized test may be more
402/// suitable.
403///
404/// ## Customizing random values
405///
406/// This macro is based on the [`#[proptest::property_test]`] macro, but handles
407/// some of the same GPUI-specific arguments as `#[gpui::test]`. Specifically,
408/// `&{mut,} TestAppContext` and `BackgroundExecutor` work as normal. `StdRng`
409/// arguments are **explicitly forbidden**, since they break shrinking, and are
410/// a common footgun.
411///
412/// All other arguments are forwarded to the underlying proptest macro.
413///
414/// The code that proptest's macro generates names the `proptest` crate directly, so a crate that
415/// uses this macro needs `proptest`, with its `attr-macro` feature, in its dev-dependencies.
416///
417/// The following terms come from proptest's
418/// [`#[proptest::property_test]`] documentation.
419///
420/// Random values of type `T` are generated by a `Strategy<Value = T>` object.
421/// Some types have a canonical `Strategy` - these types also implement
422/// `Arbitrary`. Parameters to a `#[gpui::property_test]`, by default, use a
423/// type's `Arbitrary` implementation. If you'd like to provide a custom
424/// strategy, put `#[strategy = ...]` on the argument. For example,
425/// `#[strategy = 1..10] x: i32` draws `x` from the range `1..10`.
426///
427/// For more information on writing custom `Strategy` and `Arbitrary`
428/// implementations, see [the proptest book][book], and the [`Strategy`] trait.
429///
430/// ## Scheduler
431///
432/// Each case receives a scheduler seed, which the macro supplies as a parameter
433/// that doesn't shrink, because all seeds are equally simple. Without `SEED`,
434/// the case seeds come from a proptest generator fixed at `0`, so runs are
435/// reproducible. If `SEED` is set, every case uses that value as its scheduler
436/// seed, and the proptest generator is seeded with it too. To control other
437/// values, use custom `Strategy`s.
438///
439/// [`#[proptest::property_test]`]: https://docs.rs/proptest/latest/proptest/attr.property_test.html
440/// [book]: https://proptest-rs.github.io/proptest/intro.html
441/// [`Strategy`]: https://docs.rs/proptest/latest/proptest/strategy/trait.Strategy.html
442#[proc_macro_attribute]
443pub fn property_test(args: TokenStream, function: TokenStream) -> TokenStream {
444    property_test::test(args.into(), function.into()).into()
445}
446
447/// When added to a trait, `#[derive_inspector_reflection]` generates a module which provides
448/// enumeration and lookup by name of all methods that have the shape `fn method(self) -> Self`.
449/// This is used by the inspector so that it can use the builder methods in `Styled` and
450/// `StyledExt`.
451///
452/// The generated module is named `<snake_case_trait_name>_reflection`. It has two
453/// generic functions, `methods::<T>()`, which returns every reflected method
454/// as a `Vec<gpui::inspector_reflection::FunctionReflection<T>>`, and
455/// `find_method::<T>(name)`, which looks one up by name and returns an
456/// `Option` of the same type. `T` must implement the trait and be `'static`.
457///
458/// The `invoke` method on `FunctionReflection` runs the method. `FunctionReflection`
459/// also provides the method's documentation.
460///
461/// This attribute is only compiled with the `inspector` feature or in debug builds.
462#[cfg(any(feature = "inspector", debug_assertions))]
463#[proc_macro_attribute]
464pub fn derive_inspector_reflection(_args: TokenStream, input: TokenStream) -> TokenStream {
465    derive_inspector_reflection::derive_inspector_reflection(_args, input)
466}
467
468pub(crate) fn get_simple_attribute_field(ast: &DeriveInput, name: &'static str) -> Option<Ident> {
469    match &ast.data {
470        syn::Data::Struct(data_struct) => data_struct
471            .fields
472            .iter()
473            .find(|field| field.attrs.iter().any(|attr| attr.path().is_ident(name)))
474            .map(|field| field.ident.clone().unwrap()),
475        syn::Data::Enum(_) => None,
476        syn::Data::Union(_) => None,
477    }
478}