Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse blocks in html! as Rust block expressions #3466

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ target/
*.iml
/.idea/
/.vscode/

# artifacts from tools like prettier
node_modules/
package.json
package-lock.json
40 changes: 31 additions & 9 deletions packages/yew-macro/src/html_tree/html_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ use quote::{quote, quote_spanned, ToTokens};
use syn::buffer::Cursor;
use syn::parse::{Parse, ParseStream, Result};
use syn::spanned::Spanned;
use syn::{Expr, Lit};
use syn::{Block, Lit, Stmt};

use super::ToNodeIterator;
use crate::stringify::Stringify;
use crate::PeekValue;

pub enum HtmlNode {
Literal(Box<Lit>),
Expression(Box<Expr>),
Expression(Vec<Stmt>),
}

impl Parse for HtmlNode {
Expand All @@ -23,7 +23,7 @@ impl Parse for HtmlNode {
}
HtmlNode::Literal(Box::new(lit))
} else {
HtmlNode::Expression(Box::new(input.parse()?))
HtmlNode::Expression(Block::parse_within(input)?)
};

Ok(node)
Expand All @@ -49,7 +49,17 @@ impl ToTokens for HtmlNode {
let sr = lit.stringify();
quote_spanned! {lit.span()=> ::yew::virtual_dom::VText::new(#sr) }
}
HtmlNode::Expression(expr) => quote! {#expr},
HtmlNode::Expression(stmts) => {
if let [expr] = &stmts[..] {
quote! {#expr}
} else {
let mut block = TokenStream::new();
for stmt in stmts.iter() {
stmt.to_tokens(&mut block)
}
quote_spanned! {block.span()=> {#block}}
}
}
});
}
}
Expand All @@ -58,11 +68,23 @@ impl ToNodeIterator for HtmlNode {
fn to_node_iterator_stream(&self) -> Option<TokenStream> {
match self {
HtmlNode::Literal(_) => None,
HtmlNode::Expression(expr) => {
// NodeSeq turns both Into<T> and Vec<Into<T>> into IntoIterator<Item = T>
Some(quote_spanned! {expr.span().resolved_at(Span::call_site())=>
::std::convert::Into::<::yew::utils::NodeSeq<_, _>>::into(#expr)
})
HtmlNode::Expression(stmts) => {
if let [expr] = &stmts[..] {
Some(quote_spanned! {expr.span().resolved_at(Span::call_site())=>
::std::convert::Into::<::yew::utils::NodeSeq<_, _>>::into(#expr)
})
} else {
let mut block = TokenStream::new();
for stmt in stmts.iter() {
stmt.to_tokens(&mut block)
}
// NodeSeq turns both Into<T> and Vec<Into<T>> into IntoIterator<Item = T>
Some(
quote_spanned! {block.span().resolved_at(Span::call_site())=>
::std::convert::Into::<::yew::utils::NodeSeq<_, _>>::into({#block})
},
)
}
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions packages/yew-macro/tests/html_macro/block-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,11 @@ fn main() {
{ for ::std::iter::Iterator::map(0..3, item) }
</ul>
};

::yew::html! {
<span>{
let version = 0.21;
::std::format!("Yew {version}")
}</span>
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn HelloWorld(props: &Props) -> Html {
:::note
If you have an internal pure component that makes no use of hooks and other component machinery, you can often write it instead
as a normal function returning `Html` and avoid a bit of overhead for Yew, related to running the component lifecycle. Use
[expression syntax](concepts/html/literals-and-expressions.mdx#expressions) to render them in `html!`.
[code block syntax](concepts/html/literals-and-expressions.mdx#code-blocks) to render them in `html!`.
:::

## Impure components
Expand Down
9 changes: 6 additions & 3 deletions website/docs/concepts/html/literals-and-expressions.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: 'Literals and Expressions'
title: 'Literals and Code Blocks'
---

## Literals
Expand Down Expand Up @@ -27,16 +27,19 @@ html!{

## Expressions

You can insert expressions in your HTML using `{}` blocks, as long as they resolve to `Html`
You can insert blocks of Rust code in your HTML, as long as they resolve to `Html`

```rust
use yew::prelude::*;

let show_link = true;
fn some_computation() -> bool {
true
}

html! {
<div>
{
let show_link = some_computation();
if show_link {
html! {
<a href="https://example.com">{"Link"}</a>
Expand Down
Loading