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