diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 2f5c06f3c..58613ea0b 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -172,7 +172,7 @@ export class Interpreter { node.style = {}; } node.style[name] = value; - } else if (ns != null || ns != undefined) { + } else if (ns != null && ns != undefined) { node.setAttributeNS(ns, name, value); } else { switch (name) { @@ -266,7 +266,7 @@ export class Interpreter { this.AssignId(edit.path, edit.id); break; case "CreateElement": - if (edit.namespace !== null || edit.namespace !== undefined) { + if (edit.namespace !== null && edit.namespace !== undefined) { this.CreateElementNs(edit.name, edit.id, edit.namespace); } else { this.CreateElement(edit.name, edit.id); diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 09c6bd6c8..d974a6c50 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -245,7 +245,11 @@ impl<'a> DynamicContext<'a> { quote! { ::dioxus::core::TemplateNode::Text(#text) } } - BodyNode::Text(_) | BodyNode::RawExpr(_) | BodyNode::Component(_) => { + BodyNode::RawExpr(_) + | BodyNode::Text(_) + | BodyNode::ForLoop(_) + | BodyNode::IfChain(_) + | BodyNode::Component(_) => { let ct = self.dynamic_nodes.len(); self.dynamic_nodes.push(root); self.node_paths.push(self.current_path.clone()); diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index 4013c9cba..7b4bd232b 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -5,7 +5,7 @@ use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned, - token, Expr, LitStr, Result, + token, Block, Expr, ExprIf, LitStr, Pat, Result, }; /* @@ -20,6 +20,8 @@ Parse pub enum BodyNode { Element(Element), Component(Component), + ForLoop(ForLoop), + IfChain(ExprIf), Text(IfmtInput), RawExpr(Expr), } @@ -35,6 +37,8 @@ impl BodyNode { BodyNode::Component(component) => component.name.span(), BodyNode::Text(text) => text.source.span(), BodyNode::RawExpr(exp) => exp.span(), + BodyNode::ForLoop(fl) => fl.for_token.span(), + BodyNode::IfChain(f) => f.if_token.span(), } } } @@ -89,6 +93,28 @@ impl Parse for BodyNode { } } + // Transform for loops into into_iter calls + if stream.peek(Token![for]) { + let _f = stream.parse::()?; + let pat = stream.parse::()?; + let _i = stream.parse::()?; + let expr = stream.parse::>()?; + let body = stream.parse::()?; + + return Ok(BodyNode::ForLoop(ForLoop { + for_token: _f, + pat, + in_token: _i, + expr, + body, + })); + } + + // Transform unterminated if statements into terminated optional if statements + if stream.peek(Token![if]) { + return Ok(BodyNode::IfChain(stream.parse()?)); + } + Ok(BodyNode::RawExpr(stream.parse::()?)) } } @@ -104,6 +130,104 @@ impl ToTokens for BodyNode { BodyNode::RawExpr(exp) => tokens.append_all(quote! { __cx.fragment_from_iter(#exp) }), + BodyNode::ForLoop(exp) => { + let ForLoop { + pat, expr, body, .. + } = exp; + + tokens.append_all(quote! { + __cx.fragment_from_iter( + (#expr).into_iter().map(|#pat| { + #body + }) + ) + }) + } + BodyNode::IfChain(chain) => { + if is_if_chain_terminated(chain) { + tokens.append_all(quote! { + __cx.fragment_from_iter(#chain) + }); + } else { + let ExprIf { + cond, + then_branch, + else_branch, + .. + } = chain; + + let mut body = TokenStream2::new(); + + body.append_all(quote! { + if #cond { + Some(#then_branch) + } + }); + + let mut elif = else_branch; + + while let Some((_, ref branch)) = elif { + match branch.as_ref() { + Expr::If(ref eelif) => { + let ExprIf { + cond, + then_branch, + else_branch, + .. + } = eelif; + + body.append_all(quote! { + else if #cond { + Some(#then_branch) + } + }); + + elif = else_branch; + } + _ => { + body.append_all(quote! { + else { + #branch + } + }); + break; + } + } + } + + body.append_all(quote! { + else { None } + }); + + tokens.append_all(quote! { + __cx.fragment_from_iter(#body) + }); + } + } + } + } +} + +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub struct ForLoop { + pub for_token: Token![for], + pub pat: Pat, + pub in_token: Token![in], + pub expr: Box, + pub body: Block, +} + +fn is_if_chain_terminated(chain: &ExprIf) -> bool { + let mut current = chain; + loop { + if let Some((_, else_block)) = ¤t.else_branch { + if let Expr::If(else_if) = else_block.as_ref() { + current = else_if; + } else { + return true; + } + } else { + return false; } } }