Re-implement actions as derive macros instead of blanket impls
This commit is contained in:
parent
a0e976599c
commit
4de2c0f7ef
22 changed files with 360 additions and 288 deletions
|
@ -15,48 +15,81 @@
|
|||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use syn::{parse_macro_input, DeriveInput, Error};
|
||||
|
||||
use crate::register_action::register_action;
|
||||
|
||||
pub fn action(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
pub fn action(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as DeriveInput);
|
||||
let name = &input.ident;
|
||||
let attrs = input
|
||||
.attrs
|
||||
.into_iter()
|
||||
.filter(|attr| !attr.path.is_ident("action"))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let attributes = quote! {
|
||||
#[gpui::register_action]
|
||||
#[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
|
||||
#(#attrs)*
|
||||
if input.generics.lt_token.is_some() {
|
||||
return Error::new(name.span(), "Actions must be a concrete type")
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let is_unit_struct = match input.data {
|
||||
syn::Data::Struct(struct_data) => struct_data.fields.is_empty(),
|
||||
syn::Data::Enum(_) => false,
|
||||
syn::Data::Union(_) => false,
|
||||
};
|
||||
let visibility = input.vis;
|
||||
|
||||
let output = match input.data {
|
||||
syn::Data::Struct(ref struct_data) => match &struct_data.fields {
|
||||
syn::Fields::Named(_) | syn::Fields::Unnamed(_) => {
|
||||
let fields = &struct_data.fields;
|
||||
quote! {
|
||||
#attributes
|
||||
#visibility struct #name #fields
|
||||
}
|
||||
let build_impl = if is_unit_struct {
|
||||
quote! {
|
||||
Ok(std::boxed::Box::new(Self {}))
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Ok(std::boxed::Box::new(gpui::serde_json::from_value::<Self>(value)?))
|
||||
}
|
||||
};
|
||||
|
||||
let register_action = register_action(&name);
|
||||
|
||||
let output = quote! {
|
||||
const _: fn() = || {
|
||||
fn assert_impl<T: ?Sized + for<'a> gpui::serde::Deserialize<'a> + ::std::cmp::PartialEq + ::std::clone::Clone>() {}
|
||||
assert_impl::<#name>();
|
||||
};
|
||||
|
||||
impl gpui::Action for #name {
|
||||
fn name(&self) -> &'static str
|
||||
{
|
||||
::std::any::type_name::<#name>()
|
||||
}
|
||||
syn::Fields::Unit => {
|
||||
quote! {
|
||||
#attributes
|
||||
#visibility struct #name;
|
||||
}
|
||||
|
||||
fn debug_name() -> &'static str
|
||||
where
|
||||
Self: ::std::marker::Sized
|
||||
{
|
||||
::std::any::type_name::<#name>()
|
||||
}
|
||||
},
|
||||
syn::Data::Enum(ref enum_data) => {
|
||||
let variants = &enum_data.variants;
|
||||
quote! {
|
||||
#attributes
|
||||
#visibility enum #name { #variants }
|
||||
|
||||
fn build(value: gpui::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>>
|
||||
where
|
||||
Self: ::std::marker::Sized {
|
||||
#build_impl
|
||||
}
|
||||
|
||||
fn partial_eq(&self, action: &dyn gpui::Action) -> bool {
|
||||
action
|
||||
.as_any()
|
||||
.downcast_ref::<Self>()
|
||||
.map_or(false, |a| self == a)
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> std::boxed::Box<dyn gpui::Action> {
|
||||
::std::boxed::Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn ::std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
_ => panic!("Expected a struct or an enum."),
|
||||
|
||||
#register_action
|
||||
};
|
||||
|
||||
TokenStream::from(output)
|
||||
|
|
|
@ -11,14 +11,14 @@ pub fn style_helpers(args: TokenStream) -> TokenStream {
|
|||
style_helpers::style_helpers(args)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn action(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
action::action(attr, item)
|
||||
#[proc_macro_derive(Action)]
|
||||
pub fn action(input: TokenStream) -> TokenStream {
|
||||
action::action(input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn register_action(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
register_action::register_action(attr, item)
|
||||
register_action::register_action_macro(attr, item)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Component, attributes(component))]
|
||||
|
|
|
@ -12,13 +12,54 @@
|
|||
// gpui2::register_action_builder::<Foo>()
|
||||
// }
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Ident;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
use syn::{parse_macro_input, DeriveInput, Error};
|
||||
|
||||
pub fn register_action(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
pub fn register_action_macro(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as DeriveInput);
|
||||
let type_name = &input.ident;
|
||||
let registration = register_action(&input.ident);
|
||||
|
||||
let has_action_derive = input
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|attr| {
|
||||
(|| {
|
||||
let meta = attr.parse_meta().ok()?;
|
||||
meta.path().is_ident("derive").then(|| match meta {
|
||||
syn::Meta::Path(_) => None,
|
||||
syn::Meta::NameValue(_) => None,
|
||||
syn::Meta::List(list) => list
|
||||
.nested
|
||||
.iter()
|
||||
.find(|list| match list {
|
||||
syn::NestedMeta::Meta(meta) => meta.path().is_ident("Action"),
|
||||
syn::NestedMeta::Lit(_) => false,
|
||||
})
|
||||
.map(|_| true),
|
||||
})?
|
||||
})()
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.is_some();
|
||||
|
||||
if has_action_derive {
|
||||
return Error::new(
|
||||
input.ident.span(),
|
||||
"The Action derive macro has already registered this action",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#input
|
||||
|
||||
#registration
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn register_action(type_name: &Ident) -> proc_macro2::TokenStream {
|
||||
let static_slice_name =
|
||||
format_ident!("__GPUI_ACTIONS_{}", type_name.to_string().to_uppercase());
|
||||
|
||||
|
@ -27,9 +68,7 @@ pub fn register_action(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
type_name.to_string().to_lowercase()
|
||||
);
|
||||
|
||||
let expanded = quote! {
|
||||
#input
|
||||
|
||||
quote! {
|
||||
#[doc(hidden)]
|
||||
#[gpui::linkme::distributed_slice(gpui::__GPUI_ACTIONS)]
|
||||
#[linkme(crate = gpui::linkme)]
|
||||
|
@ -44,7 +83,5 @@ pub fn register_action(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
build: <#type_name as gpui::Action>::build,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue