From 9db6a4926e105dd8ef0d2f7a2da962b7de220267 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 10 Oct 2020 11:37:48 +0200 Subject: [PATCH 01/47] Initial commit Forked at: 66d506e13329a06f7dce0b55a9427972b8aad3ff Parent branch: origin/master From 850f07f10791a3c1e7d6e07dd53bb711f0280be5 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 10 Oct 2020 11:38:23 +0200 Subject: [PATCH 02/47] Add the ability to add child nodes conditionally in html! --- yew-macro/src/html_tree/html_if.rs | 59 +++++++++++++++++++++++++++ yew-macro/src/html_tree/mod.rs | 6 +++ yew-macro/tests/macro/html-if-pass.rs | 27 ++++++++++++ yew-macro/tests/macro_test.rs | 3 ++ 4 files changed, 95 insertions(+) create mode 100644 yew-macro/src/html_tree/html_if.rs create mode 100644 yew-macro/tests/macro/html-if-pass.rs diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs new file mode 100644 index 00000000000..4bfcf058903 --- /dev/null +++ b/yew-macro/src/html_tree/html_if.rs @@ -0,0 +1,59 @@ +use super::ToNodeIterator; +use crate::PeekValue; +use boolinator::Boolinator; +use proc_macro2::TokenStream; +use quote::{quote_spanned, ToTokens}; +use syn::buffer::Cursor; +use syn::parse::{Parse, ParseStream, Result as ParseResult}; +use syn::spanned::Spanned; +use syn::{Expr, ExprIf}; + +pub struct HtmlIf(ExprIf); + +impl PeekValue<()> for HtmlIf { + fn peek(cursor: Cursor) -> Option<()> { + let (ident, _) = cursor.ident()?; + (ident == "if").as_option() + } +} + +impl Parse for HtmlIf { + fn parse(input: ParseStream) -> ParseResult { + let expr = match input.parse() { + Ok(Expr::If(expr)) => expr, + _ => { + return Err(syn::Error::new( + input.span(), + "expected a valid `if` expression", + )) + } + }; + + Ok(HtmlIf(expr)) + } +} + +impl ToTokens for HtmlIf { + fn to_tokens(&self, tokens: &mut TokenStream) { + let expr = &self.0; + let cond = &expr.cond; + let then_branch = &expr.then_branch; + let default_else_branch = Box::new(syn::parse_str::("{ html!() }").unwrap()); + let else_branch = &expr + .else_branch + .as_ref() + .map(|(_, expr)| expr) + .unwrap_or(&default_else_branch); + let new_tokens = quote_spanned! {expr.span()=> + if #cond #then_branch else #else_branch + }; + + tokens.extend(new_tokens); + } +} + +impl ToNodeIterator for HtmlIf { + fn to_node_iterator_stream(&self) -> Option { + todo!(); + } +} diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index f118fc2e682..c7bcfb6cf1b 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -1,6 +1,7 @@ mod html_block; mod html_component; mod html_dashed_name; +mod html_if; mod html_iterable; mod html_list; mod html_node; @@ -11,6 +12,7 @@ use crate::PeekValue; use html_block::HtmlBlock; use html_component::HtmlComponent; use html_dashed_name::HtmlDashedName; +use html_if::HtmlIf; use html_iterable::HtmlIterable; use html_list::HtmlList; use html_node::HtmlNode; @@ -89,6 +91,7 @@ impl ToTokens for HtmlTree { pub enum HtmlRoot { Tree(HtmlTree), Iterable(Box), + If(Box), Node(Box), } @@ -98,6 +101,8 @@ impl Parse for HtmlRoot { Self::Tree(input.parse()?) } else if HtmlIterable::peek(input.cursor()).is_some() { Self::Iterable(Box::new(input.parse()?)) + } else if HtmlIf::peek(input.cursor()).is_some() { + Self::If(Box::new(input.parse()?)) } else { Self::Node(Box::new(input.parse()?)) }; @@ -120,6 +125,7 @@ impl ToTokens for HtmlRoot { Self::Tree(tree) => tree.to_tokens(tokens), Self::Node(node) => node.to_tokens(tokens), Self::Iterable(iterable) => iterable.to_tokens(tokens), + Self::If(r#if) => r#if.to_tokens(tokens), } } } diff --git a/yew-macro/tests/macro/html-if-pass.rs b/yew-macro/tests/macro/html-if-pass.rs new file mode 100644 index 00000000000..54fd1f51c90 --- /dev/null +++ b/yew-macro/tests/macro/html-if-pass.rs @@ -0,0 +1,27 @@ +use yew::prelude::*; + +fn compile_pass() { + /* + html! { for iter::empty::() }; + html! { for Vec::::new() }; + html! { for Vec::::new().into_iter() }; + html! { for (0..3).map(|num| { html! { {num} } }) }; + html! { for {iter::empty::()} }; + + let empty: Vec = Vec::new(); + html! { for empty.into_iter() }; + + let empty: Vec = Vec::new(); + html! { for empty }; + */ + + let boolean = true; + html! { if boolean { html!() } }; + html! { if boolean { html!() } else { html!() } }; + + let option = Some("text"); + html! { if let Some(text) = option { html!( {text} ) } }; + html! { if let Some(text) = option { html!( {text} ) } else { html!() } }; +} + +fn main() {} diff --git a/yew-macro/tests/macro_test.rs b/yew-macro/tests/macro_test.rs index 849d1159083..7ea08a48314 100644 --- a/yew-macro/tests/macro_test.rs +++ b/yew-macro/tests/macro_test.rs @@ -15,6 +15,9 @@ fn tests() { t.pass("tests/macro/html-iterable-pass.rs"); t.compile_fail("tests/macro/html-iterable-fail.rs"); + t.pass("tests/macro/html-if-pass.rs"); + //t.compile_fail("tests/macro/html-if-fail.rs"); + t.pass("tests/macro/html-list-pass.rs"); t.compile_fail("tests/macro/html-list-fail.rs"); From e06676b9622d7a3240bb9ab5dd3cdb3fb2256739 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 10 Oct 2020 12:32:05 +0200 Subject: [PATCH 03/47] WIP Forked at: 66d506e13329a06f7dce0b55a9427972b8aad3ff Parent branch: origin/master --- yew-macro/src/html_tree/html_block.rs | 6 ++++++ yew-macro/src/html_tree/html_if.rs | 8 ++++++-- yew-macro/tests/macro/html-if-pass.rs | 22 ++++++++-------------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/yew-macro/src/html_tree/html_block.rs b/yew-macro/src/html_tree/html_block.rs index 2f89d92baea..6eb8c11fc29 100644 --- a/yew-macro/src/html_tree/html_block.rs +++ b/yew-macro/src/html_tree/html_block.rs @@ -1,3 +1,4 @@ +use super::html_if::HtmlIf; use super::html_iterable::HtmlIterable; use super::html_node::HtmlNode; use super::ToNodeIterator; @@ -17,6 +18,7 @@ pub struct HtmlBlock { enum BlockContent { Node(Box), Iterable(Box), + If(Box), } impl PeekValue<()> for HtmlBlock { @@ -31,6 +33,8 @@ impl Parse for HtmlBlock { let brace = braced!(content in input); let content = if HtmlIterable::peek(content.cursor()).is_some() { BlockContent::Iterable(Box::new(content.parse()?)) + } else if HtmlIf::peek(content.cursor()).is_some() { + BlockContent::If(Box::new(content.parse()?)) } else { BlockContent::Node(Box::new(content.parse()?)) }; @@ -44,6 +48,7 @@ impl ToTokens for HtmlBlock { let HtmlBlock { content, .. } = self; let new_tokens = match content { BlockContent::Iterable(html_iterable) => quote! {#html_iterable}, + BlockContent::If(html_if) => quote! {#html_if}, BlockContent::Node(html_node) => quote! {#html_node}, }; @@ -56,6 +61,7 @@ impl ToNodeIterator for HtmlBlock { let HtmlBlock { content, brace } = self; let new_tokens = match content { BlockContent::Iterable(iterable) => iterable.to_node_iterator_stream(), + BlockContent::If(r#if) => r#if.to_node_iterator_stream(), BlockContent::Node(node) => node.to_node_iterator_stream(), }?; diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index 4bfcf058903..6a6dff675f7 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -38,7 +38,7 @@ impl ToTokens for HtmlIf { let expr = &self.0; let cond = &expr.cond; let then_branch = &expr.then_branch; - let default_else_branch = Box::new(syn::parse_str::("{ html!() }").unwrap()); + let default_else_branch = Box::new(syn::parse_str::("{html!()}").unwrap()); let else_branch = &expr .else_branch .as_ref() @@ -54,6 +54,10 @@ impl ToTokens for HtmlIf { impl ToNodeIterator for HtmlIf { fn to_node_iterator_stream(&self) -> Option { - todo!(); + let mut tokens = TokenStream::new(); + self.to_tokens(&mut tokens); + Some(quote_spanned! {self.0.span()=> + ::std::iter::once(#tokens) + }) } } diff --git a/yew-macro/tests/macro/html-if-pass.rs b/yew-macro/tests/macro/html-if-pass.rs index 54fd1f51c90..bb3f89f94ba 100644 --- a/yew-macro/tests/macro/html-if-pass.rs +++ b/yew-macro/tests/macro/html-if-pass.rs @@ -1,20 +1,6 @@ use yew::prelude::*; fn compile_pass() { - /* - html! { for iter::empty::() }; - html! { for Vec::::new() }; - html! { for Vec::::new().into_iter() }; - html! { for (0..3).map(|num| { html! { {num} } }) }; - html! { for {iter::empty::()} }; - - let empty: Vec = Vec::new(); - html! { for empty.into_iter() }; - - let empty: Vec = Vec::new(); - html! { for empty }; - */ - let boolean = true; html! { if boolean { html!() } }; html! { if boolean { html!() } else { html!() } }; @@ -22,6 +8,14 @@ fn compile_pass() { let option = Some("text"); html! { if let Some(text) = option { html!( {text} ) } }; html! { if let Some(text) = option { html!( {text} ) } else { html!() } }; + + let boolean = true; + html! {
{if boolean { html!() }}
}; + html! {
{if boolean { html!() } else { html!() }}
}; + + let option = Some("text"); + html! {
{if let Some(text) = option { html!( {text} ) }}
}; + html! {
{if let Some(text) = option { html!( {text} ) } else { html!() }}
}; } fn main() {} From ac4a5b85c88c7de34cf7942570c380a05f4b8d8e Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 10 Oct 2020 12:32:49 +0200 Subject: [PATCH 04/47] CLEANUP Forked at: 66d506e13329a06f7dce0b55a9427972b8aad3ff Parent branch: origin/master --- yew-macro/tests/macro/html-if-pass.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/yew-macro/tests/macro/html-if-pass.rs b/yew-macro/tests/macro/html-if-pass.rs index bb3f89f94ba..7ab6e7a5a3f 100644 --- a/yew-macro/tests/macro/html-if-pass.rs +++ b/yew-macro/tests/macro/html-if-pass.rs @@ -4,16 +4,12 @@ fn compile_pass() { let boolean = true; html! { if boolean { html!() } }; html! { if boolean { html!() } else { html!() } }; - - let option = Some("text"); - html! { if let Some(text) = option { html!( {text} ) } }; - html! { if let Some(text) = option { html!( {text} ) } else { html!() } }; - - let boolean = true; html! {
{if boolean { html!() }}
}; html! {
{if boolean { html!() } else { html!() }}
}; let option = Some("text"); + html! { if let Some(text) = option { html!( {text} ) } }; + html! { if let Some(text) = option { html!( {text} ) } else { html!() } }; html! {
{if let Some(text) = option { html!( {text} ) }}
}; html! {
{if let Some(text) = option { html!( {text} ) } else { html!() }}
}; } From b4645a171666a05dc91ad13297334167438cca8a Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 10 Oct 2020 12:45:18 +0200 Subject: [PATCH 05/47] Experiment --- yew-macro/src/html_tree/html_block.rs | 6 ------ yew-macro/src/html_tree/html_if.rs | 13 +------------ yew-macro/src/html_tree/mod.rs | 6 ++++++ yew-macro/tests/macro/html-if-pass.rs | 8 ++++---- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/yew-macro/src/html_tree/html_block.rs b/yew-macro/src/html_tree/html_block.rs index 6eb8c11fc29..2f89d92baea 100644 --- a/yew-macro/src/html_tree/html_block.rs +++ b/yew-macro/src/html_tree/html_block.rs @@ -1,4 +1,3 @@ -use super::html_if::HtmlIf; use super::html_iterable::HtmlIterable; use super::html_node::HtmlNode; use super::ToNodeIterator; @@ -18,7 +17,6 @@ pub struct HtmlBlock { enum BlockContent { Node(Box), Iterable(Box), - If(Box), } impl PeekValue<()> for HtmlBlock { @@ -33,8 +31,6 @@ impl Parse for HtmlBlock { let brace = braced!(content in input); let content = if HtmlIterable::peek(content.cursor()).is_some() { BlockContent::Iterable(Box::new(content.parse()?)) - } else if HtmlIf::peek(content.cursor()).is_some() { - BlockContent::If(Box::new(content.parse()?)) } else { BlockContent::Node(Box::new(content.parse()?)) }; @@ -48,7 +44,6 @@ impl ToTokens for HtmlBlock { let HtmlBlock { content, .. } = self; let new_tokens = match content { BlockContent::Iterable(html_iterable) => quote! {#html_iterable}, - BlockContent::If(html_if) => quote! {#html_if}, BlockContent::Node(html_node) => quote! {#html_node}, }; @@ -61,7 +56,6 @@ impl ToNodeIterator for HtmlBlock { let HtmlBlock { content, brace } = self; let new_tokens = match content { BlockContent::Iterable(iterable) => iterable.to_node_iterator_stream(), - BlockContent::If(r#if) => r#if.to_node_iterator_stream(), BlockContent::Node(node) => node.to_node_iterator_stream(), }?; diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index 6a6dff675f7..11dfc6d3542 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -1,4 +1,3 @@ -use super::ToNodeIterator; use crate::PeekValue; use boolinator::Boolinator; use proc_macro2::TokenStream; @@ -20,7 +19,7 @@ impl PeekValue<()> for HtmlIf { impl Parse for HtmlIf { fn parse(input: ParseStream) -> ParseResult { let expr = match input.parse() { - Ok(Expr::If(expr)) => expr, + Ok(expr) => expr, _ => { return Err(syn::Error::new( input.span(), @@ -51,13 +50,3 @@ impl ToTokens for HtmlIf { tokens.extend(new_tokens); } } - -impl ToNodeIterator for HtmlIf { - fn to_node_iterator_stream(&self) -> Option { - let mut tokens = TokenStream::new(); - self.to_tokens(&mut tokens); - Some(quote_spanned! {self.0.span()=> - ::std::iter::once(#tokens) - }) - } -} diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index c7bcfb6cf1b..4d84e5d3c23 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -30,6 +30,7 @@ pub enum HtmlType { Component, List, Tag, + If, Empty, } @@ -38,6 +39,7 @@ pub enum HtmlTree { Component(Box), List(Box), Tag(Box), + If(Box), Empty, } @@ -51,6 +53,7 @@ impl Parse for HtmlTree { HtmlType::Tag => HtmlTree::Tag(Box::new(input.parse()?)), HtmlType::Block => HtmlTree::Block(Box::new(input.parse()?)), HtmlType::List => HtmlTree::List(Box::new(input.parse()?)), + HtmlType::If => HtmlTree::If(Box::new(input.parse()?)), }; Ok(html_tree) } @@ -68,6 +71,8 @@ impl PeekValue for HtmlTree { Some(HtmlType::Tag) } else if HtmlBlock::peek(cursor).is_some() { Some(HtmlType::Block) + } else if HtmlIf::peek(cursor).is_some() { + Some(HtmlType::If) } else { None } @@ -84,6 +89,7 @@ impl ToTokens for HtmlTree { HtmlTree::Tag(tag) => tag.to_tokens(tokens), HtmlTree::List(list) => list.to_tokens(tokens), HtmlTree::Block(block) => block.to_tokens(tokens), + HtmlTree::If(block) => block.to_tokens(tokens), } } } diff --git a/yew-macro/tests/macro/html-if-pass.rs b/yew-macro/tests/macro/html-if-pass.rs index 7ab6e7a5a3f..f5753827f73 100644 --- a/yew-macro/tests/macro/html-if-pass.rs +++ b/yew-macro/tests/macro/html-if-pass.rs @@ -4,14 +4,14 @@ fn compile_pass() { let boolean = true; html! { if boolean { html!() } }; html! { if boolean { html!() } else { html!() } }; - html! {
{if boolean { html!() }}
}; - html! {
{if boolean { html!() } else { html!() }}
}; + html! {
if boolean { html!() }
}; + html! {
if boolean { html!() } else { html!() }
}; let option = Some("text"); html! { if let Some(text) = option { html!( {text} ) } }; html! { if let Some(text) = option { html!( {text} ) } else { html!() } }; - html! {
{if let Some(text) = option { html!( {text} ) }}
}; - html! {
{if let Some(text) = option { html!( {text} ) } else { html!() }}
}; + html! {
if let Some(text) = option { html!( {text} ) }
}; + html! {
if let Some(text) = option { html!( {text} ) } else { html!() }
}; } fn main() {} From 24d213a0fbb3c0853a96c20bedbc2ac379e55141 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 10 Oct 2020 12:58:56 +0200 Subject: [PATCH 06/47] Failing test --- yew-macro/tests/macro/html-if-fail.rs | 21 ++++++++ yew-macro/tests/macro/html-if-fail.stderr | 60 +++++++++++++++++++++++ yew-macro/tests/macro_test.rs | 2 +- 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 yew-macro/tests/macro/html-if-fail.rs create mode 100644 yew-macro/tests/macro/html-if-fail.stderr diff --git a/yew-macro/tests/macro/html-if-fail.rs b/yew-macro/tests/macro/html-if-fail.rs new file mode 100644 index 00000000000..760328371c0 --- /dev/null +++ b/yew-macro/tests/macro/html-if-fail.rs @@ -0,0 +1,21 @@ +use yew::prelude::*; + +fn compile_fail() { + html! { if {html!()} }; + let value = 42; + html! { if value { html!() } }; + html! { if {value} { html!() } }; + let boolean = true; + html! { if boolean }; + html! { if boolean { () } }; + + html! { + <> +
+ if value { + } + + }; +} + +fn main() {} diff --git a/yew-macro/tests/macro/html-if-fail.stderr b/yew-macro/tests/macro/html-if-fail.stderr new file mode 100644 index 00000000000..a58c1fe293d --- /dev/null +++ b/yew-macro/tests/macro/html-if-fail.stderr @@ -0,0 +1,60 @@ +error: expected a valid `if` expression + --> $DIR/html-if-fail.rs:4:5 + | +4 | html! { if {html!()} }; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected a valid `if` expression + --> $DIR/html-if-fail.rs:9:5 + | +9 | html! { if boolean }; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/html-if-fail.rs:6:16 + | +6 | html! { if value { html!() } }; + | ^^^^^ expected `bool`, found integer + +error[E0308]: mismatched types + --> $DIR/html-if-fail.rs:7:17 + | +7 | html! { if {value} { html!() } }; + | ^^^^^ expected `bool`, found integer + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/html-if-fail.rs:10:5 + | +10 | html! { if boolean { () } }; + | ^^^^^^^^^^^^^^^^^^^^^--^^^^^ + | | | + | | expected because of this + | expected `()`, found enum `yew::virtual_dom::vnode::VNode` + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> $DIR/html-if-fail.rs:15:16 + | +15 | if value { + | ^^^^^ expected `bool`, found integer + +error[E0308]: `if` and `else` have incompatible types + --> $DIR/html-if-fail.rs:12:5 + | +12 | / html! { +13 | | <> +14 | |
+15 | | if value { + | |______________________- +16 | || } + | ||_____________- expected because of this +17 | | +18 | | }; + | |______^ expected `()`, found enum `yew::virtual_dom::vnode::VNode` + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/yew-macro/tests/macro_test.rs b/yew-macro/tests/macro_test.rs index 7ea08a48314..7215922d80d 100644 --- a/yew-macro/tests/macro_test.rs +++ b/yew-macro/tests/macro_test.rs @@ -16,7 +16,7 @@ fn tests() { t.compile_fail("tests/macro/html-iterable-fail.rs"); t.pass("tests/macro/html-if-pass.rs"); - //t.compile_fail("tests/macro/html-if-fail.rs"); + t.compile_fail("tests/macro/html-if-fail.rs"); t.pass("tests/macro/html-list-pass.rs"); t.compile_fail("tests/macro/html-list-fail.rs"); From 7b5fc7531c0df0c6bfa81f490edda579fc173141 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 10 Oct 2020 13:10:27 +0200 Subject: [PATCH 07/47] More tests --- yew/src/virtual_dom/vtag.rs | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/yew/src/virtual_dom/vtag.rs b/yew/src/virtual_dom/vtag.rs index 0b3c95ef948..3cf246e2184 100644 --- a/yew/src/virtual_dom/vtag.rs +++ b/yew/src/virtual_dom/vtag.rs @@ -1423,6 +1423,68 @@ mod tests { elem.detach(&parent); assert!(node_ref.get().is_none()); } + + #[test] + fn html_if_bool() { + assert_class( + html! { + if true { + html!(
) + } + }, + "foo", + ); + assert_class( + html! { + if false { + html!(
) + } else { + html!(
) + } + }, + "bar", + ); + assert_eq!( + html! { + if false { + html!(
) + } + }, + html!() + ); + } + + #[test] + fn html_if_option() { + let option_foo = Some("foo"); + let none: Option<&'static str> = None; + assert_class( + html! { + if let Some(class) = option_foo { + html!(
) + } + }, + "foo", + ); + assert_class( + html! { + if let Some(class) = none { + html!(
) + } else { + html!(
) + } + }, + "bar", + ); + assert_eq!( + html! { + if let Some(class) = none { + html!(
) + } + }, + html!() + ); + } } #[cfg(all(test, feature = "web_sys"))] From 1ddd05177bb9e78c33fce553c68ee158f45ff2e9 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 10 Oct 2020 13:20:26 +0200 Subject: [PATCH 08/47] More tests --- yew/src/virtual_dom/vtag.rs | 102 ++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 10 deletions(-) diff --git a/yew/src/virtual_dom/vtag.rs b/yew/src/virtual_dom/vtag.rs index 3cf246e2184..293c6856ae0 100644 --- a/yew/src/virtual_dom/vtag.rs +++ b/yew/src/virtual_dom/vtag.rs @@ -1426,15 +1426,15 @@ mod tests { #[test] fn html_if_bool() { - assert_class( + assert_eq!( html! { if true { html!(
) } }, - "foo", + html!(
), ); - assert_class( + assert_eq!( html! { if false { html!(
) @@ -1442,7 +1442,9 @@ mod tests { html!(
) } }, - "bar", + html! { +
+ }, ); assert_eq!( html! { @@ -1450,7 +1452,53 @@ mod tests { html!(
) } }, - html!() + html!(), + ); + + // non-root tests + assert_eq!( + html! { +
+ if true { + html!(
) + } +
+ }, + html! { +
+
+
+ }, + ); + assert_eq!( + html! { +
+ if false { + html!(
) + } else { + html!(
) + } +
+ }, + html! { +
+
+
+ }, + ); + assert_eq!( + html! { +
+ if false { + html!(
) + } +
+ }, + html! { +
+ <> +
+ }, ); } @@ -1458,15 +1506,15 @@ mod tests { fn html_if_option() { let option_foo = Some("foo"); let none: Option<&'static str> = None; - assert_class( + assert_eq!( html! { if let Some(class) = option_foo { html!(
) } }, - "foo", + html!(
), ); - assert_class( + assert_eq!( html! { if let Some(class) = none { html!(
) @@ -1474,7 +1522,7 @@ mod tests { html!(
) } }, - "bar", + html!(
), ); assert_eq!( html! { @@ -1482,7 +1530,41 @@ mod tests { html!(
) } }, - html!() + html!(), + ); + + // non-root tests + assert_eq!( + html! { +
+ if let Some(class) = option_foo { + html!(
) + } +
+ }, + html!(
), + ); + assert_eq!( + html! { +
+ if let Some(class) = none { + html!(
) + } else { + html!(
) + } +
+ }, + html!(
), + ); + assert_eq!( + html! { +
+ if let Some(class) = none { + html!(
) + } +
+ }, + html!(
<>
), ); } } From 6dd718ae9a8f9035d4d5457b1b45b3c054f7d74e Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 11 Oct 2020 09:47:02 +0200 Subject: [PATCH 09/47] Add new HtmlIterable with syntax `for {...}` instead of `{for ...}` --- yew-macro/src/html_tree/html_iterable_new.rs | 52 ++++++++++++ yew-macro/src/html_tree/mod.rs | 8 ++ yew-macro/tests/macro/html-iterable-fail.rs | 18 +++++ .../tests/macro/html-iterable-fail.stderr | 79 ++++++++++++++++++- yew-macro/tests/macro/html-iterable-pass.rs | 23 +++++- 5 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 yew-macro/src/html_tree/html_iterable_new.rs diff --git a/yew-macro/src/html_tree/html_iterable_new.rs b/yew-macro/src/html_tree/html_iterable_new.rs new file mode 100644 index 00000000000..b2c2c049444 --- /dev/null +++ b/yew-macro/src/html_tree/html_iterable_new.rs @@ -0,0 +1,52 @@ +use crate::PeekValue; +use proc_macro2::{Delimiter, TokenStream}; +use quote::{quote_spanned, ToTokens}; +use syn::buffer::Cursor; +use syn::parse::{Parse, ParseStream, Result as ParseResult}; +use syn::spanned::Spanned; +use syn::{Block, Token}; + +pub struct HtmlIterableNew(Block); + +impl PeekValue<()> for HtmlIterableNew { + fn peek(cursor: Cursor) -> Option<()> { + let (_, cursor) = cursor.ident().filter(|(ident, _)| ident == "for")?; + let (_, _, _cursor) = cursor.group(Delimiter::Brace)?; + + Some(()) + } +} + +impl Parse for HtmlIterableNew { + fn parse(input: ParseStream) -> ParseResult { + let for_token = input.parse::()?; + + match input.parse() { + Ok(expr) => Ok(HtmlIterableNew(expr)), + Err(err) => { + if err.to_string().starts_with("unexpected end of input") { + Err(syn::Error::new_spanned( + for_token, + "expected a block after the keyword `for`", + )) + } else { + Err(err) + } + } + } + } +} + +impl ToTokens for HtmlIterableNew { + fn to_tokens(&self, tokens: &mut TokenStream) { + let expr = &self.0; + let new_tokens = quote_spanned! {expr.span()=> + #[allow(unused_braces)] + ::std::iter::Iterator::collect::<::yew::virtual_dom::VNode>( + ::std::iter::IntoIterator::into_iter(#expr), + ) + }; + + tokens.extend(new_tokens); + } +} diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index 4d84e5d3c23..72217ac21d6 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -3,6 +3,7 @@ mod html_component; mod html_dashed_name; mod html_if; mod html_iterable; +mod html_iterable_new; mod html_list; mod html_node; mod html_prop; @@ -14,6 +15,7 @@ use html_component::HtmlComponent; use html_dashed_name::HtmlDashedName; use html_if::HtmlIf; use html_iterable::HtmlIterable; +use html_iterable_new::HtmlIterableNew; use html_list::HtmlList; use html_node::HtmlNode; use html_prop::HtmlProp; @@ -31,6 +33,7 @@ pub enum HtmlType { List, Tag, If, + IterableNew, Empty, } @@ -40,6 +43,7 @@ pub enum HtmlTree { List(Box), Tag(Box), If(Box), + IterableNew(Box), Empty, } @@ -54,6 +58,7 @@ impl Parse for HtmlTree { HtmlType::Block => HtmlTree::Block(Box::new(input.parse()?)), HtmlType::List => HtmlTree::List(Box::new(input.parse()?)), HtmlType::If => HtmlTree::If(Box::new(input.parse()?)), + HtmlType::IterableNew => HtmlTree::IterableNew(Box::new(input.parse()?)), }; Ok(html_tree) } @@ -73,6 +78,8 @@ impl PeekValue for HtmlTree { Some(HtmlType::Block) } else if HtmlIf::peek(cursor).is_some() { Some(HtmlType::If) + } else if HtmlIterableNew::peek(cursor).is_some() { + Some(HtmlType::IterableNew) } else { None } @@ -90,6 +97,7 @@ impl ToTokens for HtmlTree { HtmlTree::List(list) => list.to_tokens(tokens), HtmlTree::Block(block) => block.to_tokens(tokens), HtmlTree::If(block) => block.to_tokens(tokens), + HtmlTree::IterableNew(block) => block.to_tokens(tokens), } } } diff --git a/yew-macro/tests/macro/html-iterable-fail.rs b/yew-macro/tests/macro/html-iterable-fail.rs index 97312a08ca6..df53937fe3f 100644 --- a/yew-macro/tests/macro/html-iterable-fail.rs +++ b/yew-macro/tests/macro/html-iterable-fail.rs @@ -18,6 +18,24 @@ fn compile_fail() { { for () } }; + + // new syntax + html! {
for {}
}; + html! {
for {()}
}; + html! {
for {Vec::<()>::new().into_iter()}
}; + + let empty = Vec::<()>::new().into_iter(); + html! {
for {empty}
}; + + let empty = Vec::<()>::new(); + html! {
for {empty.iter()}
}; + + html! { + <> +
+ { for {()} } + + }; } fn main() {} diff --git a/yew-macro/tests/macro/html-iterable-fail.stderr b/yew-macro/tests/macro/html-iterable-fail.stderr index b6abf51cdfe..ca5559700b5 100644 --- a/yew-macro/tests/macro/html-iterable-fail.stderr +++ b/yew-macro/tests/macro/html-iterable-fail.stderr @@ -71,9 +71,84 @@ error[E0277]: `()` is not an iterator 18 | { for () } | ^^ `()` is not an iterator | - ::: $WORKSPACE/yew/src/utils.rs:75:9 + ::: $WORKSPACE/yew/src/utils.rs | -75 | IT: IntoIterator, + | IT: IntoIterator, + | ---------------------- required by this bound in `yew::utils::into_node_iter` + | + = help: the trait `std::iter::Iterator` is not implemented for `()` + = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `()` + +error[E0277]: `()` is not an iterator + --> $DIR/html-iterable-fail.rs:23:22 + | +23 | html! {
for {}
}; + | ^^ `()` is not an iterator + | + = help: the trait `std::iter::Iterator` is not implemented for `()` + = note: required by `std::iter::IntoIterator::into_iter` + +error[E0277]: `()` is not an iterator + --> $DIR/html-iterable-fail.rs:24:22 + | +24 | html! {
for {()}
}; + | ^^^^ `()` is not an iterator + | + = help: the trait `std::iter::Iterator` is not implemented for `()` + = note: required by `std::iter::IntoIterator::into_iter` + +error[E0277]: `()` doesn't implement `std::fmt::Display` + --> $DIR/html-iterable-fail.rs:25:22 + | +25 | html! {
for {Vec::<()>::new().into_iter()}
}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: required because of the requirements on the impl of `std::string::ToString` for `()` + = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode` + = note: required because of the requirements on the impl of `std::convert::Into` for `()` + = note: required because of the requirements on the impl of `std::iter::FromIterator<()>` for `yew::virtual_dom::vnode::VNode` + = note: required by `std::iter::Iterator::collect` + +error[E0277]: `()` doesn't implement `std::fmt::Display` + --> $DIR/html-iterable-fail.rs:28:22 + | +28 | html! {
for {empty}
}; + | ^^^^^^^ `()` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: required because of the requirements on the impl of `std::string::ToString` for `()` + = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode` + = note: required because of the requirements on the impl of `std::convert::Into` for `()` + = note: required because of the requirements on the impl of `std::iter::FromIterator<()>` for `yew::virtual_dom::vnode::VNode` + = note: required by `std::iter::Iterator::collect` + +error[E0277]: `()` doesn't implement `std::fmt::Display` + --> $DIR/html-iterable-fail.rs:31:22 + | +31 | html! {
for {empty.iter()}
}; + | ^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter + | + = help: the trait `std::fmt::Display` is not implemented for `()` + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead + = note: required because of the requirements on the impl of `std::fmt::Display` for `&()` + = note: required because of the requirements on the impl of `std::string::ToString` for `&()` + = note: required because of the requirements on the impl of `std::convert::From<&()>` for `yew::virtual_dom::vnode::VNode` + = note: required because of the requirements on the impl of `std::convert::Into` for `&()` + = note: required because of the requirements on the impl of `std::iter::FromIterator<&()>` for `yew::virtual_dom::vnode::VNode` + = note: required by `std::iter::Iterator::collect` + +error[E0277]: `()` is not an iterator + --> $DIR/html-iterable-fail.rs:36:19 + | +36 | { for {()} } + | ^^^^ `()` is not an iterator + | + ::: $WORKSPACE/yew/src/utils.rs + | + | IT: IntoIterator, | ---------------------- required by this bound in `yew::utils::into_node_iter` | = help: the trait `std::iter::Iterator` is not implemented for `()` diff --git a/yew-macro/tests/macro/html-iterable-pass.rs b/yew-macro/tests/macro/html-iterable-pass.rs index 32afe47e4ca..32b7b04fa51 100644 --- a/yew-macro/tests/macro/html-iterable-pass.rs +++ b/yew-macro/tests/macro/html-iterable-pass.rs @@ -7,12 +7,31 @@ fn compile_pass() { html! { for Vec::::new().into_iter() }; html! { for (0..3).map(|num| { html! { {num} } }) }; html! { for {iter::empty::()} }; - let empty: Vec = Vec::new(); html! { for empty.into_iter() }; - let empty: Vec = Vec::new(); html! { for empty }; + + // test as child + html! {
{for iter::empty::()}
}; + html! {
{for Vec::::new()}
}; + html! {
{for Vec::::new().into_iter()}
}; + html! {
{for (0..3).map(|num| { html! { {num} } })}
}; + html! {
{for {iter::empty::()}}
}; + let empty: Vec = Vec::new(); + html! {
{for empty.into_iter()}
}; + let empty: Vec = Vec::new(); + html! {
{for empty}
}; + + // new syntax + html! {
for {iter::empty::()}
}; + html! {
for {Vec::::new()}
}; + html! {
for {Vec::::new().into_iter()}
}; + html! {
for {(0..3).map(|num| { html! { {num} } })}
}; + let empty: Vec = Vec::new(); + html! {
for {empty.into_iter()}
}; + let empty: Vec = Vec::new(); + html! {
for {empty}
}; } fn main() {} From 74efdac4532e779bddfb5247dde318b20065e6b9 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 11 Oct 2020 09:48:08 +0200 Subject: [PATCH 10/47] Remove HtmlIf from HtmlRoot (already done in HtmlTree) --- yew-macro/src/html_tree/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index 72217ac21d6..b1851bc3524 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -105,7 +105,6 @@ impl ToTokens for HtmlTree { pub enum HtmlRoot { Tree(HtmlTree), Iterable(Box), - If(Box), Node(Box), } @@ -115,8 +114,6 @@ impl Parse for HtmlRoot { Self::Tree(input.parse()?) } else if HtmlIterable::peek(input.cursor()).is_some() { Self::Iterable(Box::new(input.parse()?)) - } else if HtmlIf::peek(input.cursor()).is_some() { - Self::If(Box::new(input.parse()?)) } else { Self::Node(Box::new(input.parse()?)) }; @@ -139,7 +136,6 @@ impl ToTokens for HtmlRoot { Self::Tree(tree) => tree.to_tokens(tokens), Self::Node(node) => node.to_tokens(tokens), Self::Iterable(iterable) => iterable.to_tokens(tokens), - Self::If(r#if) => r#if.to_tokens(tokens), } } } From 88cadeb21c1f58f826cfbb7f3d58f667a41f4fe9 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 12:15:56 +0100 Subject: [PATCH 11/47] WIP --- yew-macro/src/html_tree/html_if.rs | 124 ++++++++++++++++++--- yew-macro/tests/html_macro/html-if-pass.rs | 14 ++- 2 files changed, 119 insertions(+), 19 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index 11dfc6d3542..12446042eb1 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -1,13 +1,23 @@ +#![allow(unused_imports)] +#![allow(unused_variables)] +#![allow(dead_code)] + use crate::PeekValue; use boolinator::Boolinator; -use proc_macro2::TokenStream; -use quote::{quote_spanned, ToTokens}; +use proc_macro2::{TokenStream, Delimiter}; +use quote::{quote_spanned, ToTokens, quote}; use syn::buffer::Cursor; use syn::parse::{Parse, ParseStream, Result as ParseResult}; use syn::spanned::Spanned; -use syn::{Expr, ExprIf}; +use syn::{Expr, ExprIf, Token, braced, token}; +use super::HtmlRoot; -pub struct HtmlIf(ExprIf); +pub struct HtmlIf { + if_token: Token![if], + cond: Box, + then_branch: HtmlBranch, + else_branch: Option<(Token![else], Box)>, +} impl PeekValue<()> for HtmlIf { fn peek(cursor: Cursor) -> Option<()> { @@ -18,22 +28,26 @@ impl PeekValue<()> for HtmlIf { impl Parse for HtmlIf { fn parse(input: ParseStream) -> ParseResult { - let expr = match input.parse() { - Ok(expr) => expr, - _ => { - return Err(syn::Error::new( - input.span(), - "expected a valid `if` expression", - )) - } - }; + let if_token = input.parse()?; + let cond = input.parse()?; + let then_branch = input.parse()?; + let else_branch = input.parse::() + .ok() + .map(|else_token| input.parse().map(|branch| (else_token, branch))) + .transpose()?; - Ok(HtmlIf(expr)) + Ok(HtmlIf { + if_token, + cond, + then_branch, + else_branch, + }) } } impl ToTokens for HtmlIf { fn to_tokens(&self, tokens: &mut TokenStream) { + /* let expr = &self.0; let cond = &expr.cond; let then_branch = &expr.then_branch; @@ -46,7 +60,89 @@ impl ToTokens for HtmlIf { let new_tokens = quote_spanned! {expr.span()=> if #cond #then_branch else #else_branch }; + */ + let HtmlIf { + if_token, + cond, + then_branch, + else_branch, + } = self; + let default_else_branch = syn::parse_str("{}").unwrap(); + let else_branch = else_branch + .as_ref() + .map(|(_, branch)| branch) + .unwrap_or(&default_else_branch); + let new_tokens = quote_spanned! {if_token.span()=> + if #cond #then_branch else #else_branch + }; tokens.extend(new_tokens); } } + +pub struct HtmlBranch { + brace: token::Brace, + root: HtmlRoot, +} + +impl PeekValue<()> for HtmlBranch { + fn peek(cursor: Cursor) -> Option<()> { + cursor.group(Delimiter::Brace).map(|_| ()) + } +} + +impl Parse for HtmlBranch { + fn parse(input: ParseStream) -> ParseResult { + let content; + let brace = braced!(content in input); + let root = content.parse()?; + + Ok(HtmlBranch { + brace, + root, + }) + } +} + +impl ToTokens for HtmlBranch { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + root, + .. + } = self; + + tokens.extend(quote! { + { #root } + }); + } +} + +pub enum HtmlBranchOrIf { + Branch(HtmlBranch), + If(HtmlIf), +} + +impl PeekValue<()> for HtmlBranchOrIf { + fn peek(cursor: Cursor) -> Option<()> { + HtmlBranch::peek(cursor).or_else(|| HtmlIf::peek(cursor)) + } +} + +impl Parse for HtmlBranchOrIf { + fn parse(input: ParseStream) -> ParseResult { + if HtmlBranch::peek(input.cursor()).is_some() { + Ok(Self::Branch(input.parse()?)) + } else { + Ok(Self::If(input.parse()?)) + } + } +} + +impl ToTokens for HtmlBranchOrIf { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Branch(x) => x.to_tokens(tokens), + Self::If(x) => x.to_tokens(tokens), + } + } +} diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index f5753827f73..3c8e639910f 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -1,17 +1,21 @@ use yew::prelude::*; fn compile_pass() { - let boolean = true; - html! { if boolean { html!() } }; - html! { if boolean { html!() } else { html!() } }; - html! {
if boolean { html!() }
}; - html! {
if boolean { html!() } else { html!() }
}; + html! { if true {
} }; + html! { if true { html!() } }; + html! { if true {} }; + html! { if true { let _x = 42; html!() } }; + html! { if true {
} else { html!() } }; + html! { if false {
} else if true { html!() } else { html!() } }; + html! { if false {
} else if false { html!() } else {} }; + /* let option = Some("text"); html! { if let Some(text) = option { html!( {text} ) } }; html! { if let Some(text) = option { html!( {text} ) } else { html!() } }; html! {
if let Some(text) = option { html!( {text} ) }
}; html! {
if let Some(text) = option { html!( {text} ) } else { html!() }
}; + */ } fn main() {} From 5b16b86305d9c9941e6f511b04c576fdfcdf0392 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 12:51:25 +0100 Subject: [PATCH 12/47] WIP --- yew-macro/src/html_tree/html_if.rs | 3 ++- yew-macro/tests/html_macro/html-if-pass.rs | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index 12446042eb1..dc28cfc48f6 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -9,8 +9,9 @@ use quote::{quote_spanned, ToTokens, quote}; use syn::buffer::Cursor; use syn::parse::{Parse, ParseStream, Result as ParseResult}; use syn::spanned::Spanned; -use syn::{Expr, ExprIf, Token, braced, token}; +use syn::{Expr, ExprIf, Token, braced, token, Block}; use super::HtmlRoot; +use super::HtmlTree; pub struct HtmlIf { if_token: Token![if], diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index 3c8e639910f..94f24555ee6 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -2,14 +2,12 @@ use yew::prelude::*; fn compile_pass() { html! { if true {
} }; - html! { if true { html!() } }; + html! { if true { { html!() } } }; html! { if true {} }; - html! { if true { let _x = 42; html!() } }; - html! { if true {
} else { html!() } }; - html! { if false {
} else if true { html!() } else { html!() } }; - html! { if false {
} else if false { html!() } else {} }; + html! { if true { { { let _x = 42; html!() } } } }; + html! { if true {} else {} }; + html! { if false {} else if true {} else {} }; /* - let option = Some("text"); html! { if let Some(text) = option { html!( {text} ) } }; html! { if let Some(text) = option { html!( {text} ) } else { html!() } }; From 05bf4ad06332b7ca5dc02ad6d839efc230104c79 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 13:13:36 +0100 Subject: [PATCH 13/47] WIP --- yew-macro/src/html_tree/html_if.rs | 27 ++++++++-------------- yew-macro/tests/html_macro/html-if-pass.rs | 1 + 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index dc28cfc48f6..90cd55b8877 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -12,6 +12,7 @@ use syn::spanned::Spanned; use syn::{Expr, ExprIf, Token, braced, token, Block}; use super::HtmlRoot; use super::HtmlTree; +use super::HtmlChildrenTree; pub struct HtmlIf { if_token: Token![if], @@ -48,20 +49,6 @@ impl Parse for HtmlIf { impl ToTokens for HtmlIf { fn to_tokens(&self, tokens: &mut TokenStream) { - /* - let expr = &self.0; - let cond = &expr.cond; - let then_branch = &expr.then_branch; - let default_else_branch = Box::new(syn::parse_str::("{html!()}").unwrap()); - let else_branch = &expr - .else_branch - .as_ref() - .map(|(_, expr)| expr) - .unwrap_or(&default_else_branch); - let new_tokens = quote_spanned! {expr.span()=> - if #cond #then_branch else #else_branch - }; - */ let HtmlIf { if_token, cond, @@ -83,7 +70,7 @@ impl ToTokens for HtmlIf { pub struct HtmlBranch { brace: token::Brace, - root: HtmlRoot, + root: HtmlChildrenTree, } impl PeekValue<()> for HtmlBranch { @@ -96,7 +83,8 @@ impl Parse for HtmlBranch { fn parse(input: ParseStream) -> ParseResult { let content; let brace = braced!(content in input); - let root = content.parse()?; + let mut root = HtmlChildrenTree::new(); + root.parse_child(&content)?; // TODO Ok(HtmlBranch { brace, @@ -111,9 +99,14 @@ impl ToTokens for HtmlBranch { root, .. } = self; + let key = quote! { None }; // TODO tokens.extend(quote! { - { #root } + { + ::yew::virtual_dom::VNode::VList( + ::yew::virtual_dom::VList::new_with_children(#root, #key) + ) + } }); } } diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index 94f24555ee6..b2b159ebc4a 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -7,6 +7,7 @@ fn compile_pass() { html! { if true { { { let _x = 42; html!() } } } }; html! { if true {} else {} }; html! { if false {} else if true {} else {} }; + html! { if true { <>
} }; /* let option = Some("text"); html! { if let Some(text) = option { html!( {text} ) } }; From 7da3645b4fd519a3ba1889dd01b8cdebb9b0f0ae Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 13:28:47 +0100 Subject: [PATCH 14/47] WIP --- yew-macro/src/html_tree/html_if.rs | 59 +++++++++++++--------- yew-macro/src/html_tree/mod.rs | 2 +- yew-macro/tests/html_macro/html-if-pass.rs | 4 +- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index 90cd55b8877..914d7926814 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -13,6 +13,7 @@ use syn::{Expr, ExprIf, Token, braced, token, Block}; use super::HtmlRoot; use super::HtmlTree; use super::HtmlChildrenTree; +use super::HtmlElement; pub struct HtmlIf { if_token: Token![if], @@ -68,9 +69,12 @@ impl ToTokens for HtmlIf { } } -pub struct HtmlBranch { - brace: token::Brace, - root: HtmlChildrenTree, +pub enum HtmlBranch { + Children { + brace: token::Brace, + root: HtmlChildrenTree, + }, + Block(Block), } impl PeekValue<()> for HtmlBranch { @@ -81,33 +85,38 @@ impl PeekValue<()> for HtmlBranch { impl Parse for HtmlBranch { fn parse(input: ParseStream) -> ParseResult { - let content; - let brace = braced!(content in input); - let mut root = HtmlChildrenTree::new(); - root.parse_child(&content)?; // TODO - - Ok(HtmlBranch { - brace, - root, - }) + if HtmlElement::peek(input.cursor()).is_some() || input.cursor().eof() { + let content; + let brace = braced!(content in input); + let mut root = HtmlChildrenTree::new(); + root.parse_child(&content)?; // TODO + + Ok(Self::Children { + brace, + root, + }) + } else { + Ok(Self::Block(input.parse()?)) + } } } impl ToTokens for HtmlBranch { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - root, - .. - } = self; - let key = quote! { None }; // TODO - - tokens.extend(quote! { - { - ::yew::virtual_dom::VNode::VList( - ::yew::virtual_dom::VList::new_with_children(#root, #key) - ) - } - }); + match self { + Self::Children { root, .. } => { + let key = quote! { None }; // TODO + + tokens.extend(quote! { + { + ::yew::virtual_dom::VNode::VList( + ::yew::virtual_dom::VList::new_with_children(#root, #key) + ) + } + }); + }, + Self::Block(block) => tokens.extend(quote! { #block }), + } } } diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index 5e3e8f15d0b..6bced859989 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -175,7 +175,7 @@ impl ToNodeIterator for HtmlTree { } } -struct HtmlChildrenTree(Vec); +pub struct HtmlChildrenTree(Vec); impl HtmlChildrenTree { pub fn new() -> Self { diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index b2b159ebc4a..25cd86ea9ff 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -4,10 +4,10 @@ fn compile_pass() { html! { if true {
} }; html! { if true { { html!() } } }; html! { if true {} }; - html! { if true { { { let _x = 42; html!() } } } }; + html! { if true { let _x = 42; html!() } }; html! { if true {} else {} }; html! { if false {} else if true {} else {} }; - html! { if true { <>
} }; + html! { if true {
} }; /* let option = Some("text"); html! { if let Some(text) = option { html!( {text} ) } }; From 5d3db9e687958b02c0f21d2e3382c8062a33d5ea Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 13:30:36 +0100 Subject: [PATCH 15/47] Revert --- yew-macro/src/html_tree/html_if.rs | 68 +++++++++++----------- yew-macro/tests/html_macro/html-if-pass.rs | 4 +- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index 914d7926814..dc28cfc48f6 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -12,8 +12,6 @@ use syn::spanned::Spanned; use syn::{Expr, ExprIf, Token, braced, token, Block}; use super::HtmlRoot; use super::HtmlTree; -use super::HtmlChildrenTree; -use super::HtmlElement; pub struct HtmlIf { if_token: Token![if], @@ -50,6 +48,20 @@ impl Parse for HtmlIf { impl ToTokens for HtmlIf { fn to_tokens(&self, tokens: &mut TokenStream) { + /* + let expr = &self.0; + let cond = &expr.cond; + let then_branch = &expr.then_branch; + let default_else_branch = Box::new(syn::parse_str::("{html!()}").unwrap()); + let else_branch = &expr + .else_branch + .as_ref() + .map(|(_, expr)| expr) + .unwrap_or(&default_else_branch); + let new_tokens = quote_spanned! {expr.span()=> + if #cond #then_branch else #else_branch + }; + */ let HtmlIf { if_token, cond, @@ -69,12 +81,9 @@ impl ToTokens for HtmlIf { } } -pub enum HtmlBranch { - Children { - brace: token::Brace, - root: HtmlChildrenTree, - }, - Block(Block), +pub struct HtmlBranch { + brace: token::Brace, + root: HtmlRoot, } impl PeekValue<()> for HtmlBranch { @@ -85,38 +94,27 @@ impl PeekValue<()> for HtmlBranch { impl Parse for HtmlBranch { fn parse(input: ParseStream) -> ParseResult { - if HtmlElement::peek(input.cursor()).is_some() || input.cursor().eof() { - let content; - let brace = braced!(content in input); - let mut root = HtmlChildrenTree::new(); - root.parse_child(&content)?; // TODO - - Ok(Self::Children { - brace, - root, - }) - } else { - Ok(Self::Block(input.parse()?)) - } + let content; + let brace = braced!(content in input); + let root = content.parse()?; + + Ok(HtmlBranch { + brace, + root, + }) } } impl ToTokens for HtmlBranch { fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Children { root, .. } => { - let key = quote! { None }; // TODO - - tokens.extend(quote! { - { - ::yew::virtual_dom::VNode::VList( - ::yew::virtual_dom::VList::new_with_children(#root, #key) - ) - } - }); - }, - Self::Block(block) => tokens.extend(quote! { #block }), - } + let Self { + root, + .. + } = self; + + tokens.extend(quote! { + { #root } + }); } } diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index 25cd86ea9ff..b2b159ebc4a 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -4,10 +4,10 @@ fn compile_pass() { html! { if true {
} }; html! { if true { { html!() } } }; html! { if true {} }; - html! { if true { let _x = 42; html!() } }; + html! { if true { { { let _x = 42; html!() } } } }; html! { if true {} else {} }; html! { if false {} else if true {} else {} }; - html! { if true {
} }; + html! { if true { <>
} }; /* let option = Some("text"); html! { if let Some(text) = option { html!( {text} ) } }; From e480e13f30d5ceac4e1e629a8059daefb9ca2d09 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 13:55:19 +0100 Subject: [PATCH 16/47] CLEANUP --- yew-macro/src/html_tree/html_if.rs | 43 ++++++---------------- yew-macro/tests/html_macro/html-if-pass.rs | 12 ++---- 2 files changed, 14 insertions(+), 41 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index dc28cfc48f6..a909afb84b7 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -1,17 +1,12 @@ -#![allow(unused_imports)] -#![allow(unused_variables)] -#![allow(dead_code)] - +use super::HtmlRoot; use crate::PeekValue; use boolinator::Boolinator; -use proc_macro2::{TokenStream, Delimiter}; -use quote::{quote_spanned, ToTokens, quote}; +use proc_macro2::{Delimiter, TokenStream}; +use quote::{quote, quote_spanned, ToTokens}; use syn::buffer::Cursor; use syn::parse::{Parse, ParseStream, Result as ParseResult}; use syn::spanned::Spanned; -use syn::{Expr, ExprIf, Token, braced, token, Block}; -use super::HtmlRoot; -use super::HtmlTree; +use syn::{braced, token, Expr, Token}; pub struct HtmlIf { if_token: Token![if], @@ -32,7 +27,8 @@ impl Parse for HtmlIf { let if_token = input.parse()?; let cond = input.parse()?; let then_branch = input.parse()?; - let else_branch = input.parse::() + let else_branch = input + .parse::() .ok() .map(|else_token| input.parse().map(|branch| (else_token, branch))) .transpose()?; @@ -48,20 +44,6 @@ impl Parse for HtmlIf { impl ToTokens for HtmlIf { fn to_tokens(&self, tokens: &mut TokenStream) { - /* - let expr = &self.0; - let cond = &expr.cond; - let then_branch = &expr.then_branch; - let default_else_branch = Box::new(syn::parse_str::("{html!()}").unwrap()); - let else_branch = &expr - .else_branch - .as_ref() - .map(|(_, expr)| expr) - .unwrap_or(&default_else_branch); - let new_tokens = quote_spanned! {expr.span()=> - if #cond #then_branch else #else_branch - }; - */ let HtmlIf { if_token, cond, @@ -82,7 +64,7 @@ impl ToTokens for HtmlIf { } pub struct HtmlBranch { - brace: token::Brace, + _brace: token::Brace, root: HtmlRoot, } @@ -99,7 +81,7 @@ impl Parse for HtmlBranch { let root = content.parse()?; Ok(HtmlBranch { - brace, + _brace: brace, root, }) } @@ -107,10 +89,7 @@ impl Parse for HtmlBranch { impl ToTokens for HtmlBranch { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - root, - .. - } = self; + let Self { root, .. } = self; tokens.extend(quote! { { #root } @@ -132,9 +111,9 @@ impl PeekValue<()> for HtmlBranchOrIf { impl Parse for HtmlBranchOrIf { fn parse(input: ParseStream) -> ParseResult { if HtmlBranch::peek(input.cursor()).is_some() { - Ok(Self::Branch(input.parse()?)) + input.parse().map(Self::Branch) } else { - Ok(Self::If(input.parse()?)) + input.parse().map(Self::If) } } } diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index b2b159ebc4a..c6605a418e6 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -1,20 +1,14 @@ use yew::prelude::*; fn compile_pass() { + html! { if true {} }; html! { if true {
} }; + html! { if true { <>
} }; html! { if true { { html!() } } }; - html! { if true {} }; html! { if true { { { let _x = 42; html!() } } } }; html! { if true {} else {} }; html! { if false {} else if true {} else {} }; - html! { if true { <>
} }; - /* - let option = Some("text"); - html! { if let Some(text) = option { html!( {text} ) } }; - html! { if let Some(text) = option { html!( {text} ) } else { html!() } }; - html! {
if let Some(text) = option { html!( {text} ) }
}; - html! {
if let Some(text) = option { html!( {text} ) } else { html!() }
}; - */ + html! { if let Some(text) = Some("text") { {text} } }; } fn main() {} From d771a7a420a78192611f641d0107240f1a7fa9cb Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 14:07:02 +0100 Subject: [PATCH 17/47] WIP --- yew-macro/tests/html_macro/html-if-fail.rs | 20 ++---- .../tests/html_macro/html-if-fail.stderr | 69 +++++++------------ yew-macro/tests/html_macro/html-if-pass.rs | 5 +- 3 files changed, 32 insertions(+), 62 deletions(-) diff --git a/yew-macro/tests/html_macro/html-if-fail.rs b/yew-macro/tests/html_macro/html-if-fail.rs index 760328371c0..bcb59b94ed3 100644 --- a/yew-macro/tests/html_macro/html-if-fail.rs +++ b/yew-macro/tests/html_macro/html-if-fail.rs @@ -1,21 +1,11 @@ use yew::prelude::*; fn compile_fail() { - html! { if {html!()} }; - let value = 42; - html! { if value { html!() } }; - html! { if {value} { html!() } }; - let boolean = true; - html! { if boolean }; - html! { if boolean { () } }; - - html! { - <> -
- if value { - } - - }; + html! { if {} }; + html! { if 42 {} }; + html! { if true {} else }; + html! { if true {} else if {} }; + html! { if true {} else if true {} else }; } fn main() {} diff --git a/yew-macro/tests/html_macro/html-if-fail.stderr b/yew-macro/tests/html_macro/html-if-fail.stderr index a58c1fe293d..da5c12c52a5 100644 --- a/yew-macro/tests/html_macro/html-if-fail.stderr +++ b/yew-macro/tests/html_macro/html-if-fail.stderr @@ -1,60 +1,37 @@ -error: expected a valid `if` expression +error: unexpected end of input, expected curly braces --> $DIR/html-if-fail.rs:4:5 | -4 | html! { if {html!()} }; - | ^^^^^^^^^^^^^^^^^^^^^^^ +4 | html! { if {} }; + | ^^^^^^^^^^^^^^^^ | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected a valid `if` expression - --> $DIR/html-if-fail.rs:9:5 +error: unexpected end of input, expected `if` + --> $DIR/html-if-fail.rs:6:5 | -9 | html! { if boolean }; - | ^^^^^^^^^^^^^^^^^^^^^ +6 | html! { if true {} else }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0308]: mismatched types - --> $DIR/html-if-fail.rs:6:16 +error: unexpected end of input, expected curly braces + --> $DIR/html-if-fail.rs:7:5 | -6 | html! { if value { html!() } }; - | ^^^^^ expected `bool`, found integer - -error[E0308]: mismatched types - --> $DIR/html-if-fail.rs:7:17 +7 | html! { if true {} else if {} }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -7 | html! { if {value} { html!() } }; - | ^^^^^ expected `bool`, found integer + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0308]: `if` and `else` have incompatible types - --> $DIR/html-if-fail.rs:10:5 - | -10 | html! { if boolean { () } }; - | ^^^^^^^^^^^^^^^^^^^^^--^^^^^ - | | | - | | expected because of this - | expected `()`, found enum `yew::virtual_dom::vnode::VNode` - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +error: unexpected end of input, expected `if` + --> $DIR/html-if-fail.rs:8:5 + | +8 | html! { if true {} else if true {} else }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types - --> $DIR/html-if-fail.rs:15:16 - | -15 | if value { - | ^^^^^ expected `bool`, found integer - -error[E0308]: `if` and `else` have incompatible types - --> $DIR/html-if-fail.rs:12:5 - | -12 | / html! { -13 | | <> -14 | |
-15 | | if value { - | |______________________- -16 | || } - | ||_____________- expected because of this -17 | | -18 | | }; - | |______^ expected `()`, found enum `yew::virtual_dom::vnode::VNode` - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + --> $DIR/html-if-fail.rs:5:16 + | +5 | html! { if 42 {} }; + | ^^ expected `bool`, found integer diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index c6605a418e6..b907c243914 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -7,8 +7,11 @@ fn compile_pass() { html! { if true { { html!() } } }; html! { if true { { { let _x = 42; html!() } } } }; html! { if true {} else {} }; - html! { if false {} else if true {} else {} }; + html! { if true {} else if true {} }; + html! { if true {} else if true {} else {} }; html! { if let Some(text) = Some("text") { {text} } }; + html! { <>
if true {}
}; + html! {
if true {}
}; } fn main() {} From 29c304e5df6637a27ca157accecf2a64bdb5acf8 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 14:12:14 +0100 Subject: [PATCH 18/47] CLEANUP --- yew/src/virtual_dom/vtag.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/yew/src/virtual_dom/vtag.rs b/yew/src/virtual_dom/vtag.rs index ba5da919fbe..06ad32d9805 100644 --- a/yew/src/virtual_dom/vtag.rs +++ b/yew/src/virtual_dom/vtag.rs @@ -1098,7 +1098,7 @@ mod tests { assert_eq!( html! { if true { - html!(
) +
} }, html!(
), @@ -1106,9 +1106,9 @@ mod tests { assert_eq!( html! { if false { - html!(
) +
} else { - html!(
) +
} }, html! { @@ -1118,7 +1118,7 @@ mod tests { assert_eq!( html! { if false { - html!(
) +
} }, html!(), @@ -1129,7 +1129,7 @@ mod tests { html! {
if true { - html!(
) +
}
}, @@ -1143,9 +1143,9 @@ mod tests { html! {
if false { - html!(
) +
} else { - html!(
) +
}
}, @@ -1159,7 +1159,7 @@ mod tests { html! {
if false { - html!(
) +
}
}, @@ -1178,7 +1178,7 @@ mod tests { assert_eq!( html! { if let Some(class) = option_foo { - html!(
) +
} }, html!(
), @@ -1186,9 +1186,9 @@ mod tests { assert_eq!( html! { if let Some(class) = none { - html!(
) +
} else { - html!(
) +
} }, html!(
), @@ -1196,7 +1196,7 @@ mod tests { assert_eq!( html! { if let Some(class) = none { - html!(
) +
} }, html!(), @@ -1207,7 +1207,7 @@ mod tests { html! {
if let Some(class) = option_foo { - html!(
) +
}
}, @@ -1217,9 +1217,9 @@ mod tests { html! {
if let Some(class) = none { - html!(
) +
} else { - html!(
) +
}
}, @@ -1229,7 +1229,7 @@ mod tests { html! {
if let Some(class) = none { - html!(
) +
}
}, From c306153dd80b7d22a2fbaa128f6088ba68cfd401 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 14:17:26 +0100 Subject: [PATCH 19/47] Remove IterableNew --- yew-macro/src/html_tree/html_iterable_new.rs | 52 ------------ yew-macro/src/html_tree/mod.rs | 10 +-- .../tests/html_macro/html-iterable-fail.rs | 18 ----- .../html_macro/html-iterable-fail.stderr | 79 +------------------ .../tests/html_macro/html-iterable-pass.rs | 23 +----- 5 files changed, 5 insertions(+), 177 deletions(-) delete mode 100644 yew-macro/src/html_tree/html_iterable_new.rs diff --git a/yew-macro/src/html_tree/html_iterable_new.rs b/yew-macro/src/html_tree/html_iterable_new.rs deleted file mode 100644 index b2c2c049444..00000000000 --- a/yew-macro/src/html_tree/html_iterable_new.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::PeekValue; -use proc_macro2::{Delimiter, TokenStream}; -use quote::{quote_spanned, ToTokens}; -use syn::buffer::Cursor; -use syn::parse::{Parse, ParseStream, Result as ParseResult}; -use syn::spanned::Spanned; -use syn::{Block, Token}; - -pub struct HtmlIterableNew(Block); - -impl PeekValue<()> for HtmlIterableNew { - fn peek(cursor: Cursor) -> Option<()> { - let (_, cursor) = cursor.ident().filter(|(ident, _)| ident == "for")?; - let (_, _, _cursor) = cursor.group(Delimiter::Brace)?; - - Some(()) - } -} - -impl Parse for HtmlIterableNew { - fn parse(input: ParseStream) -> ParseResult { - let for_token = input.parse::()?; - - match input.parse() { - Ok(expr) => Ok(HtmlIterableNew(expr)), - Err(err) => { - if err.to_string().starts_with("unexpected end of input") { - Err(syn::Error::new_spanned( - for_token, - "expected a block after the keyword `for`", - )) - } else { - Err(err) - } - } - } - } -} - -impl ToTokens for HtmlIterableNew { - fn to_tokens(&self, tokens: &mut TokenStream) { - let expr = &self.0; - let new_tokens = quote_spanned! {expr.span()=> - #[allow(unused_braces)] - ::std::iter::Iterator::collect::<::yew::virtual_dom::VNode>( - ::std::iter::IntoIterator::into_iter(#expr), - ) - }; - - tokens.extend(new_tokens); - } -} diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index 6bced859989..a479bcac555 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -11,7 +11,6 @@ mod html_dashed_name; mod html_element; mod html_if; mod html_iterable; -mod html_iterable_new; mod html_list; mod html_node; mod tag; @@ -22,7 +21,6 @@ pub use html_dashed_name::HtmlDashedName; use html_element::HtmlElement; use html_if::HtmlIf; use html_iterable::HtmlIterable; -use html_iterable_new::HtmlIterableNew; use html_list::HtmlList; use html_node::HtmlNode; use tag::TagTokens; @@ -33,7 +31,6 @@ pub enum HtmlType { List, Element, If, - IterableNew, Empty, } @@ -43,7 +40,6 @@ pub enum HtmlTree { List(Box), Element(Box), If(Box), - IterableNew(Box), Empty, } @@ -58,7 +54,6 @@ impl Parse for HtmlTree { HtmlType::Block => HtmlTree::Block(Box::new(input.parse()?)), HtmlType::List => HtmlTree::List(Box::new(input.parse()?)), HtmlType::If => HtmlTree::If(Box::new(input.parse()?)), - HtmlType::IterableNew => HtmlTree::IterableNew(Box::new(input.parse()?)), }; Ok(html_tree) } @@ -78,8 +73,6 @@ impl PeekValue for HtmlTree { Some(HtmlType::Block) } else if HtmlIf::peek(cursor).is_some() { Some(HtmlType::If) - } else if HtmlIterableNew::peek(cursor).is_some() { - Some(HtmlType::IterableNew) } else { None } @@ -97,7 +90,6 @@ impl ToTokens for HtmlTree { HtmlTree::List(list) => list.to_tokens(tokens), HtmlTree::Block(block) => block.to_tokens(tokens), HtmlTree::If(block) => block.to_tokens(tokens), - HtmlTree::IterableNew(block) => block.to_tokens(tokens), } } } @@ -175,7 +167,7 @@ impl ToNodeIterator for HtmlTree { } } -pub struct HtmlChildrenTree(Vec); +struct HtmlChildrenTree(Vec); impl HtmlChildrenTree { pub fn new() -> Self { diff --git a/yew-macro/tests/html_macro/html-iterable-fail.rs b/yew-macro/tests/html_macro/html-iterable-fail.rs index df53937fe3f..97312a08ca6 100644 --- a/yew-macro/tests/html_macro/html-iterable-fail.rs +++ b/yew-macro/tests/html_macro/html-iterable-fail.rs @@ -18,24 +18,6 @@ fn compile_fail() { { for () } }; - - // new syntax - html! {
for {}
}; - html! {
for {()}
}; - html! {
for {Vec::<()>::new().into_iter()}
}; - - let empty = Vec::<()>::new().into_iter(); - html! {
for {empty}
}; - - let empty = Vec::<()>::new(); - html! {
for {empty.iter()}
}; - - html! { - <> -
- { for {()} } - - }; } fn main() {} diff --git a/yew-macro/tests/html_macro/html-iterable-fail.stderr b/yew-macro/tests/html_macro/html-iterable-fail.stderr index ca5559700b5..b6abf51cdfe 100644 --- a/yew-macro/tests/html_macro/html-iterable-fail.stderr +++ b/yew-macro/tests/html_macro/html-iterable-fail.stderr @@ -71,84 +71,9 @@ error[E0277]: `()` is not an iterator 18 | { for () } | ^^ `()` is not an iterator | - ::: $WORKSPACE/yew/src/utils.rs + ::: $WORKSPACE/yew/src/utils.rs:75:9 | - | IT: IntoIterator, - | ---------------------- required by this bound in `yew::utils::into_node_iter` - | - = help: the trait `std::iter::Iterator` is not implemented for `()` - = note: required because of the requirements on the impl of `std::iter::IntoIterator` for `()` - -error[E0277]: `()` is not an iterator - --> $DIR/html-iterable-fail.rs:23:22 - | -23 | html! {
for {}
}; - | ^^ `()` is not an iterator - | - = help: the trait `std::iter::Iterator` is not implemented for `()` - = note: required by `std::iter::IntoIterator::into_iter` - -error[E0277]: `()` is not an iterator - --> $DIR/html-iterable-fail.rs:24:22 - | -24 | html! {
for {()}
}; - | ^^^^ `()` is not an iterator - | - = help: the trait `std::iter::Iterator` is not implemented for `()` - = note: required by `std::iter::IntoIterator::into_iter` - -error[E0277]: `()` doesn't implement `std::fmt::Display` - --> $DIR/html-iterable-fail.rs:25:22 - | -25 | html! {
for {Vec::<()>::new().into_iter()}
}; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter - | - = help: the trait `std::fmt::Display` is not implemented for `()` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: required because of the requirements on the impl of `std::string::ToString` for `()` - = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode` - = note: required because of the requirements on the impl of `std::convert::Into` for `()` - = note: required because of the requirements on the impl of `std::iter::FromIterator<()>` for `yew::virtual_dom::vnode::VNode` - = note: required by `std::iter::Iterator::collect` - -error[E0277]: `()` doesn't implement `std::fmt::Display` - --> $DIR/html-iterable-fail.rs:28:22 - | -28 | html! {
for {empty}
}; - | ^^^^^^^ `()` cannot be formatted with the default formatter - | - = help: the trait `std::fmt::Display` is not implemented for `()` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: required because of the requirements on the impl of `std::string::ToString` for `()` - = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode` - = note: required because of the requirements on the impl of `std::convert::Into` for `()` - = note: required because of the requirements on the impl of `std::iter::FromIterator<()>` for `yew::virtual_dom::vnode::VNode` - = note: required by `std::iter::Iterator::collect` - -error[E0277]: `()` doesn't implement `std::fmt::Display` - --> $DIR/html-iterable-fail.rs:31:22 - | -31 | html! {
for {empty.iter()}
}; - | ^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter - | - = help: the trait `std::fmt::Display` is not implemented for `()` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: required because of the requirements on the impl of `std::fmt::Display` for `&()` - = note: required because of the requirements on the impl of `std::string::ToString` for `&()` - = note: required because of the requirements on the impl of `std::convert::From<&()>` for `yew::virtual_dom::vnode::VNode` - = note: required because of the requirements on the impl of `std::convert::Into` for `&()` - = note: required because of the requirements on the impl of `std::iter::FromIterator<&()>` for `yew::virtual_dom::vnode::VNode` - = note: required by `std::iter::Iterator::collect` - -error[E0277]: `()` is not an iterator - --> $DIR/html-iterable-fail.rs:36:19 - | -36 | { for {()} } - | ^^^^ `()` is not an iterator - | - ::: $WORKSPACE/yew/src/utils.rs - | - | IT: IntoIterator, +75 | IT: IntoIterator, | ---------------------- required by this bound in `yew::utils::into_node_iter` | = help: the trait `std::iter::Iterator` is not implemented for `()` diff --git a/yew-macro/tests/html_macro/html-iterable-pass.rs b/yew-macro/tests/html_macro/html-iterable-pass.rs index 32b7b04fa51..32afe47e4ca 100644 --- a/yew-macro/tests/html_macro/html-iterable-pass.rs +++ b/yew-macro/tests/html_macro/html-iterable-pass.rs @@ -7,31 +7,12 @@ fn compile_pass() { html! { for Vec::::new().into_iter() }; html! { for (0..3).map(|num| { html! { {num} } }) }; html! { for {iter::empty::()} }; - let empty: Vec = Vec::new(); - html! { for empty.into_iter() }; - let empty: Vec = Vec::new(); - html! { for empty }; - // test as child - html! {
{for iter::empty::()}
}; - html! {
{for Vec::::new()}
}; - html! {
{for Vec::::new().into_iter()}
}; - html! {
{for (0..3).map(|num| { html! { {num} } })}
}; - html! {
{for {iter::empty::()}}
}; let empty: Vec = Vec::new(); - html! {
{for empty.into_iter()}
}; - let empty: Vec = Vec::new(); - html! {
{for empty}
}; + html! { for empty.into_iter() }; - // new syntax - html! {
for {iter::empty::()}
}; - html! {
for {Vec::::new()}
}; - html! {
for {Vec::::new().into_iter()}
}; - html! {
for {(0..3).map(|num| { html! { {num} } })}
}; let empty: Vec = Vec::new(); - html! {
for {empty.into_iter()}
}; - let empty: Vec = Vec::new(); - html! {
for {empty}
}; + html! { for empty }; } fn main() {} From 9ffed6883e31515a2d20900a81388e6f72c945bf Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 29 Nov 2020 19:34:26 +0100 Subject: [PATCH 20/47] Renamed HtmlBranch to HtmlRootBraced and moved to mod.rs --- yew-macro/src/html_tree/html_if.rs | 60 +++++++----------------------- yew-macro/src/html_tree/mod.rs | 44 +++++++++++++++++++--- 2 files changed, 51 insertions(+), 53 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index a909afb84b7..d6f15715b50 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -1,18 +1,18 @@ -use super::HtmlRoot; +use super::HtmlRootBraced; use crate::PeekValue; use boolinator::Boolinator; -use proc_macro2::{Delimiter, TokenStream}; -use quote::{quote, quote_spanned, ToTokens}; +use proc_macro2::TokenStream; +use quote::{quote_spanned, ToTokens}; use syn::buffer::Cursor; use syn::parse::{Parse, ParseStream, Result as ParseResult}; use syn::spanned::Spanned; -use syn::{braced, token, Expr, Token}; +use syn::{Expr, Token}; pub struct HtmlIf { if_token: Token![if], cond: Box, - then_branch: HtmlBranch, - else_branch: Option<(Token![else], Box)>, + then_branch: HtmlRootBraced, + else_branch: Option<(Token![else], Box)>, } impl PeekValue<()> for HtmlIf { @@ -63,54 +63,20 @@ impl ToTokens for HtmlIf { } } -pub struct HtmlBranch { - _brace: token::Brace, - root: HtmlRoot, -} - -impl PeekValue<()> for HtmlBranch { - fn peek(cursor: Cursor) -> Option<()> { - cursor.group(Delimiter::Brace).map(|_| ()) - } -} - -impl Parse for HtmlBranch { - fn parse(input: ParseStream) -> ParseResult { - let content; - let brace = braced!(content in input); - let root = content.parse()?; - - Ok(HtmlBranch { - _brace: brace, - root, - }) - } -} - -impl ToTokens for HtmlBranch { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { root, .. } = self; - - tokens.extend(quote! { - { #root } - }); - } -} - -pub enum HtmlBranchOrIf { - Branch(HtmlBranch), +pub enum HtmlRootBracedOrIf { + Branch(HtmlRootBraced), If(HtmlIf), } -impl PeekValue<()> for HtmlBranchOrIf { +impl PeekValue<()> for HtmlRootBracedOrIf { fn peek(cursor: Cursor) -> Option<()> { - HtmlBranch::peek(cursor).or_else(|| HtmlIf::peek(cursor)) + HtmlRootBraced::peek(cursor).or_else(|| HtmlIf::peek(cursor)) } } -impl Parse for HtmlBranchOrIf { +impl Parse for HtmlRootBracedOrIf { fn parse(input: ParseStream) -> ParseResult { - if HtmlBranch::peek(input.cursor()).is_some() { + if HtmlRootBraced::peek(input.cursor()).is_some() { input.parse().map(Self::Branch) } else { input.parse().map(Self::If) @@ -118,7 +84,7 @@ impl Parse for HtmlBranchOrIf { } } -impl ToTokens for HtmlBranchOrIf { +impl ToTokens for HtmlRootBracedOrIf { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Branch(x) => x.to_tokens(tokens), diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index a479bcac555..48a8c78bdc1 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -1,9 +1,10 @@ use crate::PeekValue; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Delimiter, Ident, Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use syn::buffer::Cursor; -use syn::parse::{Parse, ParseStream, Result}; +use syn::parse::{Parse, ParseStream, Result as ParseResult}; use syn::spanned::Spanned; +use syn::{braced, token}; mod html_block; mod html_component; @@ -44,7 +45,7 @@ pub enum HtmlTree { } impl Parse for HtmlTree { - fn parse(input: ParseStream) -> Result { + fn parse(input: ParseStream) -> ParseResult { let html_type = HtmlTree::peek(input.cursor()) .ok_or_else(|| input.error("expected a valid html element"))?; let html_tree = match html_type { @@ -101,7 +102,7 @@ pub enum HtmlRoot { } impl Parse for HtmlRoot { - fn parse(input: ParseStream) -> Result { + fn parse(input: ParseStream) -> ParseResult { let html_root = if HtmlTree::peek(input.cursor()).is_some() { Self::Tree(input.parse()?) } else if HtmlIterable::peek(input.cursor()).is_some() { @@ -135,7 +136,7 @@ impl ToTokens for HtmlRoot { /// Same as HtmlRoot but always returns a VNode. pub struct HtmlRootVNode(HtmlRoot); impl Parse for HtmlRootVNode { - fn parse(input: ParseStream) -> Result { + fn parse(input: ParseStream) -> ParseResult { input.parse().map(Self) } } @@ -174,7 +175,7 @@ impl HtmlChildrenTree { Self(Vec::new()) } - pub fn parse_child(&mut self, input: ParseStream) -> Result<()> { + pub fn parse_child(&mut self, input: ParseStream) -> ParseResult<()> { self.0.push(input.parse()?); Ok(()) } @@ -233,3 +234,34 @@ impl ToTokens for HtmlChildrenTree { tokens.extend(self.to_build_vec_token_stream()); } } + +pub struct HtmlRootBraced { + brace: token::Brace, + root: HtmlRoot, +} + +impl PeekValue<()> for HtmlRootBraced { + fn peek(cursor: Cursor) -> Option<()> { + cursor.group(Delimiter::Brace).map(|_| ()) + } +} + +impl Parse for HtmlRootBraced { + fn parse(input: ParseStream) -> ParseResult { + let content; + let brace = braced!(content in input); + let root = content.parse()?; + + Ok(HtmlRootBraced { brace, root }) + } +} + +impl ToTokens for HtmlRootBraced { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { brace, root } = self; + + tokens.extend(quote_spanned! {brace.span=> + { #root } + }); + } +} From 6bfb7af6a444b3172bb41b28be644a0a6ca5e43b Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Fri, 4 Dec 2020 17:05:29 +0100 Subject: [PATCH 21/47] Update yew-macro/tests/html_macro/html-if-pass.rs Co-authored-by: Simon --- yew-macro/tests/html_macro/html-if-pass.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index b907c243914..697d6b75b67 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -9,7 +9,7 @@ fn compile_pass() { html! { if true {} else {} }; html! { if true {} else if true {} }; html! { if true {} else if true {} else {} }; - html! { if let Some(text) = Some("text") { {text} } }; + html! { if let Some(text) = Some("text") { { text } } }; html! { <>
if true {}
}; html! {
if true {}
}; } From 3a84c2b339a7d24024544419ab12b382be8f054f Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Fri, 4 Dec 2020 17:07:28 +0100 Subject: [PATCH 22/47] Suggestion --- yew/src/virtual_dom/vtag.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yew/src/virtual_dom/vtag.rs b/yew/src/virtual_dom/vtag.rs index 06ad32d9805..d7fcb9ce464 100644 --- a/yew/src/virtual_dom/vtag.rs +++ b/yew/src/virtual_dom/vtag.rs @@ -1181,7 +1181,7 @@ mod tests {
} }, - html!(
), + html! {
}, ); assert_eq!( html! { @@ -1199,7 +1199,7 @@ mod tests {
} }, - html!(), + html! {}, ); // non-root tests @@ -1211,7 +1211,7 @@ mod tests { }
}, - html!(
), + html! {
) }, ); assert_eq!( html! { @@ -1223,7 +1223,7 @@ mod tests { }
}, - html!(
), + html! {
}, ); assert_eq!( html! { @@ -1233,7 +1233,7 @@ mod tests { }
}, - html!(
<>
), + html! {
<>
}, ); } } From d030cd2b2de93fb3eb62b637fb5ae841cea44f2a Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Fri, 4 Dec 2020 17:14:50 +0100 Subject: [PATCH 23/47] Oops --- yew/src/virtual_dom/vtag.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yew/src/virtual_dom/vtag.rs b/yew/src/virtual_dom/vtag.rs index d7fcb9ce464..7a8156208b5 100644 --- a/yew/src/virtual_dom/vtag.rs +++ b/yew/src/virtual_dom/vtag.rs @@ -1211,7 +1211,7 @@ mod tests { }
}, - html! {
) }, + html! {
}, ); assert_eq!( html! { From 28543880018e32460fc19b3659365de7efcff8a0 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Fri, 4 Dec 2020 17:22:15 +0100 Subject: [PATCH 24/47] Added ToNodeIterator to HtmlIf --- yew-macro/src/html_tree/html_if.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index d6f15715b50..cca6df95665 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -1,4 +1,4 @@ -use super::HtmlRootBraced; +use super::{HtmlRootBraced, ToNodeIterator}; use crate::PeekValue; use boolinator::Boolinator; use proc_macro2::TokenStream; @@ -63,6 +63,27 @@ impl ToTokens for HtmlIf { } } +impl ToNodeIterator for HtmlIf { + fn to_node_iterator_stream(&self) -> Option { + let HtmlIf { + if_token, + cond, + then_branch, + else_branch, + } = self; + let default_else_branch = syn::parse_str("{}").unwrap(); + let else_branch = else_branch + .as_ref() + .map(|(_, branch)| branch) + .unwrap_or(&default_else_branch); + let new_tokens = quote_spanned! {if_token.span()=> + if #cond #then_branch else #else_branch + }; + + Some(quote_spanned! {if_token.span=> #new_tokens}) + } +} + pub enum HtmlRootBracedOrIf { Branch(HtmlRootBraced), If(HtmlIf), From 58d83c6d6354744eada2a9fd586cb07cab124515 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Fri, 4 Dec 2020 17:39:31 +0100 Subject: [PATCH 25/47] Improve error spans --- yew-macro/src/html_tree/html_if.rs | 21 ++++++++++-- .../tests/html_macro/html-if-fail.stderr | 32 +++++++------------ 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index cca6df95665..a792c5c9b13 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -25,12 +25,29 @@ impl PeekValue<()> for HtmlIf { impl Parse for HtmlIf { fn parse(input: ParseStream) -> ParseResult { let if_token = input.parse()?; - let cond = input.parse()?; + let cond: Box = input.parse()?; + + if input.is_empty() { + return Err(syn::Error::new( + cond.span(), + "expected block after this condition", + )); + } + let then_branch = input.parse()?; let else_branch = input .parse::() .ok() - .map(|else_token| input.parse().map(|branch| (else_token, branch))) + .map(|else_token| { + if input.is_empty() { + return Err(syn::Error::new( + else_token.span(), + "expected block or `if` token after this token", + )); + } + + input.parse().map(|branch| (else_token, branch)) + }) .transpose()?; Ok(HtmlIf { diff --git a/yew-macro/tests/html_macro/html-if-fail.stderr b/yew-macro/tests/html_macro/html-if-fail.stderr index da5c12c52a5..710cf1cd64a 100644 --- a/yew-macro/tests/html_macro/html-if-fail.stderr +++ b/yew-macro/tests/html_macro/html-if-fail.stderr @@ -1,34 +1,26 @@ -error: unexpected end of input, expected curly braces - --> $DIR/html-if-fail.rs:4:5 +error: expected block after this condition + --> $DIR/html-if-fail.rs:4:16 | 4 | html! { if {} }; - | ^^^^^^^^^^^^^^^^ - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^ -error: unexpected end of input, expected `if` - --> $DIR/html-if-fail.rs:6:5 +error: expected block or `if` token after this token + --> $DIR/html-if-fail.rs:6:24 | 6 | html! { if true {} else }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^ -error: unexpected end of input, expected curly braces - --> $DIR/html-if-fail.rs:7:5 +error: expected block after this condition + --> $DIR/html-if-fail.rs:7:32 | 7 | html! { if true {} else if {} }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^ -error: unexpected end of input, expected `if` - --> $DIR/html-if-fail.rs:8:5 +error: expected block or `if` token after this token + --> $DIR/html-if-fail.rs:8:40 | 8 | html! { if true {} else if true {} else }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + | ^^^^ error[E0308]: mismatched types --> $DIR/html-if-fail.rs:5:16 From 47bd3025fc931ddcb84f0223ee758f49a8628409 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Fri, 4 Dec 2020 17:46:04 +0100 Subject: [PATCH 26/47] More html!() --- yew-macro/tests/html_macro/html-if-pass.rs | 4 ++-- yew/src/virtual_dom/vtag.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index 697d6b75b67..7b8c6094160 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -4,8 +4,8 @@ fn compile_pass() { html! { if true {} }; html! { if true {
} }; html! { if true { <>
} }; - html! { if true { { html!() } } }; - html! { if true { { { let _x = 42; html!() } } } }; + html! { if true { { html! {} } } }; + html! { if true { { { let _x = 42; html! {} } } } }; html! { if true {} else {} }; html! { if true {} else if true {} }; html! { if true {} else if true {} else {} }; diff --git a/yew/src/virtual_dom/vtag.rs b/yew/src/virtual_dom/vtag.rs index 7a8156208b5..76412b70db9 100644 --- a/yew/src/virtual_dom/vtag.rs +++ b/yew/src/virtual_dom/vtag.rs @@ -1101,7 +1101,7 @@ mod tests {
} }, - html!(
), + html! {
}, ); assert_eq!( html! { @@ -1121,7 +1121,7 @@ mod tests {
} }, - html!(), + html! {}, ); // non-root tests @@ -1191,7 +1191,7 @@ mod tests {
} }, - html!(
), + html! {
}, ); assert_eq!( html! { From 8bcce7fbbc915e80f97c7b2bd6fb74d09f2596fe Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Fri, 4 Dec 2020 17:52:04 +0100 Subject: [PATCH 27/47] Move tests to not use browser --- yew/src/virtual_dom/vtag.rs | 197 ++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 96 deletions(-) diff --git a/yew/src/virtual_dom/vtag.rs b/yew/src/virtual_dom/vtag.rs index 76412b70db9..3ba444d7eb2 100644 --- a/yew/src/virtual_dom/vtag.rs +++ b/yew/src/virtual_dom/vtag.rs @@ -1092,6 +1092,107 @@ mod tests { panic!("vtag expected"); } } +} + +#[cfg(all(test, feature = "web_sys"))] +mod layout_tests { + extern crate self as yew; + + use crate::html; + use crate::virtual_dom::layout_tests::{diff_layouts, TestLayout}; + + #[cfg(feature = "wasm_test")] + use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; + + #[cfg(feature = "wasm_test")] + wasm_bindgen_test_configure!(run_in_browser); + + #[test] + fn diff() { + let layout1 = TestLayout { + name: "1", + node: html! { +
    +
  • + {"a"} +
  • +
  • + {"b"} +
  • +
+ }, + expected: "
  • a
  • b
", + }; + + let layout2 = TestLayout { + name: "2", + node: html! { +
    +
  • + {"a"} +
  • +
  • + {"b"} +
  • +
  • + {"d"} +
  • +
+ }, + expected: "
  • a
  • b
  • d
", + }; + + let layout3 = TestLayout { + name: "3", + node: html! { +
    +
  • + {"a"} +
  • +
  • + {"b"} +
  • +
  • + {"c"} +
  • +
  • + {"d"} +
  • +
+ }, + expected: "
  • a
  • b
  • c
  • d
", + }; + + let layout4 = TestLayout { + name: "4", + node: html! { +
    +
  • + <> + {"a"} + +
  • +
  • + {"b"} +
  • + {"c"} +
  • +
  • + {"d"} +
  • + +
+ }, + expected: "
  • a
  • b
  • c
  • d
", + }; + + diff_layouts(vec![layout1, layout2, layout3, layout4]); + } +} + +#[cfg(test)] +mod tests_without_browser { + use crate::html; #[test] fn html_if_bool() { @@ -1237,99 +1338,3 @@ mod tests { ); } } - -#[cfg(all(test, feature = "web_sys"))] -mod layout_tests { - extern crate self as yew; - - use crate::html; - use crate::virtual_dom::layout_tests::{diff_layouts, TestLayout}; - - #[cfg(feature = "wasm_test")] - use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; - - #[cfg(feature = "wasm_test")] - wasm_bindgen_test_configure!(run_in_browser); - - #[test] - fn diff() { - let layout1 = TestLayout { - name: "1", - node: html! { -
    -
  • - {"a"} -
  • -
  • - {"b"} -
  • -
- }, - expected: "
  • a
  • b
", - }; - - let layout2 = TestLayout { - name: "2", - node: html! { -
    -
  • - {"a"} -
  • -
  • - {"b"} -
  • -
  • - {"d"} -
  • -
- }, - expected: "
  • a
  • b
  • d
", - }; - - let layout3 = TestLayout { - name: "3", - node: html! { -
    -
  • - {"a"} -
  • -
  • - {"b"} -
  • -
  • - {"c"} -
  • -
  • - {"d"} -
  • -
- }, - expected: "
  • a
  • b
  • c
  • d
", - }; - - let layout4 = TestLayout { - name: "4", - node: html! { -
    -
  • - <> - {"a"} - -
  • -
  • - {"b"} -
  • - {"c"} -
  • -
  • - {"d"} -
  • - -
- }, - expected: "
  • a
  • b
  • c
  • d
", - }; - - diff_layouts(vec![layout1, layout2, layout3, layout4]); - } -} From 9d6af31ff022188ab547e467d4bd0b6ec5629ce6 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Fri, 4 Dec 2020 18:19:49 +0100 Subject: [PATCH 28/47] Multiple children in if-expr --- yew-macro/src/html_tree/html_component.rs | 4 +-- yew-macro/src/html_tree/html_element.rs | 4 +-- yew-macro/src/html_tree/html_list.rs | 4 +-- yew-macro/src/html_tree/mod.rs | 30 ++++++++++++++----- yew-macro/tests/html_macro/html-if-fail.rs | 1 + .../tests/html_macro/html-if-fail.stderr | 6 ++++ yew-macro/tests/html_macro/html-if-pass.rs | 2 +- 7 files changed, 36 insertions(+), 15 deletions(-) diff --git a/yew-macro/src/html_tree/html_component.rs b/yew-macro/src/html_tree/html_component.rs index 47585369151..5bc10e4d619 100644 --- a/yew-macro/src/html_tree/html_component.rs +++ b/yew-macro/src/html_tree/html_component.rs @@ -27,7 +27,7 @@ impl PeekValue<()> for HtmlComponent { } impl Parse for HtmlComponent { - fn parse(input: ParseStream) -> syn::Result { + fn parse(mut input: ParseStream) -> syn::Result { if HtmlComponentClose::peek(input.cursor()).is_some() { return match input.parse::() { Ok(close) => Err(syn::Error::new_spanned( @@ -62,7 +62,7 @@ impl Parse for HtmlComponent { } } - children.parse_child(input)?; + children.parse_child(&mut input)?; } input.parse::()?; diff --git a/yew-macro/src/html_tree/html_element.rs b/yew-macro/src/html_tree/html_element.rs index 9774fdcd39c..995abd2aacc 100644 --- a/yew-macro/src/html_tree/html_element.rs +++ b/yew-macro/src/html_tree/html_element.rs @@ -25,7 +25,7 @@ impl PeekValue<()> for HtmlElement { } impl Parse for HtmlElement { - fn parse(input: ParseStream) -> syn::Result { + fn parse(mut input: ParseStream) -> syn::Result { if HtmlElementClose::peek(input.cursor()).is_some() { return match input.parse::() { Ok(close) => Err(syn::Error::new_spanned( @@ -75,7 +75,7 @@ impl Parse for HtmlElement { } } - children.parse_child(input)?; + children.parse_child(&mut input)?; } input.parse::()?; diff --git a/yew-macro/src/html_tree/html_list.rs b/yew-macro/src/html_tree/html_list.rs index c66c6919abb..5f5aa9914b4 100644 --- a/yew-macro/src/html_tree/html_list.rs +++ b/yew-macro/src/html_tree/html_list.rs @@ -22,7 +22,7 @@ impl PeekValue<()> for HtmlList { } impl Parse for HtmlList { - fn parse(input: ParseStream) -> syn::Result { + fn parse(mut input: ParseStream) -> syn::Result { if HtmlListClose::peek(input.cursor()).is_some() { return match input.parse::() { Ok(close) => Err(syn::Error::new_spanned( @@ -36,7 +36,7 @@ impl Parse for HtmlList { let open = input.parse::()?; let mut children = HtmlChildrenTree::new(); while HtmlListClose::peek(input.cursor()).is_none() { - children.parse_child(input)?; + children.parse_child(&mut input)?; if input.is_empty() { return Err(syn::Error::new_spanned( open.to_spanned(), diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index 48a8c78bdc1..a9073c5b7d6 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -175,7 +175,7 @@ impl HtmlChildrenTree { Self(Vec::new()) } - pub fn parse_child(&mut self, input: ParseStream) -> ParseResult<()> { + pub fn parse_child(&mut self, input: &mut ParseStream) -> ParseResult<()> { self.0.push(input.parse()?); Ok(()) } @@ -227,6 +227,16 @@ impl HtmlChildrenTree { } } } + + fn parse_delimited(mut input: ParseStream) -> syn::Result { + let mut children = HtmlChildrenTree::new(); + + while !input.is_empty() { + children.parse_child(&mut input)?; + } + + Ok(children) + } } impl ToTokens for HtmlChildrenTree { @@ -237,7 +247,7 @@ impl ToTokens for HtmlChildrenTree { pub struct HtmlRootBraced { brace: token::Brace, - root: HtmlRoot, + children: HtmlChildrenTree, } impl PeekValue<()> for HtmlRootBraced { @@ -248,20 +258,24 @@ impl PeekValue<()> for HtmlRootBraced { impl Parse for HtmlRootBraced { fn parse(input: ParseStream) -> ParseResult { - let content; + let mut content; let brace = braced!(content in input); - let root = content.parse()?; + let children = HtmlChildrenTree::parse_delimited(&mut content)?; - Ok(HtmlRootBraced { brace, root }) + Ok(HtmlRootBraced { brace, children }) } } impl ToTokens for HtmlRootBraced { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { brace, root } = self; + let Self { brace, children } = self; - tokens.extend(quote_spanned! {brace.span=> - { #root } + tokens.extend(quote_spanned! {brace.span.span()=> + { + ::yew::virtual_dom::VNode::VList( + ::yew::virtual_dom::VList::new_with_children(#children, None) + ) + } }); } } diff --git a/yew-macro/tests/html_macro/html-if-fail.rs b/yew-macro/tests/html_macro/html-if-fail.rs index bcb59b94ed3..d1722636f73 100644 --- a/yew-macro/tests/html_macro/html-if-fail.rs +++ b/yew-macro/tests/html_macro/html-if-fail.rs @@ -6,6 +6,7 @@ fn compile_fail() { html! { if true {} else }; html! { if true {} else if {} }; html! { if true {} else if true {} else }; + html! { if true {} else if true {} else }; } fn main() {} diff --git a/yew-macro/tests/html_macro/html-if-fail.stderr b/yew-macro/tests/html_macro/html-if-fail.stderr index 710cf1cd64a..67e593547e7 100644 --- a/yew-macro/tests/html_macro/html-if-fail.stderr +++ b/yew-macro/tests/html_macro/html-if-fail.stderr @@ -22,6 +22,12 @@ error: expected block or `if` token after this token 8 | html! { if true {} else if true {} else }; | ^^^^ +error: expected block or `if` token after this token + --> $DIR/html-if-fail.rs:9:40 + | +9 | html! { if true {} else if true {} else }; + | ^^^^ + error[E0308]: mismatched types --> $DIR/html-if-fail.rs:5:16 | diff --git a/yew-macro/tests/html_macro/html-if-pass.rs b/yew-macro/tests/html_macro/html-if-pass.rs index 7b8c6094160..88e0ece1ead 100644 --- a/yew-macro/tests/html_macro/html-if-pass.rs +++ b/yew-macro/tests/html_macro/html-if-pass.rs @@ -3,7 +3,7 @@ use yew::prelude::*; fn compile_pass() { html! { if true {} }; html! { if true {
} }; - html! { if true { <>
} }; + html! { if true {
} }; html! { if true { { html! {} } } }; html! { if true { { { let _x = 42; html! {} } } } }; html! { if true {} else {} }; From 9f58c73cfcc21a27c10534a56e1f5c26dbc05fec Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 12 Dec 2020 11:03:08 +0100 Subject: [PATCH 29/47] Clippy fix --- yew-macro/src/html_tree/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index a9073c5b7d6..25a665bf7a2 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -260,7 +260,7 @@ impl Parse for HtmlRootBraced { fn parse(input: ParseStream) -> ParseResult { let mut content; let brace = braced!(content in input); - let children = HtmlChildrenTree::parse_delimited(&mut content)?; + let children = HtmlChildrenTree::parse_delimited(&content)?; Ok(HtmlRootBraced { brace, children }) } From 5fab1aed071c986097e55764d03ae18ba11990bc Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 12 Dec 2020 14:14:52 +0100 Subject: [PATCH 30/47] Clippy fix again --- yew-macro/src/html_tree/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index 25a665bf7a2..3a1b5e5f837 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -258,7 +258,7 @@ impl PeekValue<()> for HtmlRootBraced { impl Parse for HtmlRootBraced { fn parse(input: ParseStream) -> ParseResult { - let mut content; + let content; let brace = braced!(content in input); let children = HtmlChildrenTree::parse_delimited(&content)?; From d2e59f8c2b53c281e23d39b9b0457441b42c2968 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sat, 12 Dec 2020 14:20:24 +0100 Subject: [PATCH 31/47] Re-trigger CI From a1df3cef8f9e0d4fc458601888e3d22d44602627 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 28 Feb 2021 15:26:26 +0100 Subject: [PATCH 32/47] Apply suggestions from code review Co-authored-by: Simon --- yew-macro/src/html_tree/html_if.rs | 4 ++-- yew-macro/src/html_tree/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index a792c5c9b13..11754cca272 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -23,7 +23,7 @@ impl PeekValue<()> for HtmlIf { } impl Parse for HtmlIf { - fn parse(input: ParseStream) -> ParseResult { + fn parse(input: ParseStream) -> syn::Result { let if_token = input.parse()?; let cond: Box = input.parse()?; @@ -67,7 +67,7 @@ impl ToTokens for HtmlIf { then_branch, else_branch, } = self; - let default_else_branch = syn::parse_str("{}").unwrap(); + let default_else_branch = syn::parse_quote! { {} }; let else_branch = else_branch .as_ref() .map(|(_, branch)| branch) diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index 3a1b5e5f837..c593413846e 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -273,7 +273,7 @@ impl ToTokens for HtmlRootBraced { tokens.extend(quote_spanned! {brace.span.span()=> { ::yew::virtual_dom::VNode::VList( - ::yew::virtual_dom::VList::new_with_children(#children, None) + ::yew::virtual_dom::VList::new_with_children(#children, ::std::option::Option::None) ) } }); From 4a6ed0a79b0949625281bdce96567e6d68c4acba Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 28 Feb 2021 15:28:10 +0100 Subject: [PATCH 33/47] Replacing ParseResult by syn::Result everywhere --- yew-macro/src/html_tree/html_if.rs | 4 ++-- yew-macro/src/html_tree/mod.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/yew-macro/src/html_tree/html_if.rs b/yew-macro/src/html_tree/html_if.rs index 11754cca272..a7ca480b4f2 100644 --- a/yew-macro/src/html_tree/html_if.rs +++ b/yew-macro/src/html_tree/html_if.rs @@ -4,7 +4,7 @@ use boolinator::Boolinator; use proc_macro2::TokenStream; use quote::{quote_spanned, ToTokens}; use syn::buffer::Cursor; -use syn::parse::{Parse, ParseStream, Result as ParseResult}; +use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::{Expr, Token}; @@ -113,7 +113,7 @@ impl PeekValue<()> for HtmlRootBracedOrIf { } impl Parse for HtmlRootBracedOrIf { - fn parse(input: ParseStream) -> ParseResult { + fn parse(input: ParseStream) -> syn::Result { if HtmlRootBraced::peek(input.cursor()).is_some() { input.parse().map(Self::Branch) } else { diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index c593413846e..83623992549 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -2,7 +2,7 @@ use crate::PeekValue; use proc_macro2::{Delimiter, Ident, Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; use syn::buffer::Cursor; -use syn::parse::{Parse, ParseStream, Result as ParseResult}; +use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::{braced, token}; @@ -45,7 +45,7 @@ pub enum HtmlTree { } impl Parse for HtmlTree { - fn parse(input: ParseStream) -> ParseResult { + fn parse(input: ParseStream) -> syn::Result { let html_type = HtmlTree::peek(input.cursor()) .ok_or_else(|| input.error("expected a valid html element"))?; let html_tree = match html_type { @@ -102,7 +102,7 @@ pub enum HtmlRoot { } impl Parse for HtmlRoot { - fn parse(input: ParseStream) -> ParseResult { + fn parse(input: ParseStream) -> syn::Result { let html_root = if HtmlTree::peek(input.cursor()).is_some() { Self::Tree(input.parse()?) } else if HtmlIterable::peek(input.cursor()).is_some() { @@ -136,7 +136,7 @@ impl ToTokens for HtmlRoot { /// Same as HtmlRoot but always returns a VNode. pub struct HtmlRootVNode(HtmlRoot); impl Parse for HtmlRootVNode { - fn parse(input: ParseStream) -> ParseResult { + fn parse(input: ParseStream) -> syn::Result { input.parse().map(Self) } } @@ -175,7 +175,7 @@ impl HtmlChildrenTree { Self(Vec::new()) } - pub fn parse_child(&mut self, input: &mut ParseStream) -> ParseResult<()> { + pub fn parse_child(&mut self, input: &mut ParseStream) -> syn::Result<()> { self.0.push(input.parse()?); Ok(()) } @@ -257,7 +257,7 @@ impl PeekValue<()> for HtmlRootBraced { } impl Parse for HtmlRootBraced { - fn parse(input: ParseStream) -> ParseResult { + fn parse(input: ParseStream) -> syn::Result { let content; let brace = braced!(content in input); let children = HtmlChildrenTree::parse_delimited(&content)?; From 17718832365d5610ba47e097ff041844f724f281 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 28 Feb 2021 15:36:43 +0100 Subject: [PATCH 34/47] Remove unnecessary &mut --- yew-macro/src/html_tree/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yew-macro/src/html_tree/mod.rs b/yew-macro/src/html_tree/mod.rs index 83623992549..3e1687b2758 100644 --- a/yew-macro/src/html_tree/mod.rs +++ b/yew-macro/src/html_tree/mod.rs @@ -175,7 +175,7 @@ impl HtmlChildrenTree { Self(Vec::new()) } - pub fn parse_child(&mut self, input: &mut ParseStream) -> syn::Result<()> { + pub fn parse_child(&mut self, input: ParseStream) -> syn::Result<()> { self.0.push(input.parse()?); Ok(()) } From 75b1a85c28df36ad8bf61344eef56e06667da3b6 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 28 Feb 2021 15:51:08 +0100 Subject: [PATCH 35/47] Attempt to add test on ToNodeIterator --- .../tests/html_macro/html-component-pass.rs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/yew-macro/tests/html_macro/html-component-pass.rs b/yew-macro/tests/html_macro/html-component-pass.rs index 4c6bf71b1cf..d869e6a2d68 100644 --- a/yew-macro/tests/html_macro/html-component-pass.rs +++ b/yew-macro/tests/html_macro/html-component-pass.rs @@ -175,6 +175,37 @@ impl Component for ChildContainer { } } +#[derive(Properties, Clone)] +struct Props { + children: Children, +} + +struct ComponentWithChildren { + props: Props, +} + +impl Component for ComponentWithChildren { + type Message = (); + type Properties = Props; + + fn create(_: Self::Properties, _: ComponentLink) -> Self { + unimplemented!() + } + fn update(&mut self, _: Self::Message) -> ShouldRender { + unimplemented!() + } + fn change(&mut self, _: Self::Properties) -> ShouldRender { + unimplemented!() + } + fn view(&self) -> Html { + html! { +
    + { for self.props.children.iter().map(|child| html! {
  • { child }
  • }) } +
+ } + } +} + mod scoped { pub use super::Child; pub use super::Container; @@ -348,6 +379,25 @@ fn compile_pass() { }; html_nested! { 1 }; + + let a = html! { + + if true { + { "hello" } + { "world" } + } else { + { "goodbye" } + { "world" } + } + + }; + let b = html! { +
    +
  • {"Hello"}
  • +
  • {"World"}
  • +
+ }; + assert_eq!(a, b); } fn main() {} From 660a2b61d18d4c34fa41cd8f53f290e499ef4601 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 28 Feb 2021 17:00:51 +0100 Subject: [PATCH 36/47] Clippy fixes --- packages/yew-macro/src/html_tree/html_component.rs | 4 ++-- packages/yew-macro/src/html_tree/html_element.rs | 4 ++-- packages/yew-macro/src/html_tree/html_list.rs | 4 ++-- packages/yew-macro/src/html_tree/mod.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_component.rs b/packages/yew-macro/src/html_tree/html_component.rs index ef363d9a4b2..f2431ace48b 100644 --- a/packages/yew-macro/src/html_tree/html_component.rs +++ b/packages/yew-macro/src/html_tree/html_component.rs @@ -27,7 +27,7 @@ impl PeekValue<()> for HtmlComponent { } impl Parse for HtmlComponent { - fn parse(mut input: ParseStream) -> syn::Result { + fn parse(input: ParseStream) -> syn::Result { if HtmlComponentClose::peek(input.cursor()).is_some() { return match input.parse::() { Ok(close) => Err(syn::Error::new_spanned( @@ -62,7 +62,7 @@ impl Parse for HtmlComponent { } } - children.parse_child(&mut input)?; + children.parse_child(input)?; } input.parse::()?; diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs index 0e7e050d7cf..1bd46e093eb 100644 --- a/packages/yew-macro/src/html_tree/html_element.rs +++ b/packages/yew-macro/src/html_tree/html_element.rs @@ -25,7 +25,7 @@ impl PeekValue<()> for HtmlElement { } impl Parse for HtmlElement { - fn parse(mut input: ParseStream) -> syn::Result { + fn parse(input: ParseStream) -> syn::Result { if HtmlElementClose::peek(input.cursor()).is_some() { return match input.parse::() { Ok(close) => Err(syn::Error::new_spanned( @@ -75,7 +75,7 @@ impl Parse for HtmlElement { } } - children.parse_child(&mut input)?; + children.parse_child(input)?; } input.parse::()?; diff --git a/packages/yew-macro/src/html_tree/html_list.rs b/packages/yew-macro/src/html_tree/html_list.rs index 1b65313d501..d3fb07c9d60 100644 --- a/packages/yew-macro/src/html_tree/html_list.rs +++ b/packages/yew-macro/src/html_tree/html_list.rs @@ -22,7 +22,7 @@ impl PeekValue<()> for HtmlList { } impl Parse for HtmlList { - fn parse(mut input: ParseStream) -> syn::Result { + fn parse(input: ParseStream) -> syn::Result { if HtmlListClose::peek(input.cursor()).is_some() { return match input.parse::() { Ok(close) => Err(syn::Error::new_spanned( @@ -36,7 +36,7 @@ impl Parse for HtmlList { let open = input.parse::()?; let mut children = HtmlChildrenTree::new(); while HtmlListClose::peek(input.cursor()).is_none() { - children.parse_child(&mut input)?; + children.parse_child(input)?; if input.is_empty() { return Err(syn::Error::new_spanned( open.to_spanned(), diff --git a/packages/yew-macro/src/html_tree/mod.rs b/packages/yew-macro/src/html_tree/mod.rs index 3c410a8f8d5..e8e8d5842a9 100644 --- a/packages/yew-macro/src/html_tree/mod.rs +++ b/packages/yew-macro/src/html_tree/mod.rs @@ -262,11 +262,11 @@ impl HtmlChildrenTree { } } - fn parse_delimited(mut input: ParseStream) -> syn::Result { + fn parse_delimited(input: ParseStream) -> syn::Result { let mut children = HtmlChildrenTree::new(); while !input.is_empty() { - children.parse_child(&mut input)?; + children.parse_child(input)?; } Ok(children) From 805f591e1c40b4a34c70df0639e4ff1603da2d37 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 28 Feb 2021 17:37:52 +0100 Subject: [PATCH 37/47] Still works for some reason --- packages/yew/src/virtual_dom/vcomp.rs | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/yew/src/virtual_dom/vcomp.rs b/packages/yew/src/virtual_dom/vcomp.rs index 3d919bf3b9c..08a08aa07e7 100644 --- a/packages/yew/src/virtual_dom/vcomp.rs +++ b/packages/yew/src/virtual_dom/vcomp.rs @@ -815,4 +815,58 @@ mod layout_tests { layout10, layout11, layout12, ]); } + + #[test] + fn component_with_children() { + #[derive(Properties, Clone)] + struct Props { + children: Children, + } + + struct ComponentWithChildren { + props: Props, + } + + impl Component for ComponentWithChildren { + type Message = (); + type Properties = Props; + + fn create(props: Self::Properties, _: ComponentLink) -> Self { + Self { + props, + } + } + fn update(&mut self, _: Self::Message) -> ShouldRender { + true + } + fn change(&mut self, _: Self::Properties) -> ShouldRender { + true + } + fn view(&self) -> Html { + html! { +
    + { for self.props.children.iter().map(|child| html! {
  • { child }
  • }) } +
+ } + } + } + + let layout = TestLayout { + name: "13", + node: html! { + + if true { + { "hello" } + { "world" } + } else { + { "goodbye" } + { "world" } + } + + }, + expected: "
  • helloworld
", + }; + + diff_layouts(vec![layout]); + } } From 83bcf2395e1f8e4135dbf2c107f4695f72f8e13b Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Sun, 28 Feb 2021 17:38:21 +0100 Subject: [PATCH 38/47] Revert "Attempt to add test on ToNodeIterator" This reverts commit 75b1a85c28df36ad8bf61344eef56e06667da3b6. --- .../tests/html_macro/component-pass.rs | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/packages/yew-macro/tests/html_macro/component-pass.rs b/packages/yew-macro/tests/html_macro/component-pass.rs index a0e2dead22d..fcd87114445 100644 --- a/packages/yew-macro/tests/html_macro/component-pass.rs +++ b/packages/yew-macro/tests/html_macro/component-pass.rs @@ -132,37 +132,6 @@ impl Component for ChildContainer { } } -#[derive(Properties, Clone)] -struct Props { - children: Children, -} - -struct ComponentWithChildren { - props: Props, -} - -impl Component for ComponentWithChildren { - type Message = (); - type Properties = Props; - - fn create(_: Self::Properties, _: ComponentLink) -> Self { - unimplemented!() - } - fn update(&mut self, _: Self::Message) -> ShouldRender { - unimplemented!() - } - fn change(&mut self, _: Self::Properties) -> ShouldRender { - unimplemented!() - } - fn view(&self) -> Html { - html! { -
    - { for self.props.children.iter().map(|child| html! {
  • { child }
  • }) } -
- } - } -} - mod scoped { pub use super::Child; pub use super::Container; @@ -327,25 +296,6 @@ fn compile_pass() { }; html_nested! { 1 }; - - let a = html! { - - if true { - { "hello" } - { "world" } - } else { - { "goodbye" } - { "world" } - } - - }; - let b = html! { -
    -
  • {"Hello"}
  • -
  • {"World"}
  • -
- }; - assert_eq!(a, b); } fn main() {} From a265d4d6f2c233968a32228d239422d4ea818381 Mon Sep 17 00:00:00 2001 From: Hamza Date: Sun, 21 Nov 2021 17:26:22 +0500 Subject: [PATCH 39/47] fix CI --- packages/yew-macro/src/html_tree/mod.rs | 2 +- .../tests/html_macro/html-if-fail.stderr | 12 +++++----- packages/yew/src/virtual_dom/vcomp.rs | 23 ++++++------------- packages/yew/src/virtual_dom/vtag.rs | 12 +++++----- 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/packages/yew-macro/src/html_tree/mod.rs b/packages/yew-macro/src/html_tree/mod.rs index e8e8d5842a9..e91cff6d9b9 100644 --- a/packages/yew-macro/src/html_tree/mod.rs +++ b/packages/yew-macro/src/html_tree/mod.rs @@ -307,7 +307,7 @@ impl ToTokens for HtmlRootBraced { tokens.extend(quote_spanned! {brace.span.span()=> { ::yew::virtual_dom::VNode::VList( - ::yew::virtual_dom::VList::new_with_children(#children, ::std::option::Option::None) + ::yew::virtual_dom::VList::with_children(#children, ::std::option::Option::None) ) } }); diff --git a/packages/yew-macro/tests/html_macro/html-if-fail.stderr b/packages/yew-macro/tests/html_macro/html-if-fail.stderr index 67e593547e7..5d105e5e716 100644 --- a/packages/yew-macro/tests/html_macro/html-if-fail.stderr +++ b/packages/yew-macro/tests/html_macro/html-if-fail.stderr @@ -1,35 +1,35 @@ error: expected block after this condition - --> $DIR/html-if-fail.rs:4:16 + --> tests/html_macro/html-if-fail.rs:4:16 | 4 | html! { if {} }; | ^^ error: expected block or `if` token after this token - --> $DIR/html-if-fail.rs:6:24 + --> tests/html_macro/html-if-fail.rs:6:24 | 6 | html! { if true {} else }; | ^^^^ error: expected block after this condition - --> $DIR/html-if-fail.rs:7:32 + --> tests/html_macro/html-if-fail.rs:7:32 | 7 | html! { if true {} else if {} }; | ^^ error: expected block or `if` token after this token - --> $DIR/html-if-fail.rs:8:40 + --> tests/html_macro/html-if-fail.rs:8:40 | 8 | html! { if true {} else if true {} else }; | ^^^^ error: expected block or `if` token after this token - --> $DIR/html-if-fail.rs:9:40 + --> tests/html_macro/html-if-fail.rs:9:40 | 9 | html! { if true {} else if true {} else }; | ^^^^ error[E0308]: mismatched types - --> $DIR/html-if-fail.rs:5:16 + --> tests/html_macro/html-if-fail.rs:5:16 | 5 | html! { if 42 {} }; | ^^ expected `bool`, found integer diff --git a/packages/yew/src/virtual_dom/vcomp.rs b/packages/yew/src/virtual_dom/vcomp.rs index 36d6c11f533..c7b58b74912 100644 --- a/packages/yew/src/virtual_dom/vcomp.rs +++ b/packages/yew/src/virtual_dom/vcomp.rs @@ -819,34 +819,25 @@ mod layout_tests { #[test] fn component_with_children() { - #[derive(Properties, Clone)] + #[derive(Properties, PartialEq)] struct Props { children: Children, } - struct ComponentWithChildren { - props: Props, - } + struct ComponentWithChildren; impl Component for ComponentWithChildren { type Message = (); type Properties = Props; - fn create(props: Self::Properties, _: ComponentLink) -> Self { - Self { - props, - } - } - fn update(&mut self, _: Self::Message) -> ShouldRender { - true + fn create(_ctx: &Context) -> Self { + Self } - fn change(&mut self, _: Self::Properties) -> ShouldRender { - true - } - fn view(&self) -> Html { + + fn view(&self, ctx: &Context) -> Html { html! {
    - { for self.props.children.iter().map(|child| html! {
  • { child }
  • }) } + { for ctx.props().children.iter().map(|child| html! {
  • { child }
  • }) }
} } diff --git a/packages/yew/src/virtual_dom/vtag.rs b/packages/yew/src/virtual_dom/vtag.rs index d6e60645ed2..9e6130ec03c 100644 --- a/packages/yew/src/virtual_dom/vtag.rs +++ b/packages/yew/src/virtual_dom/vtag.rs @@ -1317,7 +1317,7 @@ mod tests_without_browser { assert_eq!( html! { if let Some(class) = option_foo { -
+
} }, html! {
}, @@ -1325,7 +1325,7 @@ mod tests_without_browser { assert_eq!( html! { if let Some(class) = none { -
+
} else {
} @@ -1335,7 +1335,7 @@ mod tests_without_browser { assert_eq!( html! { if let Some(class) = none { -
+
} }, html! {}, @@ -1346,7 +1346,7 @@ mod tests_without_browser { html! {
if let Some(class) = option_foo { -
+
}
}, @@ -1356,7 +1356,7 @@ mod tests_without_browser { html! {
if let Some(class) = none { -
+
} else {
} @@ -1368,7 +1368,7 @@ mod tests_without_browser { html! {
if let Some(class) = none { -
+
}
}, From bb8ef9da8fc9572f0705d3e775daadaa41ea7490 Mon Sep 17 00:00:00 2001 From: Hamza Date: Sun, 21 Nov 2021 20:14:42 +0500 Subject: [PATCH 40/47] add docs on website --- website/docs/concepts/html.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/website/docs/concepts/html.md b/website/docs/concepts/html.md index e5559c83367..76080261f4b 100644 --- a/website/docs/concepts/html.md +++ b/website/docs/concepts/html.md @@ -139,3 +139,37 @@ The documentation for keys is yet to be written. See [#1263](https://github.com/ For now, use keys when you have a list where the order of elements changes. This includes inserting or removing elements from anywhere but the end of the list. ::: + +## If blocks + +To conditionally render some markup, we wrap it in an `if` block: + +```rust +# use yew::html; +# let condition = true; + +html! { + if condition { +

{ "True case" }

+ } +} +``` + +There may also be an `else` case: + +```rust +# use yew::html; +# let condition = true; + +html! { + if condition { +

{ "True case" }

+ } else { +

{ "False case" }

+ } +} +``` + +:::note +`if let` statements can also be used in the same way. +::: From 1134c4c15dc3fd56a688aa18f5b2b141ec5ba33c Mon Sep 17 00:00:00 2001 From: Muhammad Hamza Date: Sun, 21 Nov 2021 20:16:24 +0500 Subject: [PATCH 41/47] Apply suggestions from code review Co-authored-by: mc1098 --- packages/yew-macro/src/html_tree/html_if.rs | 2 +- packages/yew-macro/tests/html_macro/html-if-pass.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/yew-macro/src/html_tree/html_if.rs b/packages/yew-macro/src/html_tree/html_if.rs index a7ca480b4f2..879b867d66a 100644 --- a/packages/yew-macro/src/html_tree/html_if.rs +++ b/packages/yew-macro/src/html_tree/html_if.rs @@ -42,7 +42,7 @@ impl Parse for HtmlIf { if input.is_empty() { return Err(syn::Error::new( else_token.span(), - "expected block or `if` token after this token", + "expected block or `if` after `else`", )); } diff --git a/packages/yew-macro/tests/html_macro/html-if-pass.rs b/packages/yew-macro/tests/html_macro/html-if-pass.rs index 88e0ece1ead..18420c1c6ab 100644 --- a/packages/yew-macro/tests/html_macro/html-if-pass.rs +++ b/packages/yew-macro/tests/html_macro/html-if-pass.rs @@ -4,6 +4,7 @@ fn compile_pass() { html! { if true {} }; html! { if true {
} }; html! { if true {
} }; + html! { if true { <>
} }; html! { if true { { html! {} } } }; html! { if true { { { let _x = 42; html! {} } } } }; html! { if true {} else {} }; From cd285a905eedca60b3d8fdb75c57e38d914229e6 Mon Sep 17 00:00:00 2001 From: Hamza Date: Sun, 21 Nov 2021 20:26:41 +0500 Subject: [PATCH 42/47] apparently I can't hide lines on website --- website/docs/concepts/html.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/website/docs/concepts/html.md b/website/docs/concepts/html.md index 76080261f4b..15407001cfc 100644 --- a/website/docs/concepts/html.md +++ b/website/docs/concepts/html.md @@ -145,11 +145,11 @@ For now, use keys when you have a list where the order of elements changes. This To conditionally render some markup, we wrap it in an `if` block: ```rust -# use yew::html; -# let condition = true; +use yew::html; +let some_condition = true; html! { - if condition { + if some_condition {

{ "True case" }

} } @@ -158,11 +158,11 @@ html! { There may also be an `else` case: ```rust -# use yew::html; -# let condition = true; +use yew::html; +let some_condition = true; html! { - if condition { + if some_condition {

{ "True case" }

} else {

{ "False case" }

From 8e089842cead0fb03a2b4a5a0a1620f410c958c4 Mon Sep 17 00:00:00 2001 From: Hamza Date: Sun, 21 Nov 2021 21:39:13 +0500 Subject: [PATCH 43/47] update stderr file --- packages/yew-macro/tests/html_macro/html-if-fail.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/yew-macro/tests/html_macro/html-if-fail.stderr b/packages/yew-macro/tests/html_macro/html-if-fail.stderr index 5d105e5e716..32686a66244 100644 --- a/packages/yew-macro/tests/html_macro/html-if-fail.stderr +++ b/packages/yew-macro/tests/html_macro/html-if-fail.stderr @@ -4,7 +4,7 @@ error: expected block after this condition 4 | html! { if {} }; | ^^ -error: expected block or `if` token after this token +error: expected block or `if` after `else` --> tests/html_macro/html-if-fail.rs:6:24 | 6 | html! { if true {} else }; @@ -16,13 +16,13 @@ error: expected block after this condition 7 | html! { if true {} else if {} }; | ^^ -error: expected block or `if` token after this token +error: expected block or `if` after `else` --> tests/html_macro/html-if-fail.rs:8:40 | 8 | html! { if true {} else if true {} else }; | ^^^^ -error: expected block or `if` token after this token +error: expected block or `if` after `else` --> tests/html_macro/html-if-fail.rs:9:40 | 9 | html! { if true {} else if true {} else }; From 063038fe36b04b5818e89449b2598bf99b384381 Mon Sep 17 00:00:00 2001 From: Hamza Date: Sun, 21 Nov 2021 21:48:08 +0500 Subject: [PATCH 44/47] will this work? --- website/docs/concepts/html.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/concepts/html.md b/website/docs/concepts/html.md index 15407001cfc..b322ad1a878 100644 --- a/website/docs/concepts/html.md +++ b/website/docs/concepts/html.md @@ -152,7 +152,7 @@ html! { if some_condition {

{ "True case" }

} -} +}; ``` There may also be an `else` case: @@ -167,7 +167,7 @@ html! { } else {

{ "False case" }

} -} +}; ``` :::note From 2b225671f2ce78c4f5263b3b8fe1acd14b5ffe30 Mon Sep 17 00:00:00 2001 From: Hamza Date: Sun, 21 Nov 2021 22:46:45 +0500 Subject: [PATCH 45/47] fix bug where conditions can't be expressions --- packages/yew-macro/src/html_tree/html_if.rs | 2 +- .../tests/html_macro/html-if-pass.rs | 19 ++++++++++++++++++- website/docs/concepts/html.md | 5 ++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_if.rs b/packages/yew-macro/src/html_tree/html_if.rs index 879b867d66a..14e4bd1364e 100644 --- a/packages/yew-macro/src/html_tree/html_if.rs +++ b/packages/yew-macro/src/html_tree/html_if.rs @@ -25,7 +25,7 @@ impl PeekValue<()> for HtmlIf { impl Parse for HtmlIf { fn parse(input: ParseStream) -> syn::Result { let if_token = input.parse()?; - let cond: Box = input.parse()?; + let cond = Box::new(input.call(Expr::parse_without_eager_brace)?); if input.is_empty() { return Err(syn::Error::new( diff --git a/packages/yew-macro/tests/html_macro/html-if-pass.rs b/packages/yew-macro/tests/html_macro/html-if-pass.rs index 18420c1c6ab..4ad4a265715 100644 --- a/packages/yew-macro/tests/html_macro/html-if-pass.rs +++ b/packages/yew-macro/tests/html_macro/html-if-pass.rs @@ -1,6 +1,6 @@ use yew::prelude::*; -fn compile_pass() { +fn compile_pass_lit() { html! { if true {} }; html! { if true {
} }; html! { if true {
} }; @@ -15,4 +15,21 @@ fn compile_pass() { html! {
if true {}
}; } +fn compile_pass_expr() { + let condition = true; + + html! { if condition {} }; + html! { if condition {
} }; + html! { if condition {
} }; + html! { if condition { <>
} }; + html! { if condition { { html! {} } } }; + html! { if condition { { { let _x = 42; html! {} } } } }; + html! { if condition {} else {} }; + html! { if condition {} else if condition {} }; + html! { if condition {} else if condition {} else {} }; + html! { if let Some(text) = Some("text") { { text } } }; + html! { <>
if condition {}
}; + html! {
if condition {}
}; +} + fn main() {} diff --git a/website/docs/concepts/html.md b/website/docs/concepts/html.md index b322ad1a878..7448786c874 100644 --- a/website/docs/concepts/html.md +++ b/website/docs/concepts/html.md @@ -146,10 +146,9 @@ To conditionally render some markup, we wrap it in an `if` block: ```rust use yew::html; -let some_condition = true; html! { - if some_condition { + if true {

{ "True case" }

} }; @@ -162,7 +161,7 @@ use yew::html; let some_condition = true; html! { - if some_condition { + if false {

{ "True case" }

} else {

{ "False case" }

From 714657f94c76e3fd0f740e4f91757e8877e5c005 Mon Sep 17 00:00:00 2001 From: Hamza Date: Mon, 22 Nov 2021 20:52:56 +0500 Subject: [PATCH 46/47] better error message --- packages/yew-macro/src/html_tree/html_if.rs | 14 ++++++++++++-- .../yew-macro/tests/html_macro/html-if-fail.stderr | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_if.rs b/packages/yew-macro/src/html_tree/html_if.rs index 14e4bd1364e..858f99cdae4 100644 --- a/packages/yew-macro/src/html_tree/html_if.rs +++ b/packages/yew-macro/src/html_tree/html_if.rs @@ -26,11 +26,21 @@ impl Parse for HtmlIf { fn parse(input: ParseStream) -> syn::Result { let if_token = input.parse()?; let cond = Box::new(input.call(Expr::parse_without_eager_brace)?); - + match &*cond { + Expr::Block(syn::ExprBlock { block, .. }) if block.stmts.len() == 0 => { + return Err( + syn::Error::new( + cond.span(), + "missing condition for `if` expression", + ) + ) + }, + _ => {} + } if input.is_empty() { return Err(syn::Error::new( cond.span(), - "expected block after this condition", + "this `if` expression has a condition, but no block", )); } diff --git a/packages/yew-macro/tests/html_macro/html-if-fail.stderr b/packages/yew-macro/tests/html_macro/html-if-fail.stderr index 32686a66244..4d20890640e 100644 --- a/packages/yew-macro/tests/html_macro/html-if-fail.stderr +++ b/packages/yew-macro/tests/html_macro/html-if-fail.stderr @@ -1,4 +1,4 @@ -error: expected block after this condition +error: missing condition for `if` expression --> tests/html_macro/html-if-fail.rs:4:16 | 4 | html! { if {} }; @@ -10,7 +10,7 @@ error: expected block or `if` after `else` 6 | html! { if true {} else }; | ^^^^ -error: expected block after this condition +error: missing condition for `if` expression --> tests/html_macro/html-if-fail.rs:7:32 | 7 | html! { if true {} else if {} }; From f0e60911dee9470a94116140c02d30fd9c125141 Mon Sep 17 00:00:00 2001 From: Hamza Date: Mon, 22 Nov 2021 20:55:20 +0500 Subject: [PATCH 47/47] clippy & fmt --- packages/yew-macro/src/html_tree/html_if.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_if.rs b/packages/yew-macro/src/html_tree/html_if.rs index 858f99cdae4..aae1890ab60 100644 --- a/packages/yew-macro/src/html_tree/html_if.rs +++ b/packages/yew-macro/src/html_tree/html_if.rs @@ -27,14 +27,12 @@ impl Parse for HtmlIf { let if_token = input.parse()?; let cond = Box::new(input.call(Expr::parse_without_eager_brace)?); match &*cond { - Expr::Block(syn::ExprBlock { block, .. }) if block.stmts.len() == 0 => { - return Err( - syn::Error::new( - cond.span(), - "missing condition for `if` expression", - ) - ) - }, + Expr::Block(syn::ExprBlock { block, .. }) if block.stmts.is_empty() => { + return Err(syn::Error::new( + cond.span(), + "missing condition for `if` expression", + )) + } _ => {} } if input.is_empty() {