Skip to main content

gpui_hooks_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput};
4
5#[proc_macro_attribute]
6pub fn hook_element(_attr: TokenStream, item: TokenStream) -> TokenStream {
7    let input = parse_macro_input!(item as DeriveInput);
8
9    let attrs = &input.attrs;
10    let vis = &input.vis;
11    let ident = &input.ident;
12    let generics = &input.generics;
13    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
14
15    // Extract original fields
16    let mut fields = Vec::new();
17    let mut original_field_names = Vec::new();
18    let mut original_field_types = Vec::new();
19
20    let data_struct = match &input.data {
21        syn::Data::Struct(data_struct) => data_struct,
22        _ => {
23            return syn::Error::new(
24                proc_macro2::Span::call_site(),
25                "#[hook_element] can only be used on structs",
26            )
27            .to_compile_error()
28            .into();
29        }
30    };
31
32    let fields_named = match &data_struct.fields {
33        syn::Fields::Named(fields_named) => fields_named,
34        _ => {
35            return syn::Error::new(
36                proc_macro2::Span::call_site(),
37                "#[hook_element] can only be used on structs with named fields",
38            )
39            .to_compile_error()
40            .into();
41        }
42    };
43
44    for field in &fields_named.named {
45        // Save fields for regeneration
46        fields.push(field);
47
48        // Extract field names and types for Default implementation
49        if let Some(field_ident) = &field.ident {
50            original_field_names.push(field_ident);
51            original_field_types.push(&field.ty);
52        }
53    }
54
55    // Generate new struct definition, add hooks field and implement Render
56    let expanded = quote! {
57        #(#attrs)*
58        #vis struct #ident #generics {
59            #(#fields,)*
60            _hooks: ::std::cell::RefCell<::std::vec::Vec<::std::boxed::Box<dyn gpui_hooks::hooks::Hook>>>,
61            _hook_index: ::std::cell::Cell<usize>,
62            _prev: ::std::cell::Cell<usize>,
63        }
64
65        impl #impl_generics ::std::default::Default for #ident #ty_generics #where_clause {
66            fn default() -> Self {
67                Self {
68                    #(
69                        #original_field_names: ::std::default::Default::default(),
70                    )*
71                    _hooks: ::std::cell::RefCell::new(::std::vec::Vec::new()),
72                    _hook_index: ::std::cell::Cell::new(0),
73                    _prev: ::std::cell::Cell::new(0),
74                }
75            }
76        }
77
78        impl #impl_generics gpui_hooks::HookedElement for #ident #ty_generics #where_clause {
79            fn _hooks_ref(&self) -> &::std::cell::RefCell<::std::vec::Vec<::std::boxed::Box<dyn gpui_hooks::hooks::Hook>>> {
80                &self._hooks
81            }
82
83            fn _hook_index(&self) -> usize {
84                self._hook_index.get()
85            }
86
87            fn _set_hook_index(&self, index: usize) {
88                self._hook_index.set(index);
89            }
90
91            fn _prev(&self) -> usize {
92                self._prev.get()
93            }
94
95            fn _set_prev(&self, prev: usize) {
96                self._prev.set(prev);
97            }
98        }
99
100        // 自动实现 GPUI 的 Render trait
101        impl #impl_generics gpui::Render for #ident #ty_generics #where_clause {
102            fn render(&mut self, window: &mut gpui::Window, cx: &mut gpui::Context<Self>) -> impl gpui::IntoElement {
103                gpui_hooks::execute_hooked_render(self, window, cx)
104            }
105        }
106    };
107
108    TokenStream::from(expanded)
109}