From d9b84f9f8f1588ef5d2cb5af4415ab9ccf457a60 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 10 Jan 2024 19:33:34 -0800 Subject: [PATCH] Disambiguate expressions in rsx by requiring curlies --- examples/all_events.rs | 6 +- examples/calculator.rs | 8 +- examples/compose.rs | 4 +- examples/control_focus.rs | 2 +- examples/crm.rs | 4 +- examples/drops.rs | 4 +- examples/fermi.rs | 6 +- examples/file_explorer.rs | 20 +- examples/framework_benchmark.rs | 11 +- examples/inputs.rs | 6 +- .../openid_connect_demo/src/views/header.rs | 10 +- .../src/views/not_found.rs | 7 +- examples/pattern_model.rs | 10 +- examples/pattern_reducer.rs | 2 +- examples/query_segments_demo/src/main.rs | 4 +- examples/rsx_compile_fail.rs | 16 +- examples/rsx_usage.rs | 33 ++-- examples/scroll_to_top.rs | 2 +- examples/signals.rs | 6 +- examples/simple_list.rs | 10 +- examples/svg.rs | 2 +- examples/todomvc.rs | 68 ++++--- packages/autofmt/src/writer.rs | 9 +- packages/core/tests/create_dom.rs | 8 +- packages/core/tests/create_fragments.rs | 18 +- packages/core/tests/create_lists.rs | 4 +- packages/core/tests/create_passthru.rs | 4 +- packages/core/tests/diff_component.rs | 2 +- packages/core/tests/diff_keyed_list.rs | 24 +-- packages/core/tests/diff_unkeyed_list.rs | 28 +-- packages/core/tests/event_propagation.rs | 4 +- packages/core/tests/lifecycle.rs | 2 +- packages/core/tests/miri_full_app.rs | 4 +- packages/core/tests/miri_stress.rs | 4 +- packages/core/tests/task.rs | 2 +- packages/desktop/headless_tests/rendering.rs | 2 +- .../examples/all_terminal_events.rs | 2 +- packages/dioxus-tui/examples/buttons.rs | 56 +++--- packages/dioxus-tui/examples/color_test.rs | 37 ++-- packages/dioxus-tui/examples/list.rs | 4 +- .../examples/many_small_edit_stress.rs | 40 ++-- packages/dioxus/examples/stress.rs | 10 +- .../src/utils/persistant_iterator.rs | 37 ++-- packages/router/examples/simple_routes.rs | 2 +- .../router/src/components/history_buttons.rs | 4 +- packages/router/src/components/link.rs | 4 +- packages/rsx/src/node.rs | 181 +++++++++++------- packages/ssr/src/renderer.rs | 4 +- packages/ssr/tests/hydration.rs | 10 +- packages/ssr/tests/simple.rs | 12 +- packages/web/examples/hydrate.rs | 4 +- packages/web/examples/timeout_count.rs | 7 +- packages/web/tests/hydrate.rs | 2 +- 53 files changed, 399 insertions(+), 373 deletions(-) diff --git a/examples/all_events.rs b/examples/all_events.rs index 3f57ec747..14fd1282e 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -76,7 +76,11 @@ fn app(cx: Scope) -> Element { "Hover, click, type or scroll to see the info down below" } - div { events.read().iter().map(|event| rsx!( div { "{event:?}" } )) } + div { + for event in events.read().iter() { + div { "{event:?}" } + } + } } )) } diff --git a/examples/calculator.rs b/examples/calculator.rs index 2f8d4ee5f..11151dcbf 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -58,13 +58,13 @@ fn app(cx: Scope) -> Element { }; cx.render(rsx!( - style { include_str!("./assets/calculator.css") } + style { {include_str!("./assets/calculator.css")} } div { id: "wrapper", div { class: "app", div { class: "calculator", tabindex: "0", onkeydown: handle_key_down_event, - div { class: "calculator-display", val.to_string() } + div { class: "calculator-display", "{val}" } div { class: "calculator-keypad", div { class: "input-keys", div { class: "function-keys", @@ -103,14 +103,14 @@ fn app(cx: Scope) -> Element { div { class: "digit-keys", button { class: "calculator-key key-0", onclick: move |_| input_digit(0), "0" } button { class: "calculator-key key-dot", onclick: move |_| val.make_mut().push('.'), "●" } - (1..10).map(|k| rsx!{ + for k in 1..10 { button { class: "calculator-key {k}", name: "key-{k}", onclick: move |_| input_digit(k), "{k}" } - }), + } } } div { class: "operator-keys", diff --git a/examples/compose.rs b/examples/compose.rs index 133192281..180cd6983 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -32,12 +32,12 @@ fn app(cx: Scope) -> Element { } ul { - emails_sent.read().iter().map(|message| cx.render(rsx! { + for message in emails_sent.read().iter() { li { h3 { "email" } span {"{message}"} } - })) + } } } }) diff --git a/examples/control_focus.rs b/examples/control_focus.rs index 4f4c2ddcb..ac4e20418 100644 --- a/examples/control_focus.rs +++ b/examples/control_focus.rs @@ -16,7 +16,7 @@ fn app(cx: Scope) -> Element { loop { tokio::time::sleep(std::time::Duration::from_millis(10)).await; if let Some(element) = elements.read().get(focused) { - element.set_focus(true); + _ = element.set_focus(true).await; } else { focused = 0; } diff --git a/examples/crm.rs b/examples/crm.rs index 8af49b401..cd9ea330d 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -62,7 +62,7 @@ fn ClientList(cx: Scope) -> Element { Link { to: Route::ClientAdd {}, class: "pure-button pure-button-primary", "Add Client" } Link { to: Route::Settings {}, class: "pure-button", "Settings" } - clients.read().iter().map(|client| rsx! { + for client in clients.read().iter() { div { class: "client", style: "margin-bottom: 50px", @@ -70,7 +70,7 @@ fn ClientList(cx: Scope) -> Element { p { "Name: {client.first_name} {client.last_name}" } p { "Description: {client.description}" } } - }) + } }) } diff --git a/examples/drops.rs b/examples/drops.rs index 71a791990..956545e35 100644 --- a/examples/drops.rs +++ b/examples/drops.rs @@ -14,9 +14,9 @@ fn app(cx: Scope) -> Element { } render! { - (0..count).map(|_| rsx!{ + {(0..count).map(|_| rsx!{ drop_child {} - }) + })} } } diff --git a/examples/fermi.rs b/examples/fermi.rs index a15fc807f..3250471a4 100644 --- a/examples/fermi.rs +++ b/examples/fermi.rs @@ -39,9 +39,9 @@ fn ChildWithRef(cx: Scope) -> Element { cx.render(rsx! { div { ul { - names.read().iter().map(|f| rsx!{ - li { "hello: {f}" } - }) + for name in names.read().iter() { + li { "hello: {name}" } + } } button { onclick: move |_| { diff --git a/examples/file_explorer.rs b/examples/file_explorer.rs index 3ff757c60..a99ac3232 100644 --- a/examples/file_explorer.rs +++ b/examples/file_explorer.rs @@ -28,12 +28,12 @@ fn app(cx: Scope) -> Element { link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet", } header { i { class: "material-icons icon-menu", "menu" } - h1 { "Files: ", files.read().current() } + h1 { "Files: ", {files.read().current()} } span { } i { class: "material-icons", onclick: move |_| files.write().go_up(), "logout" } } main { - files.read().path_names.iter().enumerate().map(|(dir_id, path)| { + {files.read().path_names.iter().enumerate().map(|(dir_id, path)| { let path_end = path.split('/').last().unwrap_or(path.as_str()); let icon_type = if path_end.contains('.') { "description" @@ -52,15 +52,13 @@ fn app(cx: Scope) -> Element { h1 { "{path_end}" } } ) - }), - files.read().err.as_ref().map(|err| { - rsx! ( - div { - code { "{err}" } - button { onclick: move |_| files.write().clear_err(), "x" } - } - ) - }) + })}, + if let Some(err) = files.read().err.as_ref() { + div { + code { "{err}" } + button { onclick: move |_| files.write().clear_err(), "x" } + } + } } } }) diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index 7e7a320a9..3e286332b 100644 --- a/examples/framework_benchmark.rs +++ b/examples/framework_benchmark.rs @@ -66,9 +66,9 @@ fn app(cx: Scope) -> Element { } table { tbody { - items.read().iter().enumerate().map(|(id, item)| { - let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""}; - rsx!(tr { class: "{is_in_danger}", + for (id, item) in items.read().iter().enumerate() { + tr { + class: if (*selected).map(|s| s == id).unwrap_or(false) { "danger" }, td { class:"col-md-1" } td { class:"col-md-1", "{item.key}" } td { class:"col-md-1", onclick: move |_| selected.set(Some(id)), @@ -80,8 +80,9 @@ fn app(cx: Scope) -> Element { } } td { class: "col-md-6" } - }) - }) + } + } + } } span { class: "preloadicon glyphicon glyphicon-remove", aria_hidden: "true" } diff --git a/examples/inputs.rs b/examples/inputs.rs index 311afc1f0..00fc279ed 100644 --- a/examples/inputs.rs +++ b/examples/inputs.rs @@ -37,7 +37,7 @@ const FIELDS: &[(&str, &str)] = &[ fn app(cx: Scope) -> Element { cx.render(rsx! { div { margin_left: "30px", - select_example(cx), + {select_example(cx)}, div { // handling inputs on divs will catch all input events below // so the value of our input event will be either huey, dewey, louie, or true/false (because of the checkboxe) @@ -114,7 +114,7 @@ fn app(cx: Scope) -> Element { } } - FIELDS.iter().map(|(field, value)| rsx! { + for (field, value) in FIELDS.iter() { div { input { id: "{field}", @@ -131,7 +131,7 @@ fn app(cx: Scope) -> Element { } br {} } - }) + } } }) } diff --git a/examples/openid_connect_demo/src/views/header.rs b/examples/openid_connect_demo/src/views/header.rs index 1f2c6e057..b9b99e584 100644 --- a/examples/openid_connect_demo/src/views/header.rs +++ b/examples/openid_connect_demo/src/views/header.rs @@ -41,7 +41,7 @@ pub fn LogOut(cx: Scope) -> Element { } Err(error) => { rsx! { - div { format!{"Failed to load disconnection url: {:?}", error} } + div { "Failed to load disconnection url: {error:?}" } } } }, @@ -143,9 +143,9 @@ pub fn LoadClient(cx: Scope) -> Element { } } Err(error) => { + log::info! {"Failed to load client: {:?}", error}; rsx! { - div { format!{"Failed to load client: {:?}", error} } - log::info!{"Failed to load client: {:?}", error}, + div { "Failed to load client: {error:?}" } Outlet:: {} } } @@ -184,7 +184,7 @@ pub fn AuthHeader(cx: Scope) -> Element { Ok(email) => { rsx! { div { - div { email } + div { {email} } LogOut { client_id: client_props.client_id, client: client_props.client } Outlet:: {} } @@ -207,7 +207,7 @@ pub fn AuthHeader(cx: Scope) -> Element { log::info!("Other issue with token"); rsx! { div { - div { error.to_string() } + div { "{error}" } Outlet:: {} } } diff --git a/examples/openid_connect_demo/src/views/not_found.rs b/examples/openid_connect_demo/src/views/not_found.rs index 6321dc9fc..b8fb1d8b8 100644 --- a/examples/openid_connect_demo/src/views/not_found.rs +++ b/examples/openid_connect_demo/src/views/not_found.rs @@ -2,6 +2,9 @@ use dioxus::prelude::*; #[component] pub fn NotFound(cx: Scope, route: Vec) -> Element { - let routes = route.join(""); - render! {rsx! {div{routes}}} + render! { + div{ + {route.join("")} + } + } } diff --git a/examples/pattern_model.rs b/examples/pattern_model.rs index 2f6cc2803..13b5409e1 100644 --- a/examples/pattern_model.rs +++ b/examples/pattern_model.rs @@ -39,11 +39,11 @@ fn app(cx: Scope) -> Element { let state = use_ref(cx, Calculator::new); cx.render(rsx! { - style { include_str!("./assets/calculator.css") } + style { {include_str!("./assets/calculator.css")} } div { id: "wrapper", div { class: "app", div { class: "calculator", onkeypress: move |evt| state.write().handle_keydown(evt), - div { class: "calculator-display", state.read().formatted_display() } + div { class: "calculator-display", {state.read().formatted_display()} } div { class: "calculator-keypad", div { class: "input-keys", div { class: "function-keys", @@ -74,14 +74,14 @@ fn app(cx: Scope) -> Element { onclick: move |_| state.write().input_dot(), "●" } - (1..10).map(move |k| rsx!{ + for k in 1..10 { CalculatorKey { key: "{k}", name: "key-{k}", onclick: move |_| state.write().input_digit(k), "{k}" } - }) + } } } div { class: "operator-keys", @@ -130,7 +130,7 @@ fn CalculatorKey<'a>(cx: Scope<'a, CalculatorKeyProps<'a>>) -> Element { button { class: "calculator-key {cx.props.name}", onclick: move |e| cx.props.onclick.call(e), - &cx.props.children + {&cx.props.children} } }) } diff --git a/examples/pattern_reducer.rs b/examples/pattern_reducer.rs index 6f7e16f6e..98c4fb2c6 100644 --- a/examples/pattern_reducer.rs +++ b/examples/pattern_reducer.rs @@ -20,7 +20,7 @@ fn app(cx: Scope) -> Element { cx.render(rsx!( div { h1 {"Select an option"} - h3 { "The radio is... ", state.is_playing(), "!" } + h3 { "The radio is... ", {state.is_playing()}, "!" } button { onclick: move |_| state.make_mut().reduce(PlayerAction::Pause), "Pause" } diff --git a/examples/query_segments_demo/src/main.rs b/examples/query_segments_demo/src/main.rs index 3303aeecd..24896a403 100644 --- a/examples/query_segments_demo/src/main.rs +++ b/examples/query_segments_demo/src/main.rs @@ -66,7 +66,7 @@ impl FromQuery for ManualBlogQuerySegments { fn BlogPost(cx: Scope, query_params: ManualBlogQuerySegments) -> Element { render! { div{"This is your blogpost with a query segment:"} - div{format!("{:?}", query_params)} + div{ "{query_params:?}" } } } @@ -74,7 +74,7 @@ fn BlogPost(cx: Scope, query_params: ManualBlogQuerySegments) -> Element { fn AutomaticBlogPost(cx: Scope, name: String, surname: String) -> Element { render! { div{"This is your blogpost with a query segment:"} - div{format!("name={}&surname={}", name, surname)} + div{ "name={name}&surname={surname}" } } } diff --git a/examples/rsx_compile_fail.rs b/examples/rsx_compile_fail.rs index 40bad709b..ad2577b08 100644 --- a/examples/rsx_compile_fail.rs +++ b/examples/rsx_compile_fail.rs @@ -36,17 +36,17 @@ fn example(cx: Scope) -> Element { div { id: "asd", "your neighborhood spiderman" - items.iter().cycle().take(5).map(|f| rsx!{ - div { "{f.a}" } - }) + for item in items.iter().cycle().take(5) { + div { "{item.a}" } + } - things_list.iter().map(|f| rsx!{ - div { "{f.a}" "{f.b}" } - }) + for thing in things_list.iter() { + div { "{thing.a}" "{thing.b}" } + } - mything_read.as_ref().map(|f| rsx! { + if let Some(f) = mything_read.as_ref() { div { "{f}" } - }) + } } } )) diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index 317288d55..dc452af48 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -61,7 +61,7 @@ fn App(cx: Scope) -> Element { h1 {"Some text"} h1 {"Some text with {formatting}"} h1 {"Formatting basic expressions {formatting_tuple.0} and {formatting_tuple.1}"} - h1 {"Formatting without interpolation " formatting_tuple.0 "and" formatting_tuple.1 } + h1 {"Formatting without interpolation " {formatting_tuple.0} "and" {formatting_tuple.1} } h2 { "Multiple" "Text" @@ -94,10 +94,10 @@ fn App(cx: Scope) -> Element { } // Expressions can be used in element position too: - rsx!(p { "More templating!" }), + {rsx!(p { "More templating!" })}, // Iterators - (0..10).map(|i| rsx!(li { "{i}" })), + {(0..10).map(|i| rsx!(li { "{i}" }))}, // Iterators within expressions { @@ -117,24 +117,25 @@ fn App(cx: Scope) -> Element { // Conditional rendering // Dioxus conditional rendering is based around None/Some. We have no special syntax for conditionals. // You can convert a bool condition to rsx! with .then and .or - true.then(|| rsx!(div {})), + {true.then(|| rsx!(div {}))}, // Alternatively, you can use the "if" syntax - but both branches must be resolve to Element if false { - rsx!(h1 {"Top text"}) + h1 {"Top text"} } else { - rsx!(h1 {"Bottom text"}) + h1 {"Bottom text"} } // Using optionals for diverging branches - if true { + // Note that since this is wrapped in curlies, it's interpreted as an expression + {if true { Some(rsx!(h1 {"Top text"})) } else { None - } + }} // returning "None" without a diverging branch is a bit noisy... but rare in practice - None as Option<()>, + {None as Option<()>}, // can also just use empty fragments Fragment {} @@ -169,13 +170,13 @@ fn App(cx: Scope) -> Element { // Can pass in props directly as an expression { - let props = TallerProps {a: "hello", children: cx.render(rsx!(()))}; + let props = TallerProps {a: "hello", children: None }; rsx!(Taller { ..props }) } // Spreading can also be overridden manually Taller { - ..TallerProps { a: "ballin!", children: cx.render(rsx!(()) )}, + ..TallerProps { a: "ballin!", children: None }, a: "not ballin!" } @@ -204,16 +205,16 @@ fn App(cx: Scope) -> Element { // helper functions // Anything that implements IntoVnode can be dropped directly into Rsx - helper(cx, "hello world!") + {helper(cx, "hello world!")} // Strings can be supplied directly - String::from("Hello world!") + {String::from("Hello world!")} // So can format_args - format_args!("Hello {}!", "world") + {format_args!("Hello {}!", "world")} // Or we can shell out to a helper function - format_dollars(10, 50) + {format_dollars(10, 50)} } }) } @@ -269,7 +270,7 @@ pub struct TallerProps<'a> { #[component] pub fn Taller<'a>(cx: Scope<'a, TallerProps<'a>>) -> Element { cx.render(rsx! { - &cx.props.children + {&cx.props.children} }) } diff --git a/examples/scroll_to_top.rs b/examples/scroll_to_top.rs index 4eb38a451..4159fd67a 100644 --- a/examples/scroll_to_top.rs +++ b/examples/scroll_to_top.rs @@ -23,7 +23,7 @@ fn app(cx: Scope) -> Element { button { onclick: move |_| { if let Some(header) = header_element.read().as_ref() { - header.scroll_to(ScrollBehavior::Smooth); + _ = header.scroll_to(ScrollBehavior::Smooth); } }, "Scroll to top" diff --git a/examples/signals.rs b/examples/signals.rs index 0faac0af4..e72488443 100644 --- a/examples/signals.rs +++ b/examples/signals.rs @@ -31,7 +31,7 @@ fn app(cx: Scope) -> Element { // We can do boolean operations on the current signal value if count.value() > 5 { - rsx!{ h2 { "High five!" } } + h2 { "High five!" } } // We can cleanly map signals with iterators @@ -41,9 +41,9 @@ fn app(cx: Scope) -> Element { // We can also use the signal value as a slice if let [ref first, .., ref last] = saved_values.read().as_slice() { - rsx! { li { "First and last: {first}, {last}" } } + li { "First and last: {first}, {last}" } } else { - rsx! { "No saved values" } + "No saved values" } }) } diff --git a/examples/simple_list.rs b/examples/simple_list.rs index 84cd9db71..feb5aa3b4 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -8,17 +8,17 @@ fn app(cx: Scope) -> Element { cx.render(rsx!( div { // Use Map directly to lazily pull elements - (0..10).map(|f| rsx! { "{f}" }), + {(0..10).map(|f| rsx! { "{f}" })}, // Collect into an intermediate collection if necessary, and call into_iter - ["a", "b", "c", "d", "e", "f"] + {["a", "b", "c", "d", "e", "f"] .into_iter() .map(|f| rsx! { "{f}" }) .collect::>() - .into_iter(), + .into_iter()}, // Use optionals - Some(rsx! { "Some" }), + {Some(rsx! { "Some" })}, // use a for loop where the body itself is RSX for name in 0..10 { @@ -27,7 +27,7 @@ fn app(cx: Scope) -> Element { // Or even use an unterminated conditional if true { - rsx!{ "hello world!" } + "hello world!" } } )) diff --git a/examples/svg.rs b/examples/svg.rs index 329cbe6ae..f7a28d0cb 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -97,7 +97,7 @@ pub fn Die<'a>(cx: Scope<'a, DieProps<'a>>) -> Element { fill: "{fill}", } - dots + {dots} } }) } diff --git a/examples/todomvc.rs b/examples/todomvc.rs index c891ae832..f3cb59f09 100644 --- a/examples/todomvc.rs +++ b/examples/todomvc.rs @@ -52,30 +52,30 @@ pub fn app(cx: Scope<()>) -> Element { TodoHeader { todos: todos } section { class: "main", if !todos.is_empty() { - rsx! { - input { - id: "toggle-all", - class: "toggle-all", - r#type: "checkbox", - onchange: move |_| { - let check = active_todo_count != 0; - for (_, item) in todos.make_mut().iter_mut() { - item.checked = check; - } - }, - checked: if active_todo_count == 0 { "true" } else { "false" }, - } - label { r#for: "toggle-all" } + input { + id: "toggle-all", + class: "toggle-all", + r#type: "checkbox", + onchange: move |_| { + let check = active_todo_count != 0; + for (_, item) in todos.make_mut().iter_mut() { + item.checked = check; + } + }, + checked: if active_todo_count == 0 { "true" } else { "false" }, } + label { r#for: "toggle-all" } } ul { class: "todo-list", - filtered_todos.iter().map(|id| rsx!(TodoEntry { - key: "{id}", - id: *id, - todos: todos, - })) + for id in filtered_todos.iter() { + TodoEntry { + key: "{id}", + id: *id, + todos: todos, + } + } } - (!todos.is_empty()).then(|| rsx!( + if !todos.is_empty() { ListFooter { active_todo_count: active_todo_count, active_todo_text: active_todo_text, @@ -83,7 +83,7 @@ pub fn app(cx: Scope<()>) -> Element { todos: todos, filter: filter, } - )) + } } } PageFooter {} @@ -172,7 +172,7 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element { prevent_default: "onclick" } } - is_editing.then(|| rsx!{ + if **is_editing { input { class: "edit", value: "{todo.contents}", @@ -186,7 +186,7 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element { } }, } - }) + } } }) } @@ -220,29 +220,27 @@ pub fn ListFooter<'a>(cx: Scope<'a, ListFooterProps<'a>>) -> Element { } ul { class: "filters", for (state , state_text , url) in [ - (FilterState::All, "All", "#/"), - (FilterState::Active, "Active", "#/active"), - (FilterState::Completed, "Completed", "#/completed"), -] { + (FilterState::All, "All", "#/"), + (FilterState::Active, "Active", "#/active"), + (FilterState::Completed, "Completed", "#/completed"), + ] { li { a { href: url, class: selected(state), onclick: move |_| cx.props.filter.set(state), prevent_default: "onclick", - state_text + {state_text} } } } } if cx.props.show_clear_completed { - cx.render(rsx! { - button { - class: "clear-completed", - onclick: move |_| cx.props.todos.make_mut().retain(|_, todo| !todo.checked), - "Clear completed" - } - }) + button { + class: "clear-completed", + onclick: move |_| cx.props.todos.make_mut().retain(|_, todo| !todo.checked), + "Clear completed" + } } } }) diff --git a/packages/autofmt/src/writer.rs b/packages/autofmt/src/writer.rs index d569cdb9f..c7bf300d5 100644 --- a/packages/autofmt/src/writer.rs +++ b/packages/autofmt/src/writer.rs @@ -1,11 +1,11 @@ -use dioxus_rsx::{AttributeType, BodyNode, ElementAttrValue, ForLoop}; +use dioxus_rsx::{AttributeType, BodyNode, ElementAttrValue, ForLoop, IfChain}; use proc_macro2::{LineColumn, Span}; use quote::ToTokens; use std::{ collections::{HashMap, VecDeque}, fmt::{Result, Write}, }; -use syn::{spanned::Spanned, Expr, ExprIf}; +use syn::{spanned::Spanned, Expr}; use crate::buffer::Buffer; use crate::ifmt_to_string; @@ -231,8 +231,9 @@ impl<'a> Writer<'a> { Ok(()) } - fn write_if_chain(&mut self, ifchain: &ExprIf) -> std::fmt::Result { - self.write_raw_expr(ifchain.span()) + fn write_if_chain(&mut self, ifchain: &IfChain) -> std::fmt::Result { + todo!() + // self.write_raw_expr(ifchain.span()) } } diff --git a/packages/core/tests/create_dom.rs b/packages/core/tests/create_dom.rs index 5e5005d3f..eb8b26b3c 100644 --- a/packages/core/tests/create_dom.rs +++ b/packages/core/tests/create_dom.rs @@ -84,7 +84,7 @@ fn create() { fn create_list() { let mut dom = VirtualDom::new(|cx| { cx.render(rsx! { - (0..3).map(|f| rsx!( div { "hello" } )) + {(0..3).map(|f| rsx!( div { "hello" } ))} }) }); @@ -148,7 +148,7 @@ fn create_components() { fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element { cx.render(rsx! { h1 {} - div { &cx.props.children } + div { {&cx.props.children} } p {} }) } @@ -163,10 +163,10 @@ fn anchors() { let mut dom = VirtualDom::new(|cx| { cx.render(rsx! { if true { - rsx!( div { "hello" } ) + div { "hello" } } if false { - rsx!( div { "goodbye" } ) + div { "goodbye" } } }) }); diff --git a/packages/core/tests/create_fragments.rs b/packages/core/tests/create_fragments.rs index ee312f34b..fff90464a 100644 --- a/packages/core/tests/create_fragments.rs +++ b/packages/core/tests/create_fragments.rs @@ -7,7 +7,7 @@ use dioxus_core::ElementId; #[test] fn empty_fragment_creates_nothing() { fn app(cx: Scope) -> Element { - cx.render(rsx!(())) + cx.render(rsx!({ () })) } let mut vdom = VirtualDom::new(app); @@ -43,18 +43,18 @@ fn fragments_nested() { cx.render(rsx!( div { "hello" } div { "goodbye" } - rsx! { + {rsx! { div { "hello" } div { "goodbye" } - rsx! { + {rsx! { div { "hello" } div { "goodbye" } - rsx! { + {rsx! { div { "hello" } div { "goodbye" } - } - } - } + }} + }} + }} )) }); @@ -79,7 +79,7 @@ fn fragments_across_components() { let world = "world"; cx.render(rsx! { "hellO!" - world + {world} }) } @@ -94,7 +94,7 @@ fn list_fragments() { fn app(cx: Scope) -> Element { cx.render(rsx!( h1 {"hello"} - (0..6).map(|f| rsx!( span { "{f}" })) + {(0..6).map(|f| rsx!( span { "{f}" }))} )) } assert_eq!( diff --git a/packages/core/tests/create_lists.rs b/packages/core/tests/create_lists.rs index ec3e8ecf2..9a237ae90 100644 --- a/packages/core/tests/create_lists.rs +++ b/packages/core/tests/create_lists.rs @@ -11,12 +11,12 @@ use dioxus_core::ElementId; fn app(cx: Scope) -> Element { cx.render(rsx! { div { - (0..3).map(|i| rsx! { + for i in 0..3 { div { h1 { "hello world! "} p { "{i}" } } - }) + } } }) } diff --git a/packages/core/tests/create_passthru.rs b/packages/core/tests/create_passthru.rs index 4311cf1b1..704e92cf0 100644 --- a/packages/core/tests/create_passthru.rs +++ b/packages/core/tests/create_passthru.rs @@ -20,7 +20,7 @@ fn nested_passthru_creates() { #[component] fn PassThru<'a>(cx: Scope<'a>, children: Element<'a>) -> Element { - cx.render(rsx!(children)) + cx.render(rsx!({ children })) } let mut dom = VirtualDom::new(App); @@ -60,7 +60,7 @@ fn nested_passthru_creates_add() { #[component] fn ChildComp<'a>(cx: Scope, children: Element<'a>) -> Element { - cx.render(rsx! { children }) + cx.render(rsx! { {children} }) } let mut dom = VirtualDom::new(App); diff --git a/packages/core/tests/diff_component.rs b/packages/core/tests/diff_component.rs index 4766ad378..5388bf6c1 100644 --- a/packages/core/tests/diff_component.rs +++ b/packages/core/tests/diff_component.rs @@ -41,7 +41,7 @@ fn component_swap() { cx.render(rsx! { h1 { "NavBar" - (0..3).map(|_| rsx!(nav_link {})) + {(0..3).map(|_| rsx!(nav_link {}))} } }) } diff --git a/packages/core/tests/diff_keyed_list.rs b/packages/core/tests/diff_keyed_list.rs index ed14c2d95..d6ad15207 100644 --- a/packages/core/tests/diff_keyed_list.rs +++ b/packages/core/tests/diff_keyed_list.rs @@ -17,7 +17,7 @@ fn keyed_diffing_out_of_order() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); { @@ -59,7 +59,7 @@ fn keyed_diffing_out_of_order_adds() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -85,7 +85,7 @@ fn keyed_diffing_out_of_order_adds_3() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -111,7 +111,7 @@ fn keyed_diffing_out_of_order_adds_4() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -137,7 +137,7 @@ fn keyed_diffing_out_of_order_adds_5() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -162,7 +162,7 @@ fn keyed_diffing_additions() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -187,7 +187,7 @@ fn keyed_diffing_additions_and_moves_on_ends() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -216,7 +216,7 @@ fn keyed_diffing_additions_and_moves_in_middle() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -250,7 +250,7 @@ fn controlled_keyed_diffing_out_of_order() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -284,7 +284,7 @@ fn controlled_keyed_diffing_out_of_order_max_test() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -313,7 +313,7 @@ fn remove_list() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); @@ -338,7 +338,7 @@ fn no_common_keys() { _ => unreachable!(), }; - cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" })))) + cx.render(rsx!({ order.iter().map(|i| rsx!(div { key: "{i}" })) })) }); _ = dom.rebuild(); diff --git a/packages/core/tests/diff_unkeyed_list.rs b/packages/core/tests/diff_unkeyed_list.rs index b5be161ef..22c86fb70 100644 --- a/packages/core/tests/diff_unkeyed_list.rs +++ b/packages/core/tests/diff_unkeyed_list.rs @@ -9,9 +9,9 @@ fn list_creates_one_by_one() { cx.render(rsx! { div { - (0..gen).map(|i| rsx! { + for i in 0..gen { div { "{i}" } - }) + } } }) }); @@ -78,9 +78,9 @@ fn removes_one_by_one() { cx.render(rsx! { div { - (0..gen).map(|i| rsx! { + for i in 0..gen { div { "{i}" } - }) + } } }) }); @@ -153,10 +153,10 @@ fn list_shrink_multiroot() { let mut dom = VirtualDom::new(|cx| { cx.render(rsx! { div { - (0..cx.generation()).map(|i| rsx! { + for i in 0..cx.generation() { div { "{i}" } div { "{i}" } - }) + } } }) }); @@ -214,10 +214,10 @@ fn removes_one_by_one_multiroot() { cx.render(rsx! { div { - (0..gen).map(|i| rsx! { + {(0..gen).map(|i| rsx! { div { "{i}" } div { "{i}" } - }) + })} } }) }); @@ -276,9 +276,9 @@ fn removes_one_by_one_multiroot() { fn two_equal_fragments_are_equal_static() { let mut dom = VirtualDom::new(|cx| { cx.render(rsx! { - (0..5).map(|_| rsx! { + for _ in 0..5 { div { "hello" } - }) + } }) }); @@ -290,9 +290,9 @@ fn two_equal_fragments_are_equal_static() { fn two_equal_fragments_are_equal() { let mut dom = VirtualDom::new(|cx| { cx.render(rsx! { - (0..5).map(|i| rsx! { + for i in 0..5 { div { "hello {i}" } - }) + } }) }); @@ -311,7 +311,9 @@ fn remove_many() { }; cx.render(rsx! { - (0..num).map(|i| rsx! { div { "hello {i}" } }) + for i in 0..num { + div { "hello {i}" } + } }) }); diff --git a/packages/core/tests/event_propagation.rs b/packages/core/tests/event_propagation.rs index fb87bb16a..8878f1c59 100644 --- a/packages/core/tests/event_propagation.rs +++ b/packages/core/tests/event_propagation.rs @@ -58,11 +58,11 @@ fn app(cx: Scope) -> Element { *CLICKS.lock().unwrap() += 1; }, - vec![ + {vec![ render! { problematic_child {} } - ].into_iter() + ].into_iter()} } } } diff --git a/packages/core/tests/lifecycle.rs b/packages/core/tests/lifecycle.rs index 6db294653..c03e99124 100644 --- a/packages/core/tests/lifecycle.rs +++ b/packages/core/tests/lifecycle.rs @@ -51,7 +51,7 @@ fn events_generate() { "Click me!" } }), - _ => cx.render(rsx!(())), + _ => None, } }; diff --git a/packages/core/tests/miri_full_app.rs b/packages/core/tests/miri_full_app.rs index c285db7d5..91cef719f 100644 --- a/packages/core/tests/miri_full_app.rs +++ b/packages/core/tests/miri_full_app.rs @@ -39,9 +39,9 @@ fn App(cx: Scope) -> Element { } button { onclick: move |_| idx -= 1, "-" } ul { - (0..**idx).map(|i| rsx! { + {(0..**idx).map(|i| rsx! { ChildExample { i: i, onhover: onhover } - }) + })} } } }) diff --git a/packages/core/tests/miri_stress.rs b/packages/core/tests/miri_stress.rs index ff8d69788..5ddc7d817 100644 --- a/packages/core/tests/miri_stress.rs +++ b/packages/core/tests/miri_stress.rs @@ -15,7 +15,7 @@ fn test_memory_leak() { cx.spawn(async {}); if val == 2 || val == 4 { - return cx.render(rsx!(())); + return None; } let name = cx.use_hook(|| String::from("numbers: ")); @@ -73,7 +73,7 @@ fn memo_works_properly() { let val = cx.generation(); if val == 2 || val == 4 { - return cx.render(rsx!(())); + return None; } let name = cx.use_hook(|| String::from("asd")); diff --git a/packages/core/tests/task.rs b/packages/core/tests/task.rs index e1ff55c85..256e98c21 100644 --- a/packages/core/tests/task.rs +++ b/packages/core/tests/task.rs @@ -25,7 +25,7 @@ async fn it_works() { }); }); - cx.render(rsx!(())) + None } let mut dom = VirtualDom::new(app); diff --git a/packages/desktop/headless_tests/rendering.rs b/packages/desktop/headless_tests/rendering.rs index 50d7b8419..9fa080be8 100644 --- a/packages/desktop/headless_tests/rendering.rs +++ b/packages/desktop/headless_tests/rendering.rs @@ -89,7 +89,7 @@ fn check_html_renders(cx: Scope) -> Element { h1 { "text" } - dyn_element + {dyn_element} } } } diff --git a/packages/dioxus-tui/examples/all_terminal_events.rs b/packages/dioxus-tui/examples/all_terminal_events.rs index 004578807..f58c9710f 100644 --- a/packages/dioxus-tui/examples/all_terminal_events.rs +++ b/packages/dioxus-tui/examples/all_terminal_events.rs @@ -71,7 +71,7 @@ fn app(cx: Scope) -> Element { "Hover, click, type or scroll to see the info down below" } - div { width: "80%", height: "50%", flex_direction: "column", events_rendered } + div { width: "80%", height: "50%", flex_direction: "column", {events_rendered} } } }) } diff --git a/packages/dioxus-tui/examples/buttons.rs b/packages/dioxus-tui/examples/buttons.rs index f40a88434..b8643b5a8 100644 --- a/packages/dioxus-tui/examples/buttons.rs +++ b/packages/dioxus-tui/examples/buttons.rs @@ -27,26 +27,25 @@ fn Button(cx: Scope) -> Element { height: "100%", background_color: "{color}", tabindex: "{cx.props.layer}", - onkeydown: |e| { + onkeydown: move |e| { if let Code::Space = e.inner().code() { toggle.modify(|f| !f); } }, - onclick: |_| { + onclick: move |_| { toggle.modify(|f| !f); }, - onmouseenter: |_|{ + onmouseenter: move |_| { hovered.set(true); }, - onmouseleave: |_|{ + onmouseleave: move |_|{ hovered.set(false); }, justify_content: "center", align_items: "center", display: "flex", flex_direction: "column", - - p{"tabindex: {cx.props.layer}"} + p{ "tabindex: {cx.props.layer}" } } }) } @@ -58,37 +57,28 @@ fn app(cx: Scope) -> Element { flex_direction: "column", width: "100%", height: "100%", - - (1..8).map(|y| - rsx!{ - div{ - display: "flex", - flex_direction: "row", - width: "100%", - height: "100%", - (1..8).map(|x|{ - if (x + y) % 2 == 0{ - rsx!{ - div{ - width: "100%", - height: "100%", - background_color: "rgb(100, 100, 100)", - } - } + for y in 1..8 { + div { + display: "flex", + flex_direction: "row", + width: "100%", + height: "100%", + for x in 1..8 { + if (x + y) % 2 == 0 { + div { + width: "100%", + height: "100%", + background_color: "rgb(100, 100, 100)", } - else{ - let layer = (x + y) % 3; - rsx!{ - Button{ - color_offset: x * y, - layer: layer as u16, - } - } + } else { + Button { + color_offset: x * y, + layer: ((x + y) % 3) as u16, } - }) + } } } - ) + } } }) } diff --git a/packages/dioxus-tui/examples/color_test.rs b/packages/dioxus-tui/examples/color_test.rs index 872fec53e..90cbcb7c8 100644 --- a/packages/dioxus-tui/examples/color_test.rs +++ b/packages/dioxus-tui/examples/color_test.rs @@ -14,32 +14,25 @@ fn app(cx: Scope) -> Element { width: "100%", height: "100%", flex_direction: "column", - (0..=steps).map(|x| - { - let hue = x as f32*360.0/steps as f32; - rsx! { - div{ - width: "100%", - height: "100%", - flex_direction: "row", - (0..=steps).map(|y| - { - let alpha = y as f32*100.0/steps as f32; - rsx! { - div { - left: "{x}px", - top: "{y}px", - width: "10%", - height: "100%", - background_color: "hsl({hue}, 100%, 50%, {alpha}%)", - } - } + for x in 0..=steps { + div { width: "100%", height: "100%", flex_direction: "row", + for y in 0..=steps { + { + let hue = x as f32*360.0/steps as f32; + let alpha = y as f32*100.0/steps as f32; + rsx! { + div { + left: "{x}px", + top: "{y}px", + width: "10%", + height: "100%", + background_color: "hsl({hue}, 100%, 50%, {alpha}%)", } - ) + } } } } - ) + } } }) } diff --git a/packages/dioxus-tui/examples/list.rs b/packages/dioxus-tui/examples/list.rs index 5f41de399..4ecc05e85 100644 --- a/packages/dioxus-tui/examples/list.rs +++ b/packages/dioxus-tui/examples/list.rs @@ -19,9 +19,9 @@ fn app(cx: Scope) -> Element { ul { flex_direction: "column", padding_left: "3px", - (0..10).map(|i| rsx!( + for i in 0..10 { "> hello {i}" - )) + } } } }) diff --git a/packages/dioxus-tui/examples/many_small_edit_stress.rs b/packages/dioxus-tui/examples/many_small_edit_stress.rs index 5f9d9d76f..b783dd383 100644 --- a/packages/dioxus-tui/examples/many_small_edit_stress.rs +++ b/packages/dioxus-tui/examples/many_small_edit_stress.rs @@ -71,32 +71,28 @@ fn Grid(cx: Scope) -> Element { width: "100%", height: "100%", flex_direction: "column", - (0..size).map(|x| - { - rsx! { - div{ - width: "100%", - height: "100%", - flex_direction: "row", - (0..size).map(|y| - { - let alpha = y as f32*100.0/size as f32 + counts.read()[x*size + y] as f32; - let key = format!("{}-{}", x, y); - rsx! { - Box { - x: x, - y: y, - alpha: 100.0, - hue: alpha, - key: "{key}", - } - } + for x in 0..size { + div{ + width: "100%", + height: "100%", + flex_direction: "row", + for y in 0..size { + { + let alpha = y as f32*100.0/size as f32 + counts.read()[x*size + y] as f32; + let key = format!("{}-{}", x, y); + rsx! { + Box { + x: x, + y: y, + alpha: 100.0, + hue: alpha, + key: "{key}", } - ) + } } } } - ) + } } } } diff --git a/packages/dioxus/examples/stress.rs b/packages/dioxus/examples/stress.rs index 248d851d2..536b13bd0 100644 --- a/packages/dioxus/examples/stress.rs +++ b/packages/dioxus/examples/stress.rs @@ -17,10 +17,12 @@ fn app(cx: Scope) -> Element { cx.render(rsx! ( table { tbody { - (0..10_000_usize).map(|f| { - let label = Label::new(&mut rng); - rsx!( table_row { row_id: f, label: label } ) - }) + for f in 0..10_000_usize { + table_row { + row_id: f, + label: Label::new(&mut rng) + } + } } } )) diff --git a/packages/native-core/src/utils/persistant_iterator.rs b/packages/native-core/src/utils/persistant_iterator.rs index 2442af29f..dd328e160 100644 --- a/packages/native-core/src/utils/persistant_iterator.rs +++ b/packages/native-core/src/utils/persistant_iterator.rs @@ -306,15 +306,10 @@ fn persist_removes() { _ => unreachable!(), }; render!( - div{ - (0..children).map(|i|{ - rsx!{ - p{ - key: "{i}", - "{i}" - } - } - }) + div { + for i in 0..children { + p { key: "{i}", "{i}" } + } } ) } @@ -387,15 +382,10 @@ fn persist_instertions_before() { _ => unreachable!(), }; render!( - div{ - (0..children).map(|i|{ - rsx!{ - p{ - key: "{i}", - "{i}" - } - } - }) + div { + for i in 0..children { + p { key: "{i}", "{i}" } + } } ) } @@ -446,14 +436,9 @@ fn persist_instertions_after() { }; render!( div{ - (0..children).map(|i|{ - rsx!{ - p{ - key: "{i}", - "{i}" - } - } - }) + for i in 0..children { + p { key: "{i}", "{i}" } + } } ) } diff --git a/packages/router/examples/simple_routes.rs b/packages/router/examples/simple_routes.rs index 9317f0f36..e7dc2289a 100644 --- a/packages/router/examples/simple_routes.rs +++ b/packages/router/examples/simple_routes.rs @@ -103,7 +103,7 @@ fn Route1(cx: Scope, user_id: usize, dynamic: usize, query: String, extra: Strin fn Route2(cx: Scope, user_id: usize) -> Element { render! { pre { "Route2{{\n\tuser_id:{user_id}\n}}" } - (0..*user_id).map(|i| rsx!{ p { "{i}" } }), + {(0..*user_id).map(|i| rsx!{ p { "{i}" } })}, p { "Footer" } Link { to: Route::Route3 { diff --git a/packages/router/src/components/history_buttons.rs b/packages/router/src/components/history_buttons.rs index ae11ad276..39c7a63a4 100644 --- a/packages/router/src/components/history_buttons.rs +++ b/packages/router/src/components/history_buttons.rs @@ -77,7 +77,7 @@ pub fn GoBackButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element { disabled: "{disabled}", prevent_default: "onclick", onclick: move |_| router.go_back(), - children + {children} } } } @@ -149,7 +149,7 @@ pub fn GoForwardButton<'a>(cx: Scope<'a, HistoryButtonProps<'a>>) -> Element { disabled: "{disabled}", prevent_default: "onclick", onclick: move |_| router.go_forward(), - children + {children} } } } diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index 639d99ae7..b0e7a9f13 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -167,7 +167,7 @@ impl Debug for LinkProps<'_> { /// new_tab: true, /// rel: "link_rel", /// to: Route::Index {}, -/// +/// /// "A fully configured link" /// } /// } @@ -250,7 +250,7 @@ pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element { id: "{id}", rel: "{rel}", target: "{tag_target}", - children + {children} } } } diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index c71611082..a6f0aea06 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -6,7 +6,8 @@ use syn::{ braced, parse::{Parse, ParseStream}, spanned::Spanned, - token, Expr, ExprIf, LitStr, Pat, Result, + token::{self, Brace}, + Expr, ExprIf, LitStr, Pat, Result, }; /* @@ -15,14 +16,14 @@ Parse -> Component {} -> component() -> "text {with_args}" --> (0..10).map(|f| rsx!("asd")), // <--- notice the comma - must be a complete expr +-> {(0..10).map(|f| rsx!("asd"))} // <--- notice the curly braces */ #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub enum BodyNode { Element(Element), Component(Component), ForLoop(ForLoop), - IfChain(ExprIf), + IfChain(IfChain), Text(IfmtInput), RawExpr(Expr), } @@ -112,7 +113,27 @@ impl Parse for BodyNode { return Ok(BodyNode::IfChain(stream.parse()?)); } - Ok(BodyNode::RawExpr(stream.parse::()?)) + // Match statements are special but have no special arm syntax + // we could allow arm syntax if we wanted + // + // ``` + // match { + // val => div {} + // other_val => div {} + // } + // ``` + if stream.peek(Token![match]) { + return Ok(BodyNode::RawExpr(stream.parse::()?)); + } + + if stream.peek(token::Brace) { + return Ok(BodyNode::RawExpr(stream.parse::()?)); + } + + Err(syn::Error::new( + stream.span(), + "Expected a valid body node.\nExpressions must be wrapped in curly braces.", + )) } } @@ -151,71 +172,52 @@ impl ToTokens for BodyNode { }) } BodyNode::IfChain(chain) => { - if is_if_chain_terminated(chain) { - tokens.append_all(quote! { - { - let ___nodes = (#chain).into_dyn_node(__cx); - ___nodes - } - }); - } else { - let ExprIf { + let mut body = TokenStream2::new(); + let mut terminated = false; + + let mut elif = Some(chain); + + while let Some(chain) = elif { + let IfChain { + if_token, cond, then_branch, + else_if_branch, else_branch, - .. } = chain; - let mut body = TokenStream2::new(); + let mut renderer: TemplateRenderer = TemplateRenderer { + roots: then_branch, + location: None, + }; - body.append_all(quote! { - if #cond { - Some(#then_branch) - } - }); + body.append_all(quote! { #if_token #cond { Some({#renderer}) } }); - let mut elif = else_branch; - - while let Some((_, ref branch)) = elif { - match branch.as_ref() { - Expr::If(ref eelif) => { - let ExprIf { - cond, - then_branch, - else_branch, - .. - } = eelif; - - body.append_all(quote! { - else if #cond { - Some(#then_branch) - } - }); - - elif = else_branch; - } - _ => { - body.append_all(quote! { - else { - #branch - } - }); - break; - } - } + if let Some(next) = else_if_branch { + body.append_all(quote! { else }); + elif = Some(next); + } else if let Some(else_branch) = else_branch { + renderer.roots = else_branch; + body.append_all(quote! { else { Some({#renderer}) } }); + terminated = true; + break; + } else { + elif = None; } + } + if !terminated { body.append_all(quote! { else { None } }); - - tokens.append_all(quote! { - { - let ___nodes = (#body).into_dyn_node(__cx); - ___nodes - } - }); } + + tokens.append_all(quote! { + { + let ___nodes = (#body).into_dyn_node(__cx); + ___nodes + } + }); } } } @@ -240,26 +242,73 @@ impl Parse for ForLoop { let in_token: Token![in] = input.parse()?; let expr: Expr = input.call(Expr::parse_without_eager_brace)?; - let content; - let brace_token = braced!(content in input); - - let mut children = vec![]; - - while !content.is_empty() { - children.push(content.parse()?); - } + let (brace_token, body) = parse_buffer_as_braced_children(input)?; Ok(Self { for_token, pat, in_token, - body: children, - expr: Box::new(expr), + body, brace_token, + expr: Box::new(expr), }) } } +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub struct IfChain { + pub if_token: Token![if], + pub cond: Box, + pub then_branch: Vec, + pub else_if_branch: Option>, + pub else_branch: Option>, +} + +impl Parse for IfChain { + fn parse(input: ParseStream) -> Result { + let if_token: Token![if] = input.parse()?; + + // stolen from ExprIf + let cond = Box::new(input.call(Expr::parse_without_eager_brace)?); + + let (_, then_branch) = parse_buffer_as_braced_children(input)?; + + let mut else_branch = None; + let mut else_if_branch = None; + + // if the next token is `else`, set the else branch as the next if chain + if input.peek(Token![else]) { + input.parse::()?; + if input.peek(Token![if]) { + else_if_branch = Some(Box::new(input.parse::()?)); + } else { + let (_, else_branch_nodes) = parse_buffer_as_braced_children(input)?; + else_branch = Some(else_branch_nodes); + } + } + + Ok(Self { + cond, + if_token, + then_branch, + else_if_branch, + else_branch, + }) + } +} + +fn parse_buffer_as_braced_children( + input: &syn::parse::ParseBuffer<'_>, +) -> Result<(Brace, Vec)> { + let content; + let brace_token = braced!(content in input); + let mut then_branch = vec![]; + while !content.is_empty() { + then_branch.push(content.parse()?); + } + Ok((brace_token, then_branch)) +} + pub(crate) fn is_if_chain_terminated(chain: &ExprIf) -> bool { let mut current = chain; loop { diff --git a/packages/ssr/src/renderer.rs b/packages/ssr/src/renderer.rs index 65ab4d0ed..6e3229de7 100644 --- a/packages/ssr/src/renderer.rs +++ b/packages/ssr/src/renderer.rs @@ -242,7 +242,9 @@ fn to_string_works() { div {} div { "nest 2" } "{dyn2}" - (0..5).map(|i| rsx! { div { "finalize {i}" } }) + for i in (0..5) { + div { "finalize {i}" } + } } } } diff --git a/packages/ssr/tests/hydration.rs b/packages/ssr/tests/hydration.rs index 8c9e9f5c8..b8d1912bf 100644 --- a/packages/ssr/tests/hydration.rs +++ b/packages/ssr/tests/hydration.rs @@ -70,7 +70,7 @@ fn text_nodes() { fn app(cx: Scope) -> Element { let dynamic_text = "hello"; render! { - div { dynamic_text } + div { {dynamic_text} } } } @@ -124,7 +124,7 @@ fn components_hydrate() { fn Child2(cx: Scope) -> Element { let dyn_text = "hello"; render! { - div { dyn_text } + div { {dyn_text} } } } @@ -159,11 +159,7 @@ fn components_hydrate() { fn Child4(cx: Scope) -> Element { render! { for _ in 0..2 { - render! { - render! { - "{1}" - } - } + {render! { "{1}" }} } } } diff --git a/packages/ssr/tests/simple.rs b/packages/ssr/tests/simple.rs index a9034df9b..9e4a2e2b0 100644 --- a/packages/ssr/tests/simple.rs +++ b/packages/ssr/tests/simple.rs @@ -23,9 +23,9 @@ fn lists() { assert_eq!( dioxus_ssr::render_lazy(rsx! { ul { - (0..5).map(|i| rsx! { + for i in 0..5 { li { "item {i}" } - }) + } } }), "
  • item 0
  • item 1
  • item 2
  • item 3
  • item 4
" @@ -53,9 +53,9 @@ fn components() { assert_eq!( dioxus_ssr::render_lazy(rsx! { div { - (0..5).map(|name| rsx! { + for name in 0..5 { MyComponent { name: name } - }) + } } }), "
component 0
component 1
component 2
component 3
component 4
" @@ -67,7 +67,9 @@ fn fragments() { assert_eq!( dioxus_ssr::render_lazy(rsx! { div { - (0..5).map(|_| rsx! (())) + for _ in 0..5 { + {()} + } } }), "
" diff --git a/packages/web/examples/hydrate.rs b/packages/web/examples/hydrate.rs index 8a6cd464e..f11b43032 100644 --- a/packages/web/examples/hydrate.rs +++ b/packages/web/examples/hydrate.rs @@ -12,11 +12,11 @@ fn app(cx: Scope) -> Element { "asd" Bapp {} } - (0..10).map(|f| rsx!{ + {(0..10).map(|f| rsx!{ div { "thing {f}" } - }) + })} }) } diff --git a/packages/web/examples/timeout_count.rs b/packages/web/examples/timeout_count.rs index 742e6a49e..5647e92d7 100644 --- a/packages/web/examples/timeout_count.rs +++ b/packages/web/examples/timeout_count.rs @@ -24,8 +24,11 @@ fn app(cx: Scope) -> Element { start(); *count.write() += 1; }, - // format is needed as {count} does not seemed to work in `if` within content - if **started { format!("Current score: {}", count.write()) } else { "Start".to_string() } + if **started { + "Current score: {count.read()}" + } else { + "Start" + } } }) } diff --git a/packages/web/tests/hydrate.rs b/packages/web/tests/hydrate.rs index c7de26a8c..d86cd07da 100644 --- a/packages/web/tests/hydrate.rs +++ b/packages/web/tests/hydrate.rs @@ -43,7 +43,7 @@ fn rehydrates() { }, "listener test" } - false.then(|| rsx!{ "hello" }) + {false.then(|| rsx!{ "hello" })} } }) }