Skip to main content

gpui_macros/
gpui_macros.rs

1mod gpui_pre_facade_paths;
2mod bench;
3mod derive_action;
4mod derive_app_context;
5mod derive_into_element;
6mod derive_render;
7mod derive_visual_context;
8mod property_test;
9mod register_action;
10mod styles;
11mod test;
12
13#[cfg(any(feature = "inspector", debug_assertions))]
14mod derive_inspector_reflection;
15
16use proc_macro::TokenStream;
17use syn::{DeriveInput, Ident};
18
19/// `Action` derive macro - see the trait documentation for details.
20#[proc_macro_derive(Action, attributes(action))]
21pub fn derive_action(input: TokenStream) -> TokenStream {
22    gpui_pre_facade_paths::rewrite(__gpui_pre_derive_action(input))
23}
24
25fn __gpui_pre_derive_action(input: TokenStream) -> TokenStream {
26    derive_action::derive_action(input)
27}
28
29/// This can be used to register an action with the GPUI runtime when you want to manually implement
30/// the `Action` trait. Typically you should use the `Action` derive macro or `actions!` macro
31/// instead.
32#[proc_macro]
33pub fn register_action(ident: TokenStream) -> TokenStream {
34    gpui_pre_facade_paths::rewrite(__gpui_pre_register_action(ident))
35}
36
37fn __gpui_pre_register_action(ident: TokenStream) -> TokenStream {
38    register_action::register_action(ident)
39}
40
41/// #[derive(IntoElement)] generates an `IntoElement` impl for any `RenderOnce`
42/// type, wrapping it in a `ViewElement` so it can be used as a child.
43#[proc_macro_derive(IntoElement)]
44pub fn derive_into_element(input: TokenStream) -> TokenStream {
45    gpui_pre_facade_paths::rewrite(__gpui_pre_derive_into_element(input))
46}
47
48fn __gpui_pre_derive_into_element(input: TokenStream) -> TokenStream {
49    derive_into_element::derive_into_element(input)
50}
51
52#[proc_macro_derive(Render)]
53#[doc(hidden)]
54pub fn derive_render(input: TokenStream) -> TokenStream {
55    gpui_pre_facade_paths::rewrite(__gpui_pre_derive_render(input))
56}
57
58fn __gpui_pre_derive_render(input: TokenStream) -> TokenStream {
59    derive_render::derive_render(input)
60}
61
62/// #[derive(AppContext)] is used to create a context out of anything that holds a `&mut App`
63/// Note that a `#[app]` attribute is required to identify the variable holding the &mut App.
64///
65/// Failure to add the attribute causes a compile error:
66///
67/// ```compile_fail
68/// # #[macro_use] extern crate gpui_macros;
69/// # #[macro_use] extern crate gpui;
70/// #[derive(AppContext)]
71/// struct MyContext<'a> {
72///     app: &'a mut gpui::App
73/// }
74/// ```
75#[proc_macro_derive(AppContext, attributes(app))]
76pub fn derive_app_context(input: TokenStream) -> TokenStream {
77    gpui_pre_facade_paths::rewrite(__gpui_pre_derive_app_context(input))
78}
79
80fn __gpui_pre_derive_app_context(input: TokenStream) -> TokenStream {
81    derive_app_context::derive_app_context(input)
82}
83
84/// #[derive(VisualContext)] is used to create a visual context out of anything that holds a `&mut Window` and
85/// implements `AppContext`
86/// Note that a `#[app]` and a `#[window]` attribute are required to identify the variables holding the &mut App,
87/// and &mut Window respectively.
88///
89/// Failure to add both attributes causes a compile error:
90///
91/// ```compile_fail
92/// # #[macro_use] extern crate gpui_macros;
93/// # #[macro_use] extern crate gpui;
94/// #[derive(VisualContext)]
95/// struct MyContext<'a, 'b> {
96///     #[app]
97///     app: &'a mut gpui::App,
98///     window: &'b mut gpui::Window
99/// }
100/// ```
101///
102/// ```compile_fail
103/// # #[macro_use] extern crate gpui_macros;
104/// # #[macro_use] extern crate gpui;
105/// #[derive(VisualContext)]
106/// struct MyContext<'a, 'b> {
107///     app: &'a mut gpui::App,
108///     #[window]
109///     window: &'b mut gpui::Window
110/// }
111/// ```
112#[proc_macro_derive(VisualContext, attributes(window, app))]
113pub fn derive_visual_context(input: TokenStream) -> TokenStream {
114    gpui_pre_facade_paths::rewrite(__gpui_pre_derive_visual_context(input))
115}
116
117fn __gpui_pre_derive_visual_context(input: TokenStream) -> TokenStream {
118    derive_visual_context::derive_visual_context(input)
119}
120
121/// Used by GPUI to generate the style helpers.
122#[proc_macro]
123#[doc(hidden)]
124pub fn style_helpers(input: TokenStream) -> TokenStream {
125    gpui_pre_facade_paths::rewrite(__gpui_pre_style_helpers(input))
126}
127
128fn __gpui_pre_style_helpers(input: TokenStream) -> TokenStream {
129    styles::style_helpers(input)
130}
131
132/// Generates methods for visibility styles.
133#[proc_macro]
134pub fn visibility_style_methods(input: TokenStream) -> TokenStream {
135    gpui_pre_facade_paths::rewrite(__gpui_pre_visibility_style_methods(input))
136}
137
138fn __gpui_pre_visibility_style_methods(input: TokenStream) -> TokenStream {
139    styles::visibility_style_methods(input)
140}
141
142/// Generates methods for margin styles.
143#[proc_macro]
144pub fn margin_style_methods(input: TokenStream) -> TokenStream {
145    gpui_pre_facade_paths::rewrite(__gpui_pre_margin_style_methods(input))
146}
147
148fn __gpui_pre_margin_style_methods(input: TokenStream) -> TokenStream {
149    styles::margin_style_methods(input)
150}
151
152/// Generates methods for padding styles.
153#[proc_macro]
154pub fn padding_style_methods(input: TokenStream) -> TokenStream {
155    gpui_pre_facade_paths::rewrite(__gpui_pre_padding_style_methods(input))
156}
157
158fn __gpui_pre_padding_style_methods(input: TokenStream) -> TokenStream {
159    styles::padding_style_methods(input)
160}
161
162/// Generates methods for position styles.
163#[proc_macro]
164pub fn position_style_methods(input: TokenStream) -> TokenStream {
165    gpui_pre_facade_paths::rewrite(__gpui_pre_position_style_methods(input))
166}
167
168fn __gpui_pre_position_style_methods(input: TokenStream) -> TokenStream {
169    styles::position_style_methods(input)
170}
171
172/// Generates methods for overflow styles.
173#[proc_macro]
174pub fn overflow_style_methods(input: TokenStream) -> TokenStream {
175    gpui_pre_facade_paths::rewrite(__gpui_pre_overflow_style_methods(input))
176}
177
178fn __gpui_pre_overflow_style_methods(input: TokenStream) -> TokenStream {
179    styles::overflow_style_methods(input)
180}
181
182/// Generates methods for cursor styles.
183#[proc_macro]
184pub fn cursor_style_methods(input: TokenStream) -> TokenStream {
185    gpui_pre_facade_paths::rewrite(__gpui_pre_cursor_style_methods(input))
186}
187
188fn __gpui_pre_cursor_style_methods(input: TokenStream) -> TokenStream {
189    styles::cursor_style_methods(input)
190}
191
192/// Generates methods for border styles.
193#[proc_macro]
194pub fn border_style_methods(input: TokenStream) -> TokenStream {
195    gpui_pre_facade_paths::rewrite(__gpui_pre_border_style_methods(input))
196}
197
198fn __gpui_pre_border_style_methods(input: TokenStream) -> TokenStream {
199    styles::border_style_methods(input)
200}
201
202/// Generates methods for box shadow styles.
203#[proc_macro]
204pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
205    gpui_pre_facade_paths::rewrite(__gpui_pre_box_shadow_style_methods(input))
206}
207
208fn __gpui_pre_box_shadow_style_methods(input: TokenStream) -> TokenStream {
209    styles::box_shadow_style_methods(input)
210}
211
212/// `#[gpui::test]` can be used to annotate test functions that run with GPUI support.
213///
214/// It supports both synchronous and asynchronous tests, and can provide you with
215/// as many `TestAppContext` instances as you need.
216/// The output contains a `#[test]` annotation so this can be used with any existing
217/// test harness (`cargo test` or `cargo-nextest`).
218///
219/// ```
220/// #[gpui::test]
221/// async fn test_foo(mut cx: &TestAppContext) { }
222/// ```
223///
224/// In addition to passing a TestAppContext, you can also ask for a `StdRnd` instance.
225/// this will be seeded with the `SEED` environment variable and is used internally by
226/// the ForegroundExecutor and BackgroundExecutor to run tasks deterministically in tests.
227/// Using the same `StdRng` for behavior in your test will allow you to exercise a wide
228/// variety of scenarios and interleavings just by changing the seed.
229///
230/// # Arguments
231///
232/// - `#[gpui::test]` with no arguments runs once with the seed `0` or `SEED` env var if set.
233/// - `#[gpui::test(seed = 10)]` runs once with the seed `10`.
234/// - `#[gpui::test(seeds(10, 20, 30))]` runs three times with seeds `10`, `20`, and `30`.
235/// - `#[gpui::test(iterations = 5)]` runs five times, providing as seed the values in the range `0..5`.
236/// - `#[gpui::test(retries = 3)]` runs up to four times if it fails to try and make it pass.
237/// - `#[gpui::test(on_failure = "crate::test::report_failure")]` will call the specified function after the
238///   tests fail so that you can write out more detail about the failure.
239///
240/// You can combine `iterations = ...` with `seeds(...)`:
241/// - `#[gpui::test(iterations = 5, seed = 10)]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10))]`.
242/// - `#[gpui::test(iterations = 5, seeds(10, 20, 30)]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10, 20, 30))]`.
243/// - `#[gpui::test(seeds(10, 20, 30), iterations = 5]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10, 20, 30))]`.
244///
245/// # Environment Variables
246///
247/// - `SEED`: sets a seed for the first run
248/// - `ITERATIONS`: forces the value of the `iterations` argument
249#[proc_macro_attribute]
250pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
251    gpui_pre_facade_paths::rewrite(__gpui_pre_test(args, function))
252}
253
254fn __gpui_pre_test(args: TokenStream, function: TokenStream) -> TokenStream {
255    test::test(args, function)
256}
257
258/// `#[gpui::bench]` annotates a Criterion benchmark that runs with GPUI support.
259///
260/// Use `#[gpui::bench(inputs = some_iterable())]` on benchmarks that take an
261/// additional input argument; the generated benchmark uses Criterion's
262/// `bench_with_input`. `group`, `input_name`, and `sample_size` can customize
263/// the generated input benchmark group.
264///
265/// The benchmark crate must add `criterion` and `gpui_platform` (with its
266/// `test-support` feature) to its dev-dependencies and enable gpui's `bench`
267/// feature, since the generated code references all three.
268#[proc_macro_attribute]
269pub fn bench(args: TokenStream, function: TokenStream) -> TokenStream {
270    gpui_pre_facade_paths::rewrite(__gpui_pre_bench(args, function))
271}
272
273fn __gpui_pre_bench(args: TokenStream, function: TokenStream) -> TokenStream {
274    bench::bench(args, function)
275}
276
277/// A variant of `#[gpui::test]` that supports property-based testing.
278///
279/// A property test, much like a standard GPUI randomized test, allows testing
280/// claims of the form "for any possible X, Y should hold". For example:
281/// ```
282/// #[gpui::property_test]
283/// fn test_arithmetic(x: i32, y: i32) {
284///     assert!(x == y || x < y || x > y);
285/// }
286/// ```
287/// Standard GPUI randomized tests provide you with an instance of `StdRng` to
288/// generate random data in a controlled manner. Property-based tests have some
289/// advantages, however:
290/// - Shrinking - the harness also understands a notion of the "complexity" of a
291///   particular value. This allows it to find the "simplest possible value that
292///   causes the test to fail".
293/// - Ergonomics/clarity - the property-testing harness will automatically
294///   generate values, removing the need to fill the test body with generation
295///   logic.
296/// - Failure persistence - if a failing seed is identified, it is stored in a
297///   file, which can be checked in, and future runs will check these cases before
298///   future cases.
299///
300/// Property tests work best when all inputs can be generated up-front and kept
301/// in a simple data structure. Sometimes, this isn't possible - for example, if
302/// a test needs to make a random decision based on the current state of some
303/// structure. In this case, a standard GPUI randomized test may be more
304/// suitable.
305///
306/// ## Customizing random values
307///
308/// This macro is based on the [`#[proptest::property_test]`] macro, but handles
309/// some of the same GPUI-specific arguments as `#[gpui::test]`. Specifically,
310/// `&{mut,} TestAppContext` and `BackgroundExecutor` work as normal. `StdRng`
311/// arguments are **explicitly forbidden**, since they break shrinking, and are
312/// a common footgun.
313///
314/// All other arguments are forwarded to the underlying proptest macro.
315///
316/// Note: much of the following is copied from the proptest docs, specifically the
317/// [`#[proptest::property_test]`] macro docs.
318///
319/// Random values of type `T` are generated by a `Strategy<Value = T>` object.
320/// Some types have a canonical `Strategy` - these types also implement
321/// `Arbitrary`. Parameters to a `#[gpui::property_test]`, by default, use a
322/// type's `Arbitrary` implementation. If you'd like to provide a custom
323/// strategy, you can use `#[strategy = ...]` on the argument:
324/// ```
325/// #[gpui::property_test]
326/// fn int_test(#[strategy = 1..10] x: i32, #[strategy = "[a-zA-Z0-9]{20}"] s: String) {
327///   assert!(s.len() > (x as usize));
328/// }
329/// ```
330///
331/// For more information on writing custom `Strategy` and `Arbitrary`
332/// implementations, see [the proptest book][book], and the [`Strategy`] trait.
333///
334/// ## Scheduler
335///
336/// Similar to `#[gpui::test]`, this macro will choose random seeds for the test
337/// scheduler. It uses `.no_shrink()` to tell proptest that all seeds are
338/// roughly equivalent in terms of "complexity". If `$SEED` is set, it will
339/// affect **ONLY** the seed passed to the scheduler. To control other values,
340/// use custom `Strategy`s.
341///
342/// [`#[proptest::property_test]`]: https://docs.rs/proptest/latest/proptest/attr.property_test.html
343/// [book]: https://proptest-rs.github.io/proptest/intro.html
344/// [`Strategy`]: https://docs.rs/proptest/latest/proptest/strategy/trait.Strategy.html
345#[proc_macro_attribute]
346pub fn property_test(args: TokenStream, function: TokenStream) -> TokenStream {
347    gpui_pre_facade_paths::rewrite(__gpui_pre_property_test(args, function))
348}
349
350fn __gpui_pre_property_test(args: TokenStream, function: TokenStream) -> TokenStream {
351    property_test::test(args.into(), function.into()).into()
352}
353
354/// When added to a trait, `#[derive_inspector_reflection]` generates a module which provides
355/// enumeration and lookup by name of all methods that have the shape `fn method(self) -> Self`.
356/// This is used by the inspector so that it can use the builder methods in `Styled` and
357/// `StyledExt`.
358///
359/// The generated module will have the name `<snake_case_trait_name>_reflection` and contain the
360/// following functions:
361///
362/// ```ignore
363/// pub fn methods::<T: TheTrait + 'static>() -> Vec<gpui::inspector_reflection::FunctionReflection<T>>;
364///
365/// pub fn find_method::<T: TheTrait + 'static>() -> Option<gpui::inspector_reflection::FunctionReflection<T>>;
366/// ```
367///
368/// The `invoke` method on `FunctionReflection` will run the method. `FunctionReflection` also
369/// provides the method's documentation.
370#[cfg(any(feature = "inspector", debug_assertions))]
371#[proc_macro_attribute]
372pub fn derive_inspector_reflection(_args: TokenStream, input: TokenStream) -> TokenStream {
373    gpui_pre_facade_paths::rewrite(__gpui_pre_derive_inspector_reflection(_args, input))
374}
375
376fn __gpui_pre_derive_inspector_reflection(_args: TokenStream, input: TokenStream) -> TokenStream {
377    derive_inspector_reflection::derive_inspector_reflection(_args, input)
378}
379
380pub(crate) fn get_simple_attribute_field(ast: &DeriveInput, name: &'static str) -> Option<Ident> {
381    match &ast.data {
382        syn::Data::Struct(data_struct) => data_struct
383            .fields
384            .iter()
385            .find(|field| field.attrs.iter().any(|attr| attr.path().is_ident(name)))
386            .map(|field| field.ident.clone().unwrap()),
387        syn::Data::Enum(_) => None,
388        syn::Data::Union(_) => None,
389    }
390}