Add optional attributes
This commit is contained in:
1 parent
9f1c735cf1
commit
d297e2baa2
6 files changed
+171
-57
No files matched your search
@@ -224,6 +224,15 @@ impl Writer<'_> {
|
|||||||
|
|
||||||
fn write_attribute_value(&mut self, value: &ElementAttrValue) -> Result {
|
fn write_attribute_value(&mut self, value: &ElementAttrValue) -> Result {
|
||||||
match value {
|
match value {
|
||||||
|
ElementAttrValue::AttrOptionalExpr { condition, value } => {
|
||||||
|
write!(
|
||||||
|
self.out,
|
||||||
|
"if {condition} {{ ",
|
||||||
|
condition = prettyplease::unparse_expr(condition),
|
||||||
|
)?;
|
||||||
|
self.write_attribute_value(value)?;
|
||||||
|
write!(self.out, " }}")?;
|
||||||
|
}
|
||||||
ElementAttrValue::AttrLiteral(value) => {
|
ElementAttrValue::AttrLiteral(value) => {
|
||||||
write!(self.out, "{value}", value = ifmt_to_string(value))?;
|
write!(self.out, "{value}", value = ifmt_to_string(value))?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,39 @@ impl<'a> Writer<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn attr_value_len(&mut self, value: &ElementAttrValue) -> usize {
|
||||||
|
match value {
|
||||||
|
ElementAttrValue::AttrOptionalExpr { condition, value } => {
|
||||||
|
let condition_len = self.retrieve_formatted_expr(condition).len();
|
||||||
|
let value_len = self.attr_value_len(value);
|
||||||
|
|
||||||
|
condition_len + value_len + 6
|
||||||
|
}
|
||||||
|
ElementAttrValue::AttrLiteral(lit) => ifmt_to_string(lit).len(),
|
||||||
|
ElementAttrValue::AttrExpr(expr) => expr.span().line_length(),
|
||||||
|
ElementAttrValue::EventTokens(tokens) => {
|
||||||
|
let location = Location::new(tokens.span().start());
|
||||||
|
|
||||||
|
let len = if let std::collections::hash_map::Entry::Vacant(e) =
|
||||||
|
self.cached_formats.entry(location)
|
||||||
|
{
|
||||||
|
let formatted = prettyplease::unparse_expr(tokens);
|
||||||
|
let len = if formatted.contains('\n') {
|
||||||
|
10000
|
||||||
|
} else {
|
||||||
|
formatted.len()
|
||||||
|
};
|
||||||
|
e.insert(formatted);
|
||||||
|
len
|
||||||
|
} else {
|
||||||
|
self.cached_formats[&location].len()
|
||||||
|
};
|
||||||
|
|
||||||
|
len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn is_short_attrs(&mut self, attributes: &[ElementAttrNamed]) -> usize {
|
pub(crate) fn is_short_attrs(&mut self, attributes: &[ElementAttrNamed]) -> usize {
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
|
|
||||||
@@ -154,30 +187,7 @@ impl<'a> Writer<'a> {
|
|||||||
dioxus_rsx::ElementAttrName::Custom(name) => name.value().len() + 2,
|
dioxus_rsx::ElementAttrName::Custom(name) => name.value().len() + 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
total += match &attr.attr.value {
|
total += self.attr_value_len(&attr.attr.value);
|
||||||
ElementAttrValue::AttrLiteral(lit) => ifmt_to_string(lit).len(),
|
|
||||||
ElementAttrValue::AttrExpr(expr) => expr.span().line_length(),
|
|
||||||
ElementAttrValue::EventTokens(tokens) => {
|
|
||||||
let location = Location::new(tokens.span().start());
|
|
||||||
|
|
||||||
let len = if let std::collections::hash_map::Entry::Vacant(e) =
|
|
||||||
self.cached_formats.entry(location)
|
|
||||||
{
|
|
||||||
let formatted = prettyplease::unparse_expr(tokens);
|
|
||||||
let len = if formatted.contains('\n') {
|
|
||||||
10000
|
|
||||||
} else {
|
|
||||||
formatted.len()
|
|
||||||
};
|
|
||||||
e.insert(formatted);
|
|
||||||
len
|
|
||||||
} else {
|
|
||||||
self.cached_formats[&location].len()
|
|
||||||
};
|
|
||||||
|
|
||||||
len
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
total += 6;
|
total += 6;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -791,6 +791,12 @@ impl<'a> IntoAttributeValue<'a> for &'a str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoAttributeValue<'a> for String {
|
||||||
|
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
|
||||||
|
AttributeValue::Text(bump.alloc(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for f64 {
|
impl<'a> IntoAttributeValue<'a> for f64 {
|
||||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
AttributeValue::Float(self)
|
AttributeValue::Float(self)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use super::*;
|
|||||||
|
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
use quote::{quote, ToTokens, TokenStreamExt};
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
use syn::{Expr, Ident, LitStr};
|
use syn::{parse_quote, Expr, ExprIf, Ident, LitStr};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
|
||||||
pub struct ElementAttrNamed {
|
pub struct ElementAttrNamed {
|
||||||
@@ -58,16 +58,15 @@ impl ToTokens for ElementAttrNamed {
|
|||||||
|
|
||||||
let attribute = {
|
let attribute = {
|
||||||
match &attr.value {
|
match &attr.value {
|
||||||
ElementAttrValue::AttrLiteral(_) | ElementAttrValue::AttrExpr(_) => {
|
ElementAttrValue::AttrLiteral(_)
|
||||||
|
| ElementAttrValue::AttrExpr(_)
|
||||||
|
| ElementAttrValue::AttrOptionalExpr { .. } => {
|
||||||
let name = &self.attr.name;
|
let name = &self.attr.name;
|
||||||
let ns = ns(name);
|
let ns = ns(name);
|
||||||
let volitile = volitile(name);
|
let volitile = volitile(name);
|
||||||
let attribute = attribute(name);
|
let attribute = attribute(name);
|
||||||
let value = match &self.attr.value {
|
let value = &self.attr.value;
|
||||||
ElementAttrValue::AttrLiteral(lit) => quote! { #lit },
|
let value = quote! { #value };
|
||||||
ElementAttrValue::AttrExpr(expr) => quote! { #expr },
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
quote! {
|
quote! {
|
||||||
__cx.attr(
|
__cx.attr(
|
||||||
#attribute,
|
#attribute,
|
||||||
@@ -102,13 +101,62 @@ pub struct ElementAttr {
|
|||||||
pub enum ElementAttrValue {
|
pub enum ElementAttrValue {
|
||||||
/// attribute: "value"
|
/// attribute: "value"
|
||||||
AttrLiteral(IfmtInput),
|
AttrLiteral(IfmtInput),
|
||||||
|
/// attribute: if bool { "value" }
|
||||||
|
AttrOptionalExpr {
|
||||||
|
condition: Expr,
|
||||||
|
value: Box<ElementAttrValue>,
|
||||||
|
},
|
||||||
/// attribute: true
|
/// attribute: true
|
||||||
AttrExpr(Expr),
|
AttrExpr(Expr),
|
||||||
/// onclick: move |_| {}
|
/// onclick: move |_| {}
|
||||||
EventTokens(Expr),
|
EventTokens(Expr),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parse for ElementAttrValue {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
Ok(if input.peek(Token![if]) {
|
||||||
|
let if_expr = input.parse::<ExprIf>()?;
|
||||||
|
if is_if_chain_terminated(&if_expr) {
|
||||||
|
ElementAttrValue::AttrExpr(Expr::If(if_expr))
|
||||||
|
} else {
|
||||||
|
ElementAttrValue::AttrOptionalExpr {
|
||||||
|
condition: *if_expr.cond,
|
||||||
|
value: Box::new(syn::parse2(if_expr.then_branch.into_token_stream())?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if input.peek(LitStr) {
|
||||||
|
let value = input.parse()?;
|
||||||
|
ElementAttrValue::AttrLiteral(value)
|
||||||
|
} else {
|
||||||
|
let value = input.parse::<Expr>()?;
|
||||||
|
ElementAttrValue::AttrExpr(value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for ElementAttrValue {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||||
|
match self {
|
||||||
|
ElementAttrValue::AttrLiteral(lit) => tokens.append_all(quote! { #lit }),
|
||||||
|
ElementAttrValue::AttrOptionalExpr { condition, value } => {
|
||||||
|
tokens.append_all(quote! { if #condition { Some(#value) } else { None } })
|
||||||
|
}
|
||||||
|
ElementAttrValue::AttrExpr(expr) => tokens.append_all(quote! { #expr }),
|
||||||
|
ElementAttrValue::EventTokens(expr) => tokens.append_all(quote! { #expr }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ElementAttrValue {
|
impl ElementAttrValue {
|
||||||
|
fn to_str_expr(&self) -> Option<TokenStream2> {
|
||||||
|
match self {
|
||||||
|
ElementAttrValue::AttrLiteral(lit) => Some(quote!(#lit.to_string())),
|
||||||
|
ElementAttrValue::AttrOptionalExpr { value, .. } => value.to_str_expr(),
|
||||||
|
ElementAttrValue::AttrExpr(expr) => Some(quote!(#expr.to_string())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn combine(&self, separator: &str, other: &Self) -> Self {
|
fn combine(&self, separator: &str, other: &Self) -> Self {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::AttrLiteral(lit1), Self::AttrLiteral(lit2)) => {
|
(Self::AttrLiteral(lit1), Self::AttrLiteral(lit2)) => {
|
||||||
@@ -134,6 +182,62 @@ impl ElementAttrValue {
|
|||||||
ifmt.push_expr(expr2.clone());
|
ifmt.push_expr(expr2.clone());
|
||||||
Self::AttrLiteral(ifmt)
|
Self::AttrLiteral(ifmt)
|
||||||
}
|
}
|
||||||
|
(
|
||||||
|
Self::AttrOptionalExpr {
|
||||||
|
condition: condition1,
|
||||||
|
value: value1,
|
||||||
|
},
|
||||||
|
Self::AttrOptionalExpr {
|
||||||
|
condition: condition2,
|
||||||
|
value: value2,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
let first_as_string = value1.to_str_expr();
|
||||||
|
let second_as_string = value2.to_str_expr();
|
||||||
|
Self::AttrExpr(parse_quote! {
|
||||||
|
{
|
||||||
|
let mut __combined = String::new();
|
||||||
|
if #condition1 {
|
||||||
|
__combined.push_str(&#first_as_string);
|
||||||
|
}
|
||||||
|
if #condition2 {
|
||||||
|
if __combined.len() > 0 {
|
||||||
|
__combined.push_str(&#separator);
|
||||||
|
}
|
||||||
|
__combined.push_str(&#second_as_string);
|
||||||
|
}
|
||||||
|
__combined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Self::AttrOptionalExpr { condition, value }, other) => {
|
||||||
|
let first_as_string = value.to_str_expr();
|
||||||
|
let second_as_string = other.to_str_expr();
|
||||||
|
Self::AttrExpr(parse_quote! {
|
||||||
|
{
|
||||||
|
let mut __combined = #second_as_string;
|
||||||
|
if #condition {
|
||||||
|
__combined.push_str(&#separator);
|
||||||
|
__combined.push_str(&#first_as_string);
|
||||||
|
}
|
||||||
|
__combined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(other, Self::AttrOptionalExpr { condition, value }) => {
|
||||||
|
let first_as_string = other.to_str_expr();
|
||||||
|
let second_as_string = value.to_str_expr();
|
||||||
|
Self::AttrExpr(parse_quote! {
|
||||||
|
{
|
||||||
|
let mut __combined = #first_as_string;
|
||||||
|
if #condition {
|
||||||
|
__combined.push_str(&#separator);
|
||||||
|
__combined.push_str(&#second_as_string);
|
||||||
|
}
|
||||||
|
__combined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-25
@@ -11,7 +11,7 @@ use syn::{
|
|||||||
parse::{Parse, ParseBuffer, ParseStream},
|
parse::{Parse, ParseBuffer, ParseStream},
|
||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
Expr, Ident, LitStr, Result, Token,
|
Ident, LitStr, Result, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
// =======================================
|
// =======================================
|
||||||
@@ -51,13 +51,7 @@ impl Parse for Element {
|
|||||||
|
|
||||||
content.parse::<Token![:]>()?;
|
content.parse::<Token![:]>()?;
|
||||||
|
|
||||||
let value = if content.peek(LitStr) {
|
let value = content.parse::<ElementAttrValue>()?;
|
||||||
let value = content.parse()?;
|
|
||||||
ElementAttrValue::AttrLiteral(value)
|
|
||||||
} else {
|
|
||||||
let value = content.parse::<Expr>()?;
|
|
||||||
ElementAttrValue::AttrExpr(value)
|
|
||||||
};
|
|
||||||
attributes.push(ElementAttrNamed {
|
attributes.push(ElementAttrNamed {
|
||||||
el_name: el_name.clone(),
|
el_name: el_name.clone(),
|
||||||
attr: ElementAttr {
|
attr: ElementAttr {
|
||||||
@@ -100,23 +94,14 @@ impl Parse for Element {
|
|||||||
key = Some(content.parse()?);
|
key = Some(content.parse()?);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if content.peek(LitStr) {
|
let value = content.parse::<ElementAttrValue>()?;
|
||||||
attributes.push(ElementAttrNamed {
|
attributes.push(ElementAttrNamed {
|
||||||
el_name: el_name.clone(),
|
el_name: el_name.clone(),
|
||||||
attr: ElementAttr {
|
attr: ElementAttr {
|
||||||
name: ElementAttrName::BuiltIn(name),
|
name: ElementAttrName::BuiltIn(name),
|
||||||
value: ElementAttrValue::AttrLiteral(content.parse()?),
|
value,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
attributes.push(ElementAttrNamed {
|
|
||||||
el_name: el_name.clone(),
|
|
||||||
attr: ElementAttr {
|
|
||||||
name: ElementAttrName::BuiltIn(name),
|
|
||||||
value: ElementAttrValue::AttrExpr(content.parse()?),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ impl Parse for ForLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_if_chain_terminated(chain: &ExprIf) -> bool {
|
pub(crate) fn is_if_chain_terminated(chain: &ExprIf) -> bool {
|
||||||
let mut current = chain;
|
let mut current = chain;
|
||||||
loop {
|
loop {
|
||||||
if let Some((_, else_block)) = ¤t.else_branch {
|
if let Some((_, else_block)) = ¤t.else_branch {
|
||||||
|
|||||||
Reference in new issue
Block a user