Skip to content

Commit

Permalink
implement braced key value attribute. See also: [#54](#54 (comment))
Browse files Browse the repository at this point in the history
  • Loading branch information
tachibanayui authored and vldm committed Jul 28, 2024
1 parent 0ee7a50 commit 33f1aa7
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 79 deletions.
127 changes: 54 additions & 73 deletions src/node/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{
parse::{discouraged::Speculative, Parse, ParseStream},
parse_quote,
punctuated::Punctuated,
spanned::Spanned,
token::{Brace, Comma, Paren},
Attribute, Block, Expr, Lit, Pat, PatType, Token,
Attribute, Expr, Lit, Pat, PatType, Token,
};

use super::{parse::parse_valid_block_expr, InvalidBlock};
use crate::{
node::{NodeBlock, NodeName},
parser::recoverable::{ParseRecoverable, RecoverableContext},
Expand All @@ -16,8 +18,15 @@ use crate::{
#[derive(Clone, Debug, syn_derive::ToTokens)]
pub struct AttributeValueExpr {
pub token_eq: Token![=],
pub value: Expr,
pub value: KVAttributeValue,
}

#[derive(Clone, Debug, syn_derive::ToTokens)]
pub enum KVAttributeValue {
Expr(Expr),
Braced(InvalidBlock),
}

impl AttributeValueExpr {
///
/// Returns string representation of inner value,
Expand Down Expand Up @@ -46,7 +55,7 @@ impl AttributeValueExpr {
/// Adapted from leptos
pub fn value_literal_string(&self) -> Option<String> {
match &self.value {
Expr::Lit(l) => match &l.lit {
KVAttributeValue::Expr(Expr::Lit(l)) => match &l.lit {
Lit::Str(s) => Some(s.value()),
Lit::Char(c) => Some(c.value().to_string()),
Lit::Int(i) => Some(i.base10_digits().to_string()),
Expand All @@ -69,7 +78,6 @@ pub struct AttributeValueBlock {
pub enum KeyedAttributeValue {
Binding(FnBinding),
Value(AttributeValueExpr),
Block(AttributeValueBlock),
None,
}

Expand All @@ -79,7 +87,6 @@ impl KeyedAttributeValue {
KeyedAttributeValue::Value(v) => Some(v),
KeyedAttributeValue::None => None,
KeyedAttributeValue::Binding(_) => None,
KeyedAttributeValue::Block(_) => None,
}
}
}
Expand Down Expand Up @@ -129,7 +136,13 @@ impl KeyedAttribute {
}

pub fn value(&self) -> Option<&Expr> {
self.possible_value.to_value().map(|v| &v.value)
self.possible_value
.to_value()
.map(|v| match &v.value {
KVAttributeValue::Expr(expr) => Some(expr),
KVAttributeValue::Braced(_) => None,
})
.flatten()
}

// Checks if error is about eof.
Expand Down Expand Up @@ -220,48 +233,8 @@ pub enum NodeAttribute {
Attribute(KeyedAttribute),
}

// Use custom parse to correct error.
impl Parse for KeyedAttribute {
fn parse(input: ParseStream) -> syn::Result<Self> {
let key = NodeName::parse(input)?;
let possible_value = if input.peek(Paren) {
KeyedAttributeValue::Binding(FnBinding::parse(input)?)
} else if input.peek(Token![=]) {
let eq = input.parse::<Token![=]>()?;
if input.is_empty() {
return Err(syn::Error::new(eq.span(), "missing attribute value"));
}

let fork = input.fork();
let res = fork.parse::<Expr>().map_err(|e| {
// if we stuck on end of input, span that is created will be call_site, so we
// need to correct it, in order to make it more IDE friendly.
if fork.is_empty() {
KeyedAttribute::correct_expr_error_span(e, input)
} else {
e
}
})?;

input.advance_to(&fork);
KeyedAttributeValue::Value(AttributeValueExpr {
token_eq: eq,
value: res,
})
} else {
KeyedAttributeValue::None
};
Ok(KeyedAttribute {
key,
possible_value,
})
}
}

impl ParseRecoverable for KeyedAttribute {
fn parse_recoverable(parser: &mut RecoverableContext, input: ParseStream) -> Option<Self> {
// TODO: Make this function actually recoverable

let key = NodeName::parse(input)
.map_err(|e| parser.push_diagnostic(e))
.ok()?;
Expand All @@ -283,34 +256,42 @@ impl ParseRecoverable for KeyedAttribute {
}

let fork = input.fork();
if let Some(res) = parser.parse_recoverable::<NodeBlock>(&fork) {
input.advance_to(&fork);
KeyedAttributeValue::Block(AttributeValueBlock {
token_eq: eq,
value: res,
})
} else {
let res = fork
.parse::<Expr>()
.map_err(|e| {
// if we stuck on end of input, span that is created will be call_site, so
// we need to correct it, in order to make it more
// IDE friendly.
if fork.is_empty() {
KeyedAttribute::correct_expr_error_span(e, input)
} else {
e
}
})
.map_err(|e| parser.push_diagnostic(e))
.ok()?;

input.advance_to(&fork);
KeyedAttributeValue::Value(AttributeValueExpr {
token_eq: eq,
value: res,
})
}
let rs = match parse_valid_block_expr(parser, &fork) {
Ok(vbl) => {
input.advance_to(&fork);
KVAttributeValue::Expr(parse_quote!(#vbl))
}

Err(_) if input.fork().peek(Brace) => {
let ivb = parser.parse_simple(input)?;
KVAttributeValue::Braced(ivb)
}
Err(_) => {
let res = fork
.parse::<Expr>()
.map_err(|e| {
// if we stuck on end of input, span that is created will be call_site,
// so we need to correct it, in order to
// make it more IDE friendly.
if fork.is_empty() {
KeyedAttribute::correct_expr_error_span(e, input)
} else {
e
}
})
.map_err(|e| parser.push_diagnostic(e))
.ok()?;

input.advance_to(&fork);
KVAttributeValue::Expr(res)
}
};

KeyedAttributeValue::Value(AttributeValueExpr {
token_eq: eq,
value: rs,
})
} else {
KeyedAttributeValue::None
};
Expand Down
3 changes: 2 additions & 1 deletion src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ mod parser_ext;
mod raw_text;

pub use attribute::{
AttributeValueExpr, AttributeValueBlock, FnBinding, KeyedAttribute, KeyedAttributeValue, NodeAttribute,
AttributeValueBlock, AttributeValueExpr, FnBinding, KVAttributeValue, KeyedAttribute,
KeyedAttributeValue, NodeAttribute,
};
pub use node_name::{NodeName, NodeNameFragment};
pub use node_value::{InvalidBlock, NodeBlock};
Expand Down
4 changes: 2 additions & 2 deletions src/node/node_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use syn::{token::Brace, Block};
#[derive(Clone, Debug, syn_derive::ToTokens, syn_derive::Parse)]
pub struct InvalidBlock {
#[syn(braced)]
brace: Brace,
pub brace: Brace,
#[syn(in = brace)]
body: TokenStream,
pub body: TokenStream,
}

/// Block node.
Expand Down
2 changes: 1 addition & 1 deletion src/node/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ fn block_transform(input: ParseStream, transform_fn: &TransformBlockFn) -> syn::
}

#[allow(clippy::needless_pass_by_ref_mut)]
fn parse_valid_block_expr(
pub(crate) fn parse_valid_block_expr(
parser: &mut RecoverableContext,
input: syn::parse::ParseStream,
) -> syn::Result<Block> {
Expand Down
1 change: 1 addition & 0 deletions src/parser/recoverable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ impl RecoverableContext {

///
/// Result of parsing.
#[derive(Debug)]
pub enum ParsingResult<T> {
/// Fully valid ast that was parsed without errors.
Ok(T),
Expand Down
16 changes: 14 additions & 2 deletions src/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,6 @@ where
KeyedAttributeValue::None => self.visit_attribute_flag(&mut attribute.key),
KeyedAttributeValue::Binding(b) => self.visit_attribute_binding(&mut attribute.key, b),
KeyedAttributeValue::Value(v) => self.visit_attribute_value(&mut attribute.key, v),
KeyedAttributeValue::Block(b) => self.visit_attribute_block(&mut attribute.key, b),
}
}
fn visit_attribute_flag(&mut self, key: &mut NodeName) -> bool {
Expand All @@ -374,7 +373,10 @@ where
visit_inner!(self.visitor.visit_attribute_value(key, value));

self.visit_node_name(key);
self.visit_rust_code(RustCode::Expr(&mut value.value))
match &mut value.value {
KVAttributeValue::Expr(expr) => self.visit_rust_code(RustCode::Expr(expr)),
KVAttributeValue::Braced(braced) => self.visit_invalid_block(braced),
}
}
fn visit_attribute_block(
&mut self,
Expand Down Expand Up @@ -550,6 +552,16 @@ mod tests {
assert_eq!(node_names, vec!["div", "span", "span", "foo"]);
}

#[test]
fn asd() {
let a = quote! {
<MyComponent style={{width: "20vw"}} />
};

let a = crate::parse2(a);
dbg!(a);
}

#[test]
fn collect_rust_blocks() {
#[derive(Default)]
Expand Down

0 comments on commit 33f1aa7

Please sign in to comment.