1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{DeriveInput, parse_macro_input};
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 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 fields.push(field);
47
48 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 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 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}