Merge branch 'upstream' into desktop-hot-reload
This commit is contained in:
commit
8a7cee529c
16 files changed
+528
-95
No files matched your search
@@ -67,7 +67,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 +86,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
|
||||
@@ -360,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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
+21
-16
@@ -46,6 +46,8 @@ pub fn fmt_file(contents: &str) -> Vec<FormattedBlock> {
|
||||
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<FormattedBlock> {
|
||||
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,31 +72,34 @@ pub fn fmt_file(contents: &str) -> Vec<FormattedBlock> {
|
||||
}
|
||||
};
|
||||
|
||||
let remaining = &contents[end - 1..];
|
||||
let bracket_end = find_bracket_end(remaining).unwrap();
|
||||
let sub_string = &contents[end..bracket_end + end - 1];
|
||||
last_bracket_end = bracket_end + 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 mut new = fmt_block(sub_string, indent_level).unwrap();
|
||||
// Format the substring, doesn't include the outer brackets
|
||||
let substring = &remaining[1..close - 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();
|
||||
|
||||
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 - 1;
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,7 +123,7 @@ pub fn write_block_out(body: CallBody) -> Option<String> {
|
||||
}
|
||||
|
||||
pub fn fmt_block(block: &str, indent_level: usize) -> Option<String> {
|
||||
let body = syn::parse_str::<dioxus_rsx::CallBody>(block).ok()?;
|
||||
let body = syn::parse_str::<dioxus_rsx::CallBody>(block).unwrap();
|
||||
|
||||
let mut buf = Writer {
|
||||
src: block.lines().map(|f| f.to_string()).collect(),
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
pub fn find_bracket_end(contents: &str) -> Option<usize> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
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 {
|
||||
|
||||
@@ -1,35 +1,39 @@
|
||||
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,
|
||||
messy_indent,
|
||||
long_exprs,
|
||||
ifchain_forloop
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
rsx! {
|
||||
div { class: "asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd asdasdasd",
|
||||
section { "🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
rsx! {
|
||||
// Does this work?
|
||||
for i in b {
|
||||
// Hey it works?
|
||||
div {}
|
||||
}
|
||||
|
||||
// Some ifchain
|
||||
if a > 10 {
|
||||
//
|
||||
rsx! { div {} }
|
||||
}
|
||||
}
|
||||
@@ -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 } })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" }
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -41,6 +41,7 @@ serde = { version = "1", features = ["derive"], optional = true }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
dioxus = { path = "../dioxus" }
|
||||
pretty_assertions = "1.3.0"
|
||||
rand = "0.8.5"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
use dioxus::prelude::Props;
|
||||
use dioxus_core::*;
|
||||
use std::cell::Cell;
|
||||
|
||||
fn random_ns() -> Option<&'static str> {
|
||||
let namespace = rand::random::<u8>() % 2;
|
||||
match namespace {
|
||||
0 => None,
|
||||
1 => Some(Box::leak(
|
||||
format!("ns{}", rand::random::<usize>()).into_boxed_str(),
|
||||
)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_random_attribute(attr_idx: &mut usize) -> TemplateAttribute<'static> {
|
||||
match rand::random::<u8>() % 2 {
|
||||
0 => TemplateAttribute::Static {
|
||||
name: Box::leak(format!("attr{}", rand::random::<usize>()).into_boxed_str()),
|
||||
value: Box::leak(format!("value{}", rand::random::<usize>()).into_boxed_str()),
|
||||
namespace: random_ns(),
|
||||
},
|
||||
1 => TemplateAttribute::Dynamic {
|
||||
id: {
|
||||
let old_idx = *attr_idx;
|
||||
*attr_idx += 1;
|
||||
old_idx
|
||||
},
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_random_template_node(
|
||||
dynamic_node_types: &mut Vec<DynamicNodeType>,
|
||||
template_idx: &mut usize,
|
||||
attr_idx: &mut usize,
|
||||
depth: usize,
|
||||
) -> TemplateNode<'static> {
|
||||
match rand::random::<u8>() % 4 {
|
||||
0 => {
|
||||
let attrs = {
|
||||
let attrs: Vec<_> = (0..(rand::random::<usize>() % 10))
|
||||
.map(|_| create_random_attribute(attr_idx))
|
||||
.collect();
|
||||
Box::leak(attrs.into_boxed_slice())
|
||||
};
|
||||
TemplateNode::Element {
|
||||
tag: Box::leak(format!("tag{}", rand::random::<usize>()).into_boxed_str()),
|
||||
namespace: random_ns(),
|
||||
attrs,
|
||||
children: {
|
||||
if depth > 4 {
|
||||
&[]
|
||||
} else {
|
||||
let children: Vec<_> = (0..(rand::random::<usize>() % 3))
|
||||
.map(|_| {
|
||||
create_random_template_node(
|
||||
dynamic_node_types,
|
||||
template_idx,
|
||||
attr_idx,
|
||||
depth + 1,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
Box::leak(children.into_boxed_slice())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
1 => TemplateNode::Text {
|
||||
text: Box::leak(format!("{}", rand::random::<usize>()).into_boxed_str()),
|
||||
},
|
||||
2 => TemplateNode::DynamicText {
|
||||
id: {
|
||||
let old_idx = *template_idx;
|
||||
*template_idx += 1;
|
||||
dynamic_node_types.push(DynamicNodeType::Text);
|
||||
old_idx
|
||||
},
|
||||
},
|
||||
3 => TemplateNode::Dynamic {
|
||||
id: {
|
||||
let old_idx = *template_idx;
|
||||
*template_idx += 1;
|
||||
dynamic_node_types.push(DynamicNodeType::Other);
|
||||
old_idx
|
||||
},
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_paths(
|
||||
node: &TemplateNode<'static>,
|
||||
current_path: &[u8],
|
||||
node_paths: &mut Vec<Vec<u8>>,
|
||||
attr_paths: &mut Vec<Vec<u8>>,
|
||||
) {
|
||||
match node {
|
||||
TemplateNode::Element { children, attrs, .. } => {
|
||||
for attr in *attrs {
|
||||
match attr {
|
||||
TemplateAttribute::Static { .. } => {}
|
||||
TemplateAttribute::Dynamic { .. } => {
|
||||
attr_paths.push(current_path.to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i, child) in children.iter().enumerate() {
|
||||
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.to_vec());
|
||||
}
|
||||
TemplateNode::Dynamic { .. } => {
|
||||
node_paths.push(current_path.to_vec());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum DynamicNodeType {
|
||||
Text,
|
||||
Other,
|
||||
}
|
||||
|
||||
fn create_random_template(name: &'static str) -> (Template<'static>, Vec<DynamicNodeType>) {
|
||||
let mut dynamic_node_type = Vec::new();
|
||||
let mut template_idx = 0;
|
||||
let mut attr_idx = 0;
|
||||
let roots = (0..(1 + rand::random::<usize>() % 5))
|
||||
.map(|_| {
|
||||
create_random_template_node(&mut dynamic_node_type, &mut template_idx, &mut attr_idx, 0)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
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, &[i as u8], &mut node_paths, &mut attr_paths);
|
||||
}
|
||||
let node_paths = Box::leak(
|
||||
node_paths
|
||||
.into_iter()
|
||||
.map(|v| &*Box::leak(v.into_boxed_slice()))
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
);
|
||||
let attr_paths = Box::leak(
|
||||
attr_paths
|
||||
.into_iter()
|
||||
.map(|v| &*Box::leak(v.into_boxed_slice()))
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
);
|
||||
(
|
||||
Template { name, roots, node_paths, attr_paths },
|
||||
dynamic_node_type,
|
||||
)
|
||||
}
|
||||
|
||||
fn create_random_dynamic_node(cx: &ScopeState, depth: usize) -> DynamicNode {
|
||||
let range = if depth > 3 { 1 } else { 3 };
|
||||
match rand::random::<u8>() % range {
|
||||
0 => DynamicNode::Placeholder(Default::default()),
|
||||
1 => cx.make_node((0..(rand::random::<u8>() % 5)).map(|_| VNode {
|
||||
key: None,
|
||||
parent: Default::default(),
|
||||
template: Cell::new(Template {
|
||||
name: concat!(file!(), ":", line!(), ":", column!(), ":0"),
|
||||
roots: &[TemplateNode::Dynamic { id: 0 }],
|
||||
node_paths: &[&[0]],
|
||||
attr_paths: &[],
|
||||
}),
|
||||
root_ids: Default::default(),
|
||||
dynamic_nodes: cx.bump().alloc([cx.component(
|
||||
create_random_element,
|
||||
DepthProps { depth, root: false },
|
||||
"create_random_element",
|
||||
)]),
|
||||
dynamic_attrs: &[],
|
||||
})),
|
||||
2 => cx.component(
|
||||
create_random_element,
|
||||
DepthProps { depth, root: false },
|
||||
"create_random_element",
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_random_dynamic_attr(cx: &ScopeState) -> Attribute {
|
||||
let value = match rand::random::<u8>() % 6 {
|
||||
0 => AttributeValue::Text(Box::leak(
|
||||
format!("{}", rand::random::<usize>()).into_boxed_str(),
|
||||
)),
|
||||
1 => AttributeValue::Float(rand::random()),
|
||||
2 => AttributeValue::Int(rand::random()),
|
||||
3 => AttributeValue::Bool(rand::random()),
|
||||
4 => cx.any_value(rand::random::<usize>()),
|
||||
5 => AttributeValue::None,
|
||||
// Listener(RefCell<Option<ListenerCb<'a>>>),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Attribute {
|
||||
name: Box::leak(format!("attr{}", rand::random::<usize>()).into_boxed_str()),
|
||||
value,
|
||||
namespace: random_ns(),
|
||||
mounted_element: Default::default(),
|
||||
volatile: rand::random(),
|
||||
}
|
||||
}
|
||||
|
||||
static mut TEMPLATE_COUNT: usize = 0;
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
struct DepthProps {
|
||||
depth: usize,
|
||||
root: bool,
|
||||
}
|
||||
|
||||
fn create_random_element(cx: Scope<DepthProps>) -> Element {
|
||||
cx.needs_update();
|
||||
let range = if cx.props.root { 2 } else { 3 };
|
||||
let node = match rand::random::<usize>() % range {
|
||||
0 | 1 => {
|
||||
let (template, dynamic_node_types) = create_random_template(Box::leak(
|
||||
format!(
|
||||
"{}{}",
|
||||
concat!(file!(), ":", line!(), ":", column!(), ":"),
|
||||
{
|
||||
unsafe {
|
||||
let old = TEMPLATE_COUNT;
|
||||
TEMPLATE_COUNT += 1;
|
||||
old
|
||||
}
|
||||
}
|
||||
)
|
||||
.into_boxed_str(),
|
||||
));
|
||||
println!("{template:#?}");
|
||||
let node = VNode {
|
||||
key: None,
|
||||
parent: None,
|
||||
template: Cell::new(template),
|
||||
root_ids: Default::default(),
|
||||
dynamic_nodes: {
|
||||
let dynamic_nodes: Vec<_> = dynamic_node_types
|
||||
.iter()
|
||||
.map(|ty| match ty {
|
||||
DynamicNodeType::Text => DynamicNode::Text(VText {
|
||||
value: Box::leak(
|
||||
format!("{}", rand::random::<usize>()).into_boxed_str(),
|
||||
),
|
||||
id: Default::default(),
|
||||
}),
|
||||
DynamicNodeType::Other => {
|
||||
create_random_dynamic_node(cx, cx.props.depth + 1)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
cx.bump().alloc(dynamic_nodes)
|
||||
},
|
||||
dynamic_attrs: cx.bump().alloc(
|
||||
(0..template.attr_paths.len())
|
||||
.map(|_| create_random_dynamic_attr(cx))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
};
|
||||
Some(node)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
println!("{node:#?}");
|
||||
node
|
||||
}
|
||||
|
||||
// test for panics when creating random nodes and templates
|
||||
#[test]
|
||||
fn create() {
|
||||
for _ in 0..100 {
|
||||
let mut vdom =
|
||||
VirtualDom::new_with_props(create_random_element, DepthProps { depth: 0, root: true });
|
||||
let _ = vdom.rebuild();
|
||||
}
|
||||
}
|
||||
|
||||
// test for panics when diffing random nodes
|
||||
// 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..10 {
|
||||
let mut vdom =
|
||||
VirtualDom::new_with_props(create_random_element, DepthProps { depth: 0, root: true });
|
||||
let _ = vdom.rebuild();
|
||||
for _ in 0..10 {
|
||||
let _ = vdom.render_immediate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" {
|
||||
|
||||
@@ -13,37 +13,29 @@ license = "MIT/Apache-2.0"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
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" }
|
||||
dioxus-hot-reload = { path = "../hot-reload" }
|
||||
|
||||
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"] }
|
||||
|
||||
interprocess = { version = "1.2.1", optional = true }
|
||||
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" }
|
||||
dioxus-hot-reload = { path = "../hot-reload" }
|
||||
|
||||
# warp
|
||||
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 }
|
||||
@@ -51,6 +43,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"
|
||||
|
||||
+31
-19
@@ -95,25 +95,7 @@ impl Parse for BodyNode {
|
||||
|
||||
// Transform for loops into into_iter calls
|
||||
if stream.peek(Token![for]) {
|
||||
let _f = stream.parse::<Token![for]>()?;
|
||||
let pat = stream.parse::<Pat>()?;
|
||||
let _i = stream.parse::<Token![in]>()?;
|
||||
let expr = stream.parse::<Box<Expr>>()?;
|
||||
|
||||
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<Expr>,
|
||||
pub body: Vec<BodyNode>,
|
||||
pub brace_token: token::Brace,
|
||||
}
|
||||
|
||||
impl Parse for ForLoop {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
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 {
|
||||
|
||||
Reference in new issue
Block a user