From 260589cd7fe962b2b2b3e78aca182e626b0c73f8 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 12 Jan 2023 15:53:33 -0600 Subject: [PATCH 01/10] pass clippy --- packages/core/tests/fuzzing.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/core/tests/fuzzing.rs b/packages/core/tests/fuzzing.rs index 949a0b941..be8505413 100644 --- a/packages/core/tests/fuzzing.rs +++ b/packages/core/tests/fuzzing.rs @@ -83,7 +83,7 @@ fn create_random_template_node( fn generate_paths( node: &TemplateNode<'static>, - current_path: &Vec, + current_path: &[u8], node_paths: &mut Vec>, attr_paths: &mut Vec>, ) { @@ -93,22 +93,22 @@ fn generate_paths( match attr { TemplateAttribute::Static { .. } => {} TemplateAttribute::Dynamic { .. } => { - attr_paths.push(current_path.clone()); + attr_paths.push(current_path.to_vec()); } } } for (i, child) in children.iter().enumerate() { - let mut current_path = current_path.clone(); + let mut current_path = current_path.to_vec(); current_path.push(i as u8); generate_paths(child, ¤t_path, node_paths, attr_paths); } } TemplateNode::Text { .. } => {} TemplateNode::DynamicText { .. } => { - node_paths.push(current_path.clone()); + node_paths.push(current_path.to_vec()); } TemplateNode::Dynamic { .. } => { - node_paths.push(current_path.clone()); + node_paths.push(current_path.to_vec()); } } } @@ -119,12 +119,12 @@ fn create_random_template(name: &'static str) -> Template<'static> { let roots = (0..(1 + rand::random::() % 5)) .map(|_| create_random_template_node(&mut template_idx, &mut attr_idx, 0)) .collect::>(); - assert!(roots.len() > 0); + assert!(!roots.is_empty()); let roots = Box::leak(roots.into_boxed_slice()); let mut node_paths = Vec::new(); let mut attr_paths = Vec::new(); for (i, root) in roots.iter().enumerate() { - generate_paths(root, &vec![i as u8], &mut node_paths, &mut attr_paths); + generate_paths(root, &[i as u8], &mut node_paths, &mut attr_paths); } let node_paths = Box::leak( node_paths @@ -143,7 +143,7 @@ fn create_random_template(name: &'static str) -> Template<'static> { Template { name, roots, node_paths, attr_paths } } -fn create_random_dynamic_node<'a>(cx: &'a ScopeState, depth: usize) -> DynamicNode<'a> { +fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode { let range = if depth > 10 { 2 } else { 4 }; match rand::random::() % range { 0 => DynamicNode::Text(VText { @@ -177,7 +177,7 @@ fn create_random_dynamic_node<'a>(cx: &'a ScopeState, depth: usize) -> DynamicNo } } -fn create_random_dynamic_attr<'a>(cx: &'a ScopeState) -> Attribute<'a> { +fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute { let value = match rand::random::() % 6 { 0 => AttributeValue::Text(Box::leak( format!("{}", rand::random::()).into_boxed_str(), @@ -207,7 +207,7 @@ struct DepthProps { root: bool, } -fn create_random_element<'a>(cx: Scope<'a, DepthProps>) -> Element<'a> { +fn create_random_element(cx: Scope) -> Element { cx.needs_update(); let range = if cx.props.root { 2 } else { 3 }; let node = match rand::random::() % range { @@ -226,7 +226,7 @@ fn create_random_element<'a>(cx: Scope<'a, DepthProps>) -> Element<'a> { ) .into_boxed_str(), )); - println!("{:#?}", template); + println!("{template:#?}"); let node = VNode { key: None, parent: None, @@ -248,7 +248,7 @@ fn create_random_element<'a>(cx: Scope<'a, DepthProps>) -> Element<'a> { } _ => None, }; - println!("{:#?}", node); + println!("{node:#?}"); node } From a01a65a0ebe2a78fa0215d605df3c13760701c31 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 12 Jan 2023 16:34:46 -0600 Subject: [PATCH 02/10] seperate text and other dynamic nodes --- packages/core/tests/fuzzing.rs | 64 ++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/packages/core/tests/fuzzing.rs b/packages/core/tests/fuzzing.rs index be8505413..75dd84c33 100644 --- a/packages/core/tests/fuzzing.rs +++ b/packages/core/tests/fuzzing.rs @@ -32,6 +32,7 @@ fn create_random_attribute(attr_idx: &mut usize) -> TemplateAttribute<'static> { } fn create_random_template_node( + dynamic_node_types: &mut Vec, template_idx: &mut usize, attr_idx: &mut usize, depth: usize, @@ -49,11 +50,18 @@ fn create_random_template_node( namespace: random_ns(), attrs, children: { - if depth > 10 { + if depth > 4 { &[] } else { let children: Vec<_> = (0..(rand::random::() % 3)) - .map(|_| create_random_template_node(template_idx, attr_idx, depth + 1)) + .map(|_| { + create_random_template_node( + dynamic_node_types, + template_idx, + attr_idx, + depth + 1, + ) + }) .collect(); Box::leak(children.into_boxed_slice()) } @@ -67,6 +75,7 @@ fn create_random_template_node( id: { let old_idx = *template_idx; *template_idx += 1; + dynamic_node_types.push(DynamicNodeType::Text); old_idx }, }, @@ -74,6 +83,7 @@ fn create_random_template_node( id: { let old_idx = *template_idx; *template_idx += 1; + dynamic_node_types.push(DynamicNodeType::Other); old_idx }, }, @@ -113,11 +123,19 @@ fn generate_paths( } } -fn create_random_template(name: &'static str) -> Template<'static> { +enum DynamicNodeType { + Text, + Other, +} + +fn create_random_template(name: &'static str) -> (Template<'static>, Vec) { + let mut dynamic_node_type = Vec::new(); let mut template_idx = 0; let mut attr_idx = 0; let roots = (0..(1 + rand::random::() % 5)) - .map(|_| create_random_template_node(&mut template_idx, &mut attr_idx, 0)) + .map(|_| { + create_random_template_node(&mut dynamic_node_type, &mut template_idx, &mut attr_idx, 0) + }) .collect::>(); assert!(!roots.is_empty()); let roots = Box::leak(roots.into_boxed_slice()); @@ -140,18 +158,17 @@ fn create_random_template(name: &'static str) -> Template<'static> { .collect::>() .into_boxed_slice(), ); - Template { name, roots, node_paths, attr_paths } + ( + Template { name, roots, node_paths, attr_paths }, + dynamic_node_type, + ) } fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode { - let range = if depth > 10 { 2 } else { 4 }; + let range = if depth > 3 { 1 } else { 3 }; match rand::random::() % range { - 0 => DynamicNode::Text(VText { - value: Box::leak(format!("{}", rand::random::()).into_boxed_str()), - id: Default::default(), - }), - 1 => DynamicNode::Placeholder(Default::default()), - 2 => cx.make_node((0..(rand::random::() % 5)).map(|_| VNode { + 0 => DynamicNode::Placeholder(Default::default()), + 1 => cx.make_node((0..(rand::random::() % 5)).map(|_| VNode { key: None, parent: Default::default(), template: Cell::new(Template { @@ -168,7 +185,7 @@ fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode { )]), dynamic_attrs: &[], })), - 3 => cx.component( + 2 => cx.component( create_random_element, DepthProps { depth, root: false }, "create_random_element", @@ -212,7 +229,7 @@ fn create_random_element(cx: Scope) -> Element { let range = if cx.props.root { 2 } else { 3 }; let node = match rand::random::() % range { 0 | 1 => { - let template = create_random_template(Box::leak( + let (template, dynamic_node_types) = create_random_template(Box::leak( format!( "{}{}", concat!(file!(), ":", line!(), ":", column!(), ":"), @@ -233,8 +250,19 @@ fn create_random_element(cx: Scope) -> Element { template: Cell::new(template), root_ids: Default::default(), dynamic_nodes: { - let dynamic_nodes: Vec<_> = (0..template.node_paths.len()) - .map(|_| create_random_dynamic_node(cx, cx.props.depth + 1)) + let dynamic_nodes: Vec<_> = dynamic_node_types + .iter() + .map(|ty| match ty { + DynamicNodeType::Text => DynamicNode::Text(VText { + value: Box::leak( + format!("{}", rand::random::()).into_boxed_str(), + ), + id: Default::default(), + }), + DynamicNodeType::Other => { + create_random_dynamic_node(cx, cx.props.depth + 1) + } + }) .collect(); cx.bump().alloc(dynamic_nodes) }, @@ -255,7 +283,7 @@ fn create_random_element(cx: Scope) -> Element { // test for panics when creating random nodes and templates #[test] fn create() { - for _ in 0..1000 { + for _ in 0..100 { let mut vdom = VirtualDom::new_with_props(create_random_element, DepthProps { depth: 0, root: true }); let _ = vdom.rebuild(); @@ -266,7 +294,7 @@ fn create() { // This test will change the template every render which is not very realistic, but it helps stress the system #[test] fn diff() { - for _ in 0..100 { + for _ in 0..10 { let mut vdom = VirtualDom::new_with_props(create_random_element, DepthProps { depth: 0, root: true }); let _ = vdom.rebuild(); From cc8b910cf36c0188a6621f47de22fd426eea3e68 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 12 Jan 2023 16:29:31 -0800 Subject: [PATCH 03/10] fix emoji support in autofmt --- packages/autofmt/src/lib.rs | 9 ++-- packages/autofmt/src/util.rs | 7 ++- packages/autofmt/tests/samples.rs | 63 ++++++++++++------------ packages/autofmt/tests/samples/emoji.rsx | 5 ++ 4 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 packages/autofmt/tests/samples/emoji.rsx diff --git a/packages/autofmt/src/lib.rs b/packages/autofmt/src/lib.rs index 67f087854..afd3c3c92 100644 --- a/packages/autofmt/src/lib.rs +++ b/packages/autofmt/src/lib.rs @@ -71,8 +71,11 @@ pub fn fmt_file(contents: &str) -> Vec { }; let remaining = &contents[end - 1..]; + let bracket_end = find_bracket_end(remaining).unwrap(); - let sub_string = &contents[end..bracket_end + end - 1]; + + let sub_string = &remaining[1..bracket_end - 1]; + last_bracket_end = bracket_end + end - 1; let mut new = fmt_block(sub_string, indent_level).unwrap(); @@ -85,7 +88,7 @@ pub fn fmt_file(contents: &str) -> Vec { indent_level = 0; } - let end_marker = end + bracket_end - indent_level * 4 - 1; + let end_marker = end + bracket_end - indent_level * 4 - 2; if new == contents[end..end_marker] { continue; @@ -118,7 +121,7 @@ pub fn write_block_out(body: CallBody) -> Option { } pub fn fmt_block(block: &str, indent_level: usize) -> Option { - let body = syn::parse_str::(block).ok()?; + let body = syn::parse_str::(block).unwrap(); let mut buf = Writer { src: block.lines().map(|f| f.to_string()).collect(), diff --git a/packages/autofmt/src/util.rs b/packages/autofmt/src/util.rs index 7fd4c325c..7ffd36a9a 100644 --- a/packages/autofmt/src/util.rs +++ b/packages/autofmt/src/util.rs @@ -1,14 +1,17 @@ pub fn find_bracket_end(contents: &str) -> Option { let mut depth = 0; - for (i, c) in contents.chars().enumerate() { + let mut len = 0; + + for c in contents.chars() { + len += c.len_utf8(); if c == '{' { depth += 1; } else if c == '}' { depth -= 1; if depth == 0 { - return Some(i); + return Some(len); } } } diff --git a/packages/autofmt/tests/samples.rs b/packages/autofmt/tests/samples.rs index 19588fda0..a5752c4d5 100644 --- a/packages/autofmt/tests/samples.rs +++ b/packages/autofmt/tests/samples.rs @@ -1,35 +1,36 @@ macro_rules! twoway { - ($val:literal => $name:ident) => { - #[test] - fn $name() { - let src = include_str!(concat!("./samples/", $val, ".rsx")); - let formatted = dioxus_autofmt::fmt_file(src); - let out = dioxus_autofmt::apply_formats(src, formatted); - pretty_assertions::assert_eq!(&src, &out); - } + ( + $( + + // doc attrs + $( #[doc = $doc:expr] )* + $name:ident + ),* + ) => { + $( + $( #[doc = $doc] )* + #[test] + fn $name() { + let src = include_str!(concat!("./samples/", stringify!($name), ".rsx")); + let formatted = dioxus_autofmt::fmt_file(src); + let out = dioxus_autofmt::apply_formats(src, formatted); + pretty_assertions::assert_eq!(&src, &out); + } + )* }; } -twoway! ("simple" => simple); - -twoway! ("comments" => comments); - -twoway! ("attributes" => attributes); - -twoway! ("manual_props" => manual_props); - -twoway! ("complex" => complex); - -twoway! ("tiny" => tiny); - -twoway! ("tinynoopt" => tinynoopt); - -twoway! ("long" => long); - -twoway! ("key" => key); - -// Disabled because we can't handle comments on exprs yet -twoway! ("multirsx" => multirsx); - -// Disabled because we can't handle comments on exprs yet -twoway! ("commentshard" => commentshard); +twoway![ + simple, + comments, + attributes, + manual_props, + complex, + tiny, + tinynoopt, + long, + key, + multirsx, + commentshard, + emoji +]; diff --git a/packages/autofmt/tests/samples/emoji.rsx b/packages/autofmt/tests/samples/emoji.rsx new file mode 100644 index 000000000..066491583 --- /dev/null +++ b/packages/autofmt/tests/samples/emoji.rsx @@ -0,0 +1,5 @@ +rsx! { + div { class: "asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd", + section { "🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀" } + } +} From 983789e4c55a81d54e083aca5e61d4d38edf5c85 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 12 Jan 2023 16:55:13 -0800 Subject: [PATCH 04/10] handle weird trailing whitespace in autofmt --- packages/autofmt/src/lib.rs | 34 ++++++++++--------- packages/autofmt/tests/samples.rs | 3 +- .../autofmt/tests/samples/messy_indent.rsx | 13 +++++++ 3 files changed, 33 insertions(+), 17 deletions(-) create mode 100644 packages/autofmt/tests/samples/messy_indent.rsx diff --git a/packages/autofmt/src/lib.rs b/packages/autofmt/src/lib.rs index afd3c3c92..184de1003 100644 --- a/packages/autofmt/src/lib.rs +++ b/packages/autofmt/src/lib.rs @@ -46,6 +46,8 @@ pub fn fmt_file(contents: &str) -> Vec { use triple_accel::{levenshtein_search, Match}; for Match { end, start, k } in levenshtein_search(b"rsx! {", contents.as_bytes()) { + let open = end; + if k > 1 { continue; } @@ -55,7 +57,7 @@ pub fn fmt_file(contents: &str) -> Vec { continue; } - let mut indent_level = { + let indent_level = { // walk backwards from start until we find a new line let mut lines = contents[..start].lines().rev(); match lines.next() { @@ -70,34 +72,34 @@ pub fn fmt_file(contents: &str) -> Vec { } }; - let remaining = &contents[end - 1..]; + let remaining = &contents[open - 1..]; + let close = find_bracket_end(remaining).unwrap(); + // Move the last bracket end to the end of this block to avoid nested blocks + last_bracket_end = close + open - 1; - let bracket_end = find_bracket_end(remaining).unwrap(); + // Format the substring, doesn't include the outer brackets + let substring = &remaining[1..close - 1]; - let sub_string = &remaining[1..bracket_end - 1]; + // make sure to add back whatever weird whitespace there was at the end + let mut remaining_whitespace = substring.chars().rev().take_while(|c| *c == ' ').count(); - last_bracket_end = bracket_end + end - 1; - - let mut new = fmt_block(sub_string, indent_level).unwrap(); + let mut new = fmt_block(substring, indent_level).unwrap(); + // if the new string is not multiline, don't try to adjust the marker ending + // We want to trim off any indentation that there might be if new.len() <= 80 && !new.contains('\n') { new = format!(" {new} "); - - // if the new string is not multiline, don't try to adjust the marker ending - // We want to trim off any indentation that there might be - indent_level = 0; + remaining_whitespace = 0; } - let end_marker = end + bracket_end - indent_level * 4 - 2; - - if new == contents[end..end_marker] { + if new == substring { continue; } formatted_blocks.push(FormattedBlock { formatted: new, - start: end, - end: end_marker, + start: open, + end: last_bracket_end - remaining_whitespace - 1, }); } diff --git a/packages/autofmt/tests/samples.rs b/packages/autofmt/tests/samples.rs index a5752c4d5..d8d53a4a0 100644 --- a/packages/autofmt/tests/samples.rs +++ b/packages/autofmt/tests/samples.rs @@ -32,5 +32,6 @@ twoway![ key, multirsx, commentshard, - emoji + emoji, + messy_indent ]; diff --git a/packages/autofmt/tests/samples/messy_indent.rsx b/packages/autofmt/tests/samples/messy_indent.rsx new file mode 100644 index 000000000..260dd08a6 --- /dev/null +++ b/packages/autofmt/tests/samples/messy_indent.rsx @@ -0,0 +1,13 @@ +fn SaveClipboard(cx: Scope) -> Element { + rsx! { + div { class: "relative w-1/2 {align} max-w-md leading-8", + h2 { class: "mb-6 text-3xl leading-tight md:text-4xl md:leading-tight lg:text-3xl lg:leading-tight font-heading font-mono font-bold", + "{title}" + } + } + }; + + cx.render(rsx! { + div { "hello world", "hello world", "hello world" } +}) +} From 293ef9c690621bdf9ea024f8db4f27064df4f7f9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 12 Jan 2023 17:02:41 -0800 Subject: [PATCH 05/10] fix: only flow exprs with indent in mind --- packages/autofmt/src/element.rs | 10 ++++++++-- packages/autofmt/tests/samples.rs | 3 ++- packages/autofmt/tests/samples/long_exprs.rsx | 17 +++++++++++++++++ packages/autofmt/tests/samples/simple.rsx | 4 +++- 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 packages/autofmt/tests/samples/long_exprs.rsx diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index bd9e83046..2c15ef6ca 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -2,6 +2,7 @@ use crate::Writer; use dioxus_rsx::*; use proc_macro2::Span; use std::{ + borrow::Borrow, fmt::Result, fmt::{self, Write}, }; @@ -67,7 +68,8 @@ impl Writer { // check if we have a lot of attributes let attr_len = self.is_short_attrs(attributes); let is_short_attr_list = attr_len < 80; - let is_small_children = self.is_short_children(children).is_some(); + let children_len = self.is_short_children(children); + let is_small_children = children_len.is_some(); // if we have few attributes and a lot of children, place the attrs on top if is_short_attr_list && !is_small_children { @@ -85,7 +87,11 @@ impl Writer { // if we have few children and few attributes, make it a one-liner if is_short_attr_list && is_small_children { - opt_level = ShortOptimization::Oneliner; + if children_len.unwrap() + attr_len + self.out.indent * 4 < 100 { + opt_level = ShortOptimization::Oneliner; + } else { + opt_level = ShortOptimization::PropsOnTop; + } } // If there's nothing at all, empty optimization diff --git a/packages/autofmt/tests/samples.rs b/packages/autofmt/tests/samples.rs index d8d53a4a0..9042a7f1a 100644 --- a/packages/autofmt/tests/samples.rs +++ b/packages/autofmt/tests/samples.rs @@ -33,5 +33,6 @@ twoway![ multirsx, commentshard, emoji, - messy_indent + messy_indent, + long_exprs ]; diff --git a/packages/autofmt/tests/samples/long_exprs.rsx b/packages/autofmt/tests/samples/long_exprs.rsx new file mode 100644 index 000000000..fb4dab93f --- /dev/null +++ b/packages/autofmt/tests/samples/long_exprs.rsx @@ -0,0 +1,17 @@ +rsx! { + div { + div { + div { + div { + section { class: "body-font overflow-hidden dark:bg-ideblack", + div { class: "container px-6 mx-auto", + div { class: "-my-8 divide-y-2 divide-gray-100", + POSTS.iter().enumerate().map(|(id, post)| rsx! { BlogPostItem { post: post, id: id } }) + } + } + } + } + } + } + } +} diff --git a/packages/autofmt/tests/samples/simple.rsx b/packages/autofmt/tests/samples/simple.rsx index 54ff5f867..38aaa21ca 100644 --- a/packages/autofmt/tests/samples/simple.rsx +++ b/packages/autofmt/tests/samples/simple.rsx @@ -37,7 +37,9 @@ rsx! { // One level compression div { - a { class: "py-2 px-3 bg-indigo-500 hover:bg-indigo-600 rounded text-xs text-white", href: "#", "Send invitation" } + a { class: "py-2 px-3 bg-indigo-500 hover:bg-indigo-600 rounded text-xs text-white", href: "#", + "Send invitation" + } } // Components From 8ec5cbcc205bf09a9bf7c95f0deae56d741ec66c Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 12 Jan 2023 22:54:50 -0800 Subject: [PATCH 06/10] fix ci --- packages/autofmt/src/element.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index 2c15ef6ca..f96969845 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -2,7 +2,6 @@ use crate::Writer; use dioxus_rsx::*; use proc_macro2::Span; use std::{ - borrow::Borrow, fmt::Result, fmt::{self, Write}, }; From c5bb4275e69997d60a93a40c76a8da709010be19 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 13 Jan 2023 08:48:59 -0600 Subject: [PATCH 07/10] fix boolean attributes with the web renderer --- .../interpreter/src/sledgehammer_bindings.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/interpreter/src/sledgehammer_bindings.rs b/packages/interpreter/src/sledgehammer_bindings.rs index a81e16235..61e37c2cb 100644 --- a/packages/interpreter/src/sledgehammer_bindings.rs +++ b/packages/interpreter/src/sledgehammer_bindings.rs @@ -129,6 +129,34 @@ mod js { root.appendChild(els[k]); } } + const bool_attrs = { + allowfullscreen: true, + allowpaymentrequest: true, + async: true, + autofocus: true, + autoplay: true, + checked: true, + controls: true, + default: true, + defer: true, + disabled: true, + formnovalidate: true, + hidden: true, + ismap: true, + itemscope: true, + loop: true, + multiple: true, + muted: true, + nomodule: true, + novalidate: true, + open: true, + playsinline: true, + readonly: true, + required: true, + reversed: true, + selected: true, + truespeed: true, + }; "#; extern "C" { From c4485071a7c6c6cff14764ddd73238b1d5a553c0 Mon Sep 17 00:00:00 2001 From: d4h0 Date: Fri, 13 Jan 2023 17:25:18 +0100 Subject: [PATCH 08/10] init --- packages/liveview/Cargo.toml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index 7c3ccb675..2ea5f8cd0 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -13,18 +13,16 @@ license = "MIT/Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1.23.0", features = ["full"] } +thiserror = "1.0.38" futures-util = { version = "0.3.25", default-features = false, features = [ "sink", ] } futures-channel = { version = "0.3.25", features = ["sink"] } -pretty_env_logger = "0.4.0" +tokio = { version = "1.23.0", features = ["time"] } tokio-stream = { version = "0.1.11", features = ["net"] } - +tokio-util = { version = "0.7.4", features = ["rt"] } serde = { version = "1.0.151", features = ["derive"] } serde_json = "1.0.91" -tokio-util = { version = "0.7.4", features = ["full"] } - dioxus-html = { path = "../html", features = ["serialize"], version = "^0.3.0" } dioxus-core = { path = "../core", features = ["serialize"], version = "^0.3.0" } dioxus-interpreter-js = { path = "../interpreter", version = "0.3.0" } @@ -34,13 +32,9 @@ warp = { version = "0.3.3", optional = true } # axum axum = { version = "0.6.1", optional = true, features = ["ws"] } -tower = { version = "0.4.13", optional = true } # salvo salvo = { version = "0.37.7", optional = true, features = ["ws"] } -thiserror = "1.0.38" -uuid = { version = "1.2.2", features = ["v4"] } -anyhow = "1.0.68" # actix is ... complicated? # actix-files = { version = "0.6.2", optional = true } @@ -48,6 +42,7 @@ anyhow = "1.0.68" # actix-ws = { version = "0.2.5", optional = true } [dev-dependencies] +pretty_env_logger = { version = "0.4.0" } tokio = { version = "1.23.0", features = ["full"] } dioxus = { path = "../dioxus", version = "0.3.0" } warp = "0.3.3" From 6cd87796f218f8b0af01fb2a0f70848f1735d29d Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Jan 2023 10:51:12 -0800 Subject: [PATCH 09/10] feat: allow ifchains and for loops --- packages/autofmt/src/element.rs | 4 +- packages/autofmt/src/expr.rs | 6 +-- packages/autofmt/src/writer.rs | 35 +++++++++++-- packages/autofmt/tests/samples.rs | 3 +- .../autofmt/tests/samples/ifchain_forloop.rsx | 13 +++++ packages/rsx/src/node.rs | 50 ++++++++++++------- 6 files changed, 82 insertions(+), 29 deletions(-) create mode 100644 packages/autofmt/tests/samples/ifchain_forloop.rsx diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index f96969845..b9903dc47 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -365,8 +365,8 @@ impl Writer { Some(len) => total_count += len, None => return None, }, - BodyNode::ForLoop(_) => todo!(), - BodyNode::IfChain(_) => todo!(), + BodyNode::ForLoop(_forloop) => return None, + BodyNode::IfChain(_chain) => return None, } } diff --git a/packages/autofmt/src/expr.rs b/packages/autofmt/src/expr.rs index 55a5fa9eb..4a916c71a 100644 --- a/packages/autofmt/src/expr.rs +++ b/packages/autofmt/src/expr.rs @@ -1,16 +1,16 @@ //! pretty printer for rsx! use std::fmt::{Result, Write}; +use proc_macro2::Span; + use crate::Writer; impl Writer { - pub fn write_raw_expr(&mut self, exp: &syn::Expr) -> Result { + pub fn write_raw_expr(&mut self, placement: Span) -> Result { /* We want to normalize the expr to the appropriate indent level. */ - use syn::spanned::Spanned; - let placement = exp.span(); let start = placement.start(); let end = placement.end(); diff --git a/packages/autofmt/src/writer.rs b/packages/autofmt/src/writer.rs index 4d4aed4a3..cba6d4ec4 100644 --- a/packages/autofmt/src/writer.rs +++ b/packages/autofmt/src/writer.rs @@ -1,10 +1,11 @@ -use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed}; +use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed, ForLoop}; use proc_macro2::{LineColumn, Span}; +use quote::ToTokens; use std::{ collections::{HashMap, VecDeque}, fmt::{Result, Write}, }; -use syn::{spanned::Spanned, Expr}; +use syn::{spanned::Spanned, Expr, ExprIf}; use crate::buffer::Buffer; @@ -37,8 +38,9 @@ impl Writer { BodyNode::Element(el) => self.write_element(el), BodyNode::Component(component) => self.write_component(component), BodyNode::Text(text) => self.out.write_text(text), - BodyNode::RawExpr(exp) => self.write_raw_expr(exp), - _ => Ok(()), + BodyNode::RawExpr(exp) => self.write_raw_expr(exp.span()), + BodyNode::ForLoop(forloop) => self.write_for_loop(forloop), + BodyNode::IfChain(ifchain) => self.write_if_chain(ifchain), } } @@ -176,6 +178,31 @@ impl Writer { .or_insert_with(|| prettyplease::unparse_expr(expr)) .as_str() } + + fn write_for_loop(&mut self, forloop: &ForLoop) -> std::fmt::Result { + write!( + self.out, + "for {} in {} {{", + forloop.pat.clone().into_token_stream().to_string(), + prettyplease::unparse_expr(&forloop.expr) + )?; + + if forloop.body.is_empty() { + write!(self.out, "}}")?; + return Ok(()); + } + + self.write_body_indented(&forloop.body)?; + + self.out.tabbed_line()?; + write!(self.out, "}}")?; + + Ok(()) + } + + fn write_if_chain(&mut self, ifchain: &ExprIf) -> std::fmt::Result { + self.write_raw_expr(ifchain.span()) + } } trait SpanLength { diff --git a/packages/autofmt/tests/samples.rs b/packages/autofmt/tests/samples.rs index 9042a7f1a..26492e141 100644 --- a/packages/autofmt/tests/samples.rs +++ b/packages/autofmt/tests/samples.rs @@ -34,5 +34,6 @@ twoway![ commentshard, emoji, messy_indent, - long_exprs + long_exprs, + ifchain_forloop ]; diff --git a/packages/autofmt/tests/samples/ifchain_forloop.rsx b/packages/autofmt/tests/samples/ifchain_forloop.rsx new file mode 100644 index 000000000..9ec7326af --- /dev/null +++ b/packages/autofmt/tests/samples/ifchain_forloop.rsx @@ -0,0 +1,13 @@ +rsx! { + // Does this work? + for i in b { + // Hey it works? + div {} + } + + // Some ifchain + if a > 10 { + // + rsx! { div {} } + } +} diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index ef76d14ff..0d5556552 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -95,25 +95,7 @@ impl Parse for BodyNode { // Transform for loops into into_iter calls if stream.peek(Token![for]) { - let _f = stream.parse::()?; - let pat = stream.parse::()?; - let _i = stream.parse::()?; - let expr = stream.parse::>()?; - - let body; - braced!(body in stream); - let mut children = vec![]; - while !body.is_empty() { - children.push(body.parse()?); - } - - return Ok(BodyNode::ForLoop(ForLoop { - for_token: _f, - pat, - in_token: _i, - expr, - body: children, - })); + return Ok(BodyNode::ForLoop(stream.parse()?)); } // Transform unterminated if statements into terminated optional if statements @@ -221,6 +203,36 @@ pub struct ForLoop { pub in_token: Token![in], pub expr: Box, pub body: Vec, + pub brace_token: token::Brace, +} + +impl Parse for ForLoop { + fn parse(input: ParseStream) -> Result { + let for_token: Token![for] = input.parse()?; + + let pat = input.parse()?; + + 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()?); + } + + Ok(Self { + for_token, + pat, + in_token, + body: children, + expr: Box::new(expr), + brace_token, + }) + } } fn is_if_chain_terminated(chain: &ExprIf) -> bool { From bf559a6d0657d461160911366337b4308eebc2d7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 13 Jan 2023 10:59:03 -0800 Subject: [PATCH 10/10] fix clippy --- packages/autofmt/src/writer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/autofmt/src/writer.rs b/packages/autofmt/src/writer.rs index cba6d4ec4..b60224c88 100644 --- a/packages/autofmt/src/writer.rs +++ b/packages/autofmt/src/writer.rs @@ -183,7 +183,7 @@ impl Writer { write!( self.out, "for {} in {} {{", - forloop.pat.clone().into_token_stream().to_string(), + forloop.pat.clone().into_token_stream(), prettyplease::unparse_expr(&forloop.expr) )?;