Add derive macro for IntoAnyElement

Nathan Sobo created

Change summary

crates/gpui2_macros/src/derive_into_any_element.rs | 54 ++++++++++++++++
crates/gpui2_macros/src/gpui2_macros.rs            |  6 +
crates/ui2/src/components/panes.rs                 | 13 ++-
crates/ui2/src/components/status_bar.rs            | 15 +++
4 files changed, 79 insertions(+), 9 deletions(-)

Detailed changes

crates/gpui2_macros/src/derive_into_any_element.rs 🔗

@@ -0,0 +1,54 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse_macro_input, DeriveInput};
+
+pub fn derive_into_any_element(input: TokenStream) -> TokenStream {
+    let ast = parse_macro_input!(input as DeriveInput);
+    let name = &ast.ident;
+    let generics = &ast.generics;
+    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+    let specified_view_type = ast
+        .attrs
+        .iter()
+        .find(|attr| attr.path.is_ident("element"))
+        .and_then(|attr| {
+            if let Ok(syn::Meta::List(meta_list)) = attr.parse_meta() {
+                meta_list.nested.iter().find_map(|nested| {
+                    if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = nested {
+                        if nv.path.is_ident("view_type") {
+                            if let syn::Lit::Str(lit_str) = &nv.lit {
+                                return Some(
+                                    lit_str
+                                        .parse::<syn::Ident>()
+                                        .expect("Failed to parse view_type"),
+                                );
+                            }
+                        }
+                    }
+                    None
+                })
+            } else {
+                None
+            }
+        });
+
+    let view_type = specified_view_type.unwrap_or_else(|| {
+        if let Some(syn::GenericParam::Type(type_param)) = generics.params.first() {
+            type_param.ident.clone()
+        } else {
+            panic!("Expected first type parameter");
+        }
+    });
+
+    let expanded = quote! {
+        impl #impl_generics gpui2::IntoAnyElement<#view_type> for #name #ty_generics #where_clause {
+            fn into_any(self) -> gpui2::AnyElement<#view_type> {
+                (move |view_state: &mut #view_type, cx: &mut gpui2::ViewContext<'_, '_, #view_type>| self.render(view_state, cx))
+                    .into_any()
+            }
+        }
+    };
+
+    TokenStream::from(expanded)
+}

crates/gpui2_macros/src/gpui2_macros.rs 🔗

@@ -1,6 +1,7 @@
 use proc_macro::TokenStream;
 
 mod derive_element;
+mod derive_into_any_element;
 mod style_helpers;
 mod test;
 
@@ -14,6 +15,11 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
     derive_element::derive_element(input)
 }
 
+#[proc_macro_derive(IntoAnyElement, attributes(element))]
+pub fn derive_into_any_element(input: TokenStream) -> TokenStream {
+    derive_into_any_element::derive_into_any_element(input)
+}
+
 #[proc_macro_attribute]
 pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
     test::test(args, function)

crates/ui2/src/components/panes.rs 🔗

@@ -12,6 +12,7 @@ pub enum SplitDirection {
     Vertical,
 }
 
+#[derive(IntoAnyElement)]
 pub struct Pane<V: 'static> {
     id: ElementId,
     size: Size<Length>,
@@ -19,12 +20,12 @@ pub struct Pane<V: 'static> {
     children: SmallVec<[AnyElement<V>; 2]>,
 }
 
-impl<V: 'static> IntoAnyElement<V> for Pane<V> {
-    fn into_any(self) -> AnyElement<V> {
-        (move |view_state: &mut V, cx: &mut ViewContext<'_, '_, V>| self.render(view_state, cx))
-            .into_any()
-    }
-}
+// impl<V: 'static> IntoAnyElement<V> for Pane<V> {
+//     fn into_any(self) -> AnyElement<V> {
+//         (move |view_state: &mut V, cx: &mut ViewContext<'_, '_, V>| self.render(view_state, cx))
+//             .into_any()
+//     }
+// }
 
 impl<V: 'static> Pane<V> {
     pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {

crates/ui2/src/components/status_bar.rs 🔗

@@ -28,14 +28,23 @@ impl Default for ToolGroup {
     }
 }
 
-#[derive(Element)]
-#[element(view_state = "Workspace")]
+#[derive(IntoAnyElement)]
+#[element(view_type = "Workspace")]
 pub struct StatusBar {
     left_tools: Option<ToolGroup>,
     right_tools: Option<ToolGroup>,
     bottom_tools: Option<ToolGroup>,
 }
 
+// impl IntoAnyElement<Workspace> for StatusBar {
+//     fn into_any(self) -> gpui2::AnyElement<Workspace> {
+//         (move |workspace: &mut Workspace, cx: &mut ViewContext<'_, '_, Workspace>| {
+//             self.render(workspace, cx)
+//         })
+//         .into_any()
+//     }
+// }
+
 impl StatusBar {
     pub fn new() -> Self {
         Self {
@@ -83,7 +92,7 @@ impl StatusBar {
     }
 
     fn render(
-        &mut self,
+        self,
         view: &mut Workspace,
         cx: &mut ViewContext<Workspace>,
     ) -> impl IntoAnyElement<Workspace> {