1mod derive_action;
2mod derive_app_context;
3mod derive_into_element;
4mod derive_render;
5mod derive_visual_context;
6mod register_action;
7mod styles;
8mod test;
9
10#[cfg(any(feature = "inspector", debug_assertions))]
11mod derive_inspector_reflection;
12
13use proc_macro::TokenStream;
14use syn::{DeriveInput, Ident};
15
16/// `Action` derive macro - see the trait documentation for details.
17#[proc_macro_derive(Action, attributes(action))]
18pub fn derive_action(input: TokenStream) -> TokenStream {
19 derive_action::derive_action(input)
20}
21
22/// This can be used to register an action with the GPUI runtime when you want to manually implement
23/// the `Action` trait. Typically you should use the `Action` derive macro or `actions!` macro
24/// instead.
25#[proc_macro]
26pub fn register_action(ident: TokenStream) -> TokenStream {
27 register_action::register_action(ident)
28}
29
30/// #[derive(IntoElement)] is used to create a Component out of anything that implements
31/// the `RenderOnce` trait.
32#[proc_macro_derive(IntoElement)]
33pub fn derive_into_element(input: TokenStream) -> TokenStream {
34 derive_into_element::derive_into_element(input)
35}
36
37#[proc_macro_derive(Render)]
38#[doc(hidden)]
39pub fn derive_render(input: TokenStream) -> TokenStream {
40 derive_render::derive_render(input)
41}
42
43/// #[derive(AppContext)] is used to create a context out of anything that holds a `&mut App`
44/// Note that a `#[app]` attribute is required to identify the variable holding the &mut App.
45///
46/// Failure to add the attribute causes a compile error:
47///
48/// ```compile_fail
49/// # #[macro_use] extern crate gpui_macros;
50/// # #[macro_use] extern crate gpui;
51/// #[derive(AppContext)]
52/// struct MyContext<'a> {
53/// app: &'a mut gpui::App
54/// }
55/// ```
56#[proc_macro_derive(AppContext, attributes(app))]
57pub fn derive_app_context(input: TokenStream) -> TokenStream {
58 derive_app_context::derive_app_context(input)
59}
60
61/// #[derive(VisualContext)] is used to create a visual context out of anything that holds a `&mut Window` and
62/// implements `AppContext`
63/// Note that a `#[app]` and a `#[window]` attribute are required to identify the variables holding the &mut App,
64/// and &mut Window respectively.
65///
66/// Failure to add both attributes causes a compile error:
67///
68/// ```compile_fail
69/// # #[macro_use] extern crate gpui_macros;
70/// # #[macro_use] extern crate gpui;
71/// #[derive(VisualContext)]
72/// struct MyContext<'a, 'b> {
73/// #[app]
74/// app: &'a mut gpui::App,
75/// window: &'b mut gpui::Window
76/// }
77/// ```
78///
79/// ```compile_fail
80/// # #[macro_use] extern crate gpui_macros;
81/// # #[macro_use] extern crate gpui;
82/// #[derive(VisualContext)]
83/// struct MyContext<'a, 'b> {
84/// app: &'a mut gpui::App,
85/// #[window]
86/// window: &'b mut gpui::Window
87/// }
88/// ```
89#[proc_macro_derive(VisualContext, attributes(window, app))]
90pub fn derive_visual_context(input: TokenStream) -> TokenStream {
91 derive_visual_context::derive_visual_context(input)
92}
93
94/// Used by GPUI to generate the style helpers.
95#[proc_macro]
96#[doc(hidden)]
97pub fn style_helpers(input: TokenStream) -> TokenStream {
98 styles::style_helpers(input)
99}
100
101/// Generates methods for visibility styles.
102#[proc_macro]
103pub fn visibility_style_methods(input: TokenStream) -> TokenStream {
104 styles::visibility_style_methods(input)
105}
106
107/// Generates methods for margin styles.
108#[proc_macro]
109pub fn margin_style_methods(input: TokenStream) -> TokenStream {
110 styles::margin_style_methods(input)
111}
112
113/// Generates methods for padding styles.
114#[proc_macro]
115pub fn padding_style_methods(input: TokenStream) -> TokenStream {
116 styles::padding_style_methods(input)
117}
118
119/// Generates methods for position styles.
120#[proc_macro]
121pub fn position_style_methods(input: TokenStream) -> TokenStream {
122 styles::position_style_methods(input)
123}
124
125/// Generates methods for overflow styles.
126#[proc_macro]
127pub fn overflow_style_methods(input: TokenStream) -> TokenStream {
128 styles::overflow_style_methods(input)
129}
130
131/// Generates methods for cursor styles.
132#[proc_macro]
133pub fn cursor_style_methods(input: TokenStream) -> TokenStream {
134 styles::cursor_style_methods(input)
135}
136
137/// Generates methods for border styles.
138#[proc_macro]
139pub fn border_style_methods(input: TokenStream) -> TokenStream {
140 styles::border_style_methods(input)
141}
142
143/// Generates methods for box shadow styles.
144#[proc_macro]
145pub fn box_shadow_style_methods(input: TokenStream) -> TokenStream {
146 styles::box_shadow_style_methods(input)
147}
148
149/// `#[gpui::test]` can be used to annotate test functions that run with GPUI support.
150///
151/// It supports both synchronous and asynchronous tests, and can provide you with
152/// as many `TestAppContext` instances as you need.
153/// The output contains a `#[test]` annotation so this can be used with any existing
154/// test harness (`cargo test` or `cargo-nextest`).
155///
156/// ```
157/// #[gpui::test]
158/// async fn test_foo(mut cx: &TestAppContext) { }
159/// ```
160///
161/// In addition to passing a TestAppContext, you can also ask for a `StdRnd` instance.
162/// this will be seeded with the `SEED` environment variable and is used internally by
163/// the ForegroundExecutor and BackgroundExecutor to run tasks deterministically in tests.
164/// Using the same `StdRng` for behavior in your test will allow you to exercise a wide
165/// variety of scenarios and interleavings just by changing the seed.
166///
167/// # Arguments
168///
169/// - `#[gpui::test]` with no arguments runs once with the seed `0` or `SEED` env var if set.
170/// - `#[gpui::test(seed = 10)]` runs once with the seed `10`.
171/// - `#[gpui::test(seeds(10, 20, 30))]` runs three times with seeds `10`, `20`, and `30`.
172/// - `#[gpui::test(iterations = 5)]` runs five times, providing as seed the values in the range `0..5`.
173/// - `#[gpui::test(retries = 3)]` runs up to four times if it fails to try and make it pass.
174/// - `#[gpui::test(on_failure = "crate::test::report_failure")]` will call the specified function after the
175/// tests fail so that you can write out more detail about the failure.
176///
177/// You can combine `iterations = ...` with `seeds(...)`:
178/// - `#[gpui::test(iterations = 5, seed = 10)]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10))]`.
179/// - `#[gpui::test(iterations = 5, seeds(10, 20, 30)]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10, 20, 30))]`.
180/// - `#[gpui::test(seeds(10, 20, 30), iterations = 5]` is equivalent to `#[gpui::test(seeds(0, 1, 2, 3, 4, 10, 20, 30))]`.
181///
182/// # Environment Variables
183///
184/// - `SEED`: sets a seed for the first run
185/// - `ITERATIONS`: forces the value of the `iterations` argument
186#[proc_macro_attribute]
187pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
188 test::test(args, function)
189}
190
191/// When added to a trait, `#[derive_inspector_reflection]` generates a module which provides
192/// enumeration and lookup by name of all methods that have the shape `fn method(self) -> Self`.
193/// This is used by the inspector so that it can use the builder methods in `Styled` and
194/// `StyledExt`.
195///
196/// The generated module will have the name `<snake_case_trait_name>_reflection` and contain the
197/// following functions:
198///
199/// ```ignore
200/// pub fn methods::<T: TheTrait + 'static>() -> Vec<gpui::inspector_reflection::FunctionReflection<T>>;
201///
202/// pub fn find_method::<T: TheTrait + 'static>() -> Option<gpui::inspector_reflection::FunctionReflection<T>>;
203/// ```
204///
205/// The `invoke` method on `FunctionReflection` will run the method. `FunctionReflection` also
206/// provides the method's documentation.
207#[cfg(any(feature = "inspector", debug_assertions))]
208#[proc_macro_attribute]
209pub fn derive_inspector_reflection(_args: TokenStream, input: TokenStream) -> TokenStream {
210 derive_inspector_reflection::derive_inspector_reflection(_args, input)
211}
212
213pub(crate) fn get_simple_attribute_field(ast: &DeriveInput, name: &'static str) -> Option<Ident> {
214 match &ast.data {
215 syn::Data::Struct(data_struct) => data_struct
216 .fields
217 .iter()
218 .find(|field| field.attrs.iter().any(|attr| attr.path().is_ident(name)))
219 .map(|field| field.ident.clone().unwrap()),
220 syn::Data::Enum(_) => None,
221 syn::Data::Union(_) => None,
222 }
223}