Skip to content

Commit

Permalink
Implemented constructor argument capture shorthands
Browse files Browse the repository at this point in the history
Closes #2
  • Loading branch information
Tamschi committed Oct 9, 2020
1 parent ef6d8eb commit 5ca8b5f
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

* The bump format shorthand now does not imply `'a: 'bump` anymore, where `'a` is the component's lifetime.

* You can now prefix constructor arguments with an explicit visibility (`priv`, `pub`, `pub(restriction)`) to cature them as component instance fields.

## 0.0.2

2020-10-08
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,17 @@ use std::cell::RefCell;
fn schedule_render() { /* ... */ }

component! {
pub Counter(initial: i32, step: i32)()
pub Counter(
initial: i32,
priv step: i32,
/// This component's class attribute value.
pub class: &'static str, //
)()

|value = RefCell::<i32>::new(initial)|; // shorthand capture
|step: i32 = {step}|; // long form capture, ²

<div
."class" = {self.class} //
"The current value is: " !{*self.value.borrow()} <br>

<button
Expand All @@ -149,7 +154,8 @@ impl Counter {
}
```

² <https://github.com/Tamschi/Asteracea/issues/2>
<https://github.com/Tamschi/Asteracea/issues/5>
<https://github.com/Tamschi/Asteracea/issues/6>

## License

Expand Down
1 change: 1 addition & 0 deletions proc-macro-definitions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ styles = ["rand"]

[dependencies]
call2-for-syn = "1.0.0"
debugless-unwrap = "0.0.3"
heck = "0.3.1"
lazy_static = "1.4.0"
quote = "1.0.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use quote::quote;
use syn::{
parse::Parse, parse::ParseStream, Attribute, Error, FnArg, PatType, Result, Token, Visibility,
};

pub struct ConstructorArgument {
pub capture: Capture,
pub fn_arg: PatType,
}

pub enum Capture {
No,
Yes(syn::Visibility),
}

impl Parse for ConstructorArgument {
fn parse(input: ParseStream) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let capture = if input.peek(Token![priv]) {
input.parse::<Token![priv]>().unwrap();
Capture::Yes(Visibility::Inherited)
} else {
match input.parse()? {
Visibility::Inherited => Capture::No,
visibility => Capture::Yes(visibility),
}
};
Ok(Self {
fn_arg: match input.parse::<FnArg>()? {
FnArg::Receiver(r) => {
return Err(Error::new_spanned(
r,
"Component constructors cannot expect `self` parameters.",
));
}
FnArg::Typed(pat_type)
if matches!(capture, Capture::No) || pat_type.attrs.is_empty() =>
{
PatType { attrs, ..pat_type }
}
FnArg::Typed(PatType { attrs, .. }) => {
return Err(Error::new_spanned(quote!(#(#attrs)*), "Attributes are currently not available in this position. Place them before the visibility modifier instead."));
}
},
capture,
})
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use self::constructor_argument::ConstructorArgument;
use crate::{
asteracea_ident,
parse_with_context::{ParseContext, ParseWithContext},
part::{CaptureDefinition, GenerateContext},
warn, Configuration, MapMessage, Part, YankAny,
};
use call2_for_syn::call2;
use debugless_unwrap::DebuglessUnwrapNone as _;
use heck::KebabCase;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
Expand All @@ -15,11 +17,13 @@ use syn::{
punctuated::Punctuated,
spanned::Spanned,
token::Paren,
Attribute, Error, FnArg, Generics, Ident, Lifetime, LitStr, ReturnType, Token, Type,
Attribute, Error, FnArg, Generics, Ident, Lifetime, LitStr, PatType, ReturnType, Token, Type,
Visibility, WherePredicate,
};
use unzip_n::unzip_n;

mod constructor_argument;

unzip_n!(5);
unzip_n!(6);
unzip_n!(7);
Expand All @@ -32,7 +36,7 @@ pub struct ComponentDeclaration {
component_wheres: Vec<WherePredicate>,
constructor_attributes: Vec<Attribute>,
constructor_generics: Option<Generics>,
constructor_args: Vec<FnArg>,
constructor_args: Vec<ConstructorArgument>,
render_attributes: Vec<Attribute>,
render_generics: Option<Generics>,
render_paren: Paren,
Expand Down Expand Up @@ -172,7 +176,7 @@ impl Parse for ComponentDeclaration {
let constructor_args;
parenthesized!(constructor_args in input);
let constructor_args = Punctuated::<_, Token![,]>::parse_terminated(&constructor_args)?;
let constructor_args = constructor_args.into_iter().collect();
let constructor_args: Vec<ConstructorArgument> = constructor_args.into_iter().collect();

let render_attributes = input.call(Attribute::parse_outer)?;

Expand Down Expand Up @@ -384,6 +388,35 @@ impl Parse for ComponentDeclaration {
}
};

// These captures are put at the very end of the constructor since they always move their value.
for constructor_argument in constructor_args.iter() {
if let ConstructorArgument {
capture: constructor_argument::Capture::Yes(visibility),
fn_arg,
} = constructor_argument
{
let span = match visibility {
Visibility::Inherited => fn_arg.span(),
visibility => visibility.span(),
};
let attrs = &fn_arg.attrs;
let pat = &fn_arg.pat;
let arg = {
let PatType {
colon_token, ty, ..
} = fn_arg;
quote!(#pat#colon_token #ty)
};
call2(
quote_spanned!(span=> |#(#attrs)* #visibility #arg = {#pat}|;),
|input| {
Part::<ComponentRenderConfiguration>::parse_with_context(input, &mut cx)
},
)?
.debugless_unwrap_none()
}
}

if !input.is_empty() {
return Err(input.error(
"Currently, only one root element is supported.
Expand Down Expand Up @@ -748,6 +781,12 @@ impl ComponentDeclaration {

let body = body.part_tokens(&GenerateContext::default())?;

let constructor_args = constructor_args.into_iter().map(|arg| {
let mut fn_arg = arg.fn_arg;
fn_arg.attrs.clear();
fn_arg
});

Ok(quote! {
#new_statics
#render_statics
Expand Down

0 comments on commit 5ca8b5f

Please sign in to comment.