gpui_macros.rs

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