diff --git a/Cargo.toml b/Cargo.toml index e468c9027..4cdcc9688 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,23 @@ [workspace] members = [ # Built-in - "packages/dioxus", - "packages/core", "packages/core-macro", - "packages/router", - "packages/ssr", - "packages/webview", - "packages/web", - "packages/livehost", - "packages/vscode-ext", + # "packages/dioxus", + # "packages/core", + # "packages/router", + # "packages/ssr", + # "packages/webview", + # "packages/web", + # "packages/livehost", + # "packages/vscode-ext", # "packages/recoil", # "packages/redux", # "packages/macro", # TODO @Jon, share the validation code # "packages/web", # "packages/hooks", - "packages/cli", - "examples", + # "packages/cli", + # "examples", # "packages/html-macro", # "packages/html-macro-2", # diff --git a/packages/core-macro/examples/rsxt.rs b/packages/core-macro/examples/rsxt.rs new file mode 100644 index 000000000..843ef1c2c --- /dev/null +++ b/packages/core-macro/examples/rsxt.rs @@ -0,0 +1,46 @@ +use dioxus_core_macro::rsx; + +pub fn main() { + // render(rsx! { + // _ {} + // }); + + let g = String::from("asd"); + + let lazy = rsx! { + div { + a: "asd", + a: "asd", + a: "asd", + a: "asd", + a: "asd", + a: {rsx!{ h1 {"hello world"} }}, // include + a: {g}, + b: {1 + 2}, + onclick: {move |_| { + println!("hello world!") + }}, + div { + a: "asd" + div { + div { + div { + + } + } + } + } + h1 { + + } + h2 { + "child" + } + "Childnode" + } + }; + + render(lazy); +} + +fn render(f: impl Fn(())) {} diff --git a/packages/core-macro/src/rsxt.rs b/packages/core-macro/src/rsxt.rs index 209b2a7cc..c42d10c50 100644 --- a/packages/core-macro/src/rsxt.rs +++ b/packages/core-macro/src/rsxt.rs @@ -1,5 +1,8 @@ /* + +https://github.com/chinedufn/percy/issues/37 + An example usage of rsx! would look like this: ```ignore ctx.render(rsx!{ @@ -30,7 +33,7 @@ ctx.render(rsx!{ each element is given by tag { [Attr] } */ -use syn::parse::ParseBuffer; +use syn::parse::{discouraged::Speculative, ParseBuffer}; use { proc_macro::TokenStream, @@ -50,6 +53,10 @@ pub struct RsxRender {} impl Parse for RsxRender { fn parse(input: ParseStream) -> Result { + // cannot accept multiple elements + // can only accept one root per component + // fragments can be used as + // _ { content } let g: Element = input.parse()?; Ok(Self {}) } @@ -84,28 +91,32 @@ impl ToTokens for ToToksCtx<&Node> { } } -impl Node { - fn peek(s: ParseStream) -> bool { - (s.peek(Token![<]) && !s.peek2(Token![/])) || s.peek(token::Brace) || s.peek(LitStr) - } -} +// impl Node { +// fn peek(s: ParseStream) -> bool { +// (s.peek(Token![<]) && !s.peek2(Token![/])) || s.peek(token::Brace) || s.peek(LitStr) +// } +// } impl Parse for Node { fn parse(s: ParseStream) -> Result { - Ok(if s.peek(Token![<]) { - Node::Element(s.parse()?) + let fork = s.fork(); + if let Ok(text) = fork.parse::() { + s.advance_to(&fork); + return Ok(Self::Text(text)); + } else if let Ok(el) = s.parse::() { + return Ok(Self::Element(el)); } else { - Node::Text(s.parse()?) - }) + panic!("Not a valid child node"); + } } } -/// ======================================= -/// Parse the VNode::Element type -/// ======================================= -/// - [ ] Allow VComponent -/// -/// +// ======================================= +// Parse the VNode::Element type +// ======================================= +// - [ ] Allow VComponent +// +// struct Element { name: Ident, attrs: Vec, @@ -114,79 +125,96 @@ struct Element { impl ToTokens for ToToksCtx<&Element> { fn to_tokens(&self, tokens: &mut TokenStream2) { - // let ctx = self.ctx; - let name = &self.inner.name; - tokens.append_all(quote! { - dioxus::builder::#name(bump) - }); - for attr in self.inner.attrs.iter() { - self.recurse(attr).to_tokens(tokens); - } - match &self.inner.children { - MaybeExpr::Expr(expr) => tokens.append_all(quote! { - .children(#expr) - }), - MaybeExpr::Literal(nodes) => { - let mut children = nodes.iter(); - if let Some(child) = children.next() { - let mut inner_toks = TokenStream2::new(); - self.recurse(child).to_tokens(&mut inner_toks); - while let Some(child) = children.next() { - quote!(,).to_tokens(&mut inner_toks); - self.recurse(child).to_tokens(&mut inner_toks); - } - tokens.append_all(quote! { - .children([#inner_toks]) - }); - } - } - } - tokens.append_all(quote! { - .finish() - }); + todo!() + // // let ctx = self.ctx; + // let name = &self.inner.name; + // tokens.append_all(quote! { + // dioxus::builder::#name(bump) + // }); + // for attr in self.inner.attrs.iter() { + // self.recurse(attr).to_tokens(tokens); + // } + // match &self.inner.children { + // MaybeExpr::Expr(expr) => tokens.append_all(quote! { + // .children(#expr) + // }), + // MaybeExpr::Literal(nodes) => { + // let mut children = nodes.iter(); + // if let Some(child) = children.next() { + // let mut inner_toks = TokenStream2::new(); + // self.recurse(child).to_tokens(&mut inner_toks); + // while let Some(child) = children.next() { + // quote!(,).to_tokens(&mut inner_toks); + // self.recurse(child).to_tokens(&mut inner_toks); + // } + // tokens.append_all(quote! { + // .children([#inner_toks]) + // }); + // } + // } + // } + // tokens.append_all(quote! { + // .finish() + // }); } } impl Parse for Element { fn parse(s: ParseStream) -> Result { - // steps: - // grab ident as name - // peak to the next character - // ensure it's a { - - // s.parse::()?; + // TODO: reject anything weird/nonstandard + // we want names *only* let name = Ident::parse_any(s)?; + // parse the guts let content: ParseBuffer; - // parse the guts of the div {} tag into the content buffer syn::braced!(content in s); - // s. - // s.parse::()?; - // s.parse() - // s.parse_terminated(parser) - // s.parse::()?; - let mut attrs = vec![]; - // let mut children = vec![]; let mut children: Vec = vec![]; - // // keep looking for attributes - // while !s.peek(Token![>]) { - // // self-closing - // if s.peek(Token![/]) { - // s.parse::()?; - // s.parse::]>()?; - // return Ok(Self { - // name, - // attrs, - // children: MaybeExpr::Literal(vec![]), - // }); - // } - // attrs.push(s.parse()?); - // } - // s.parse::]>()?; + 'parsing: loop { + // todo move this around into a more functional style + // [1] Break if empty + // [2] Try to consume an attr (with comma) + // [3] Try to consume a child node (with comma) + // [4] Try to consume brackets as anything thats Into + // [last] Fail if none worked + // [1] Break if empty + if content.is_empty() { + break 'parsing; + } + + // [2] Try to consume an attr + let fork = content.fork(); + if let Ok(attr) = fork.parse::() { + // make sure to advance or your computer will become a spaceheater :) + content.advance_to(&fork); + attrs.push(attr); + continue 'parsing; + } + + // [3] Try to consume a child node + let fork = content.fork(); + if let Ok(node) = fork.parse::() { + // make sure to advance or your computer will become a spaceheater :) + content.advance_to(&fork); + children.push(node); + continue 'parsing; + } + + // [4] TODO: Parsing brackets + // let fork = content.fork(); + // if let Ok(el) = fork.parse() { + // children.push(el); + // continue 'parsing; + // } + + // todo: pass a descriptive error onto the offending tokens + panic!("Entry is not an attr or element\n {}", content) + } + + let children = MaybeExpr::Literal(Vec::new()); // // Contents of an element can either be a brace (in which case we just copy verbatim), or a // // sequence of nodes. // let children = if s.peek(token::Brace) { @@ -237,7 +265,7 @@ impl Parse for Attr { fn parse(s: ParseStream) -> Result { let mut name = Ident::parse_any(s)?; let name_str = name.to_string(); - s.parse::()?; + s.parse::()?; // Check if this is an event handler // If so, parse into literal tokens @@ -271,6 +299,13 @@ impl Parse for Attr { }; AttrType::Value(lit_str) }; + + // consume comma if it exists + // we don't actually care if there *are* commas between attrs + if s.peek(Token![,]) { + let _ = s.parse::(); + } + Ok(Attr { name, ty }) } } @@ -301,12 +336,12 @@ enum AttrType { // todo Bool(MaybeExpr) } -/// ======================================= -/// Parse just plain text -/// ======================================= -/// - [ ] Perform formatting automatically -/// -/// +// ======================================= +// Parse just plain text +// ======================================= +// - [ ] Perform formatting automatically +// +// struct TextNode(MaybeExpr); impl Parse for TextNode { diff --git a/packages/core/examples/rsx_usage.rs b/packages/core/examples/rsx_usage.rs index 5a8aa971e..b1ff32d18 100644 --- a/packages/core/examples/rsx_usage.rs +++ b/packages/core/examples/rsx_usage.rs @@ -2,88 +2,106 @@ use dioxus_core::prelude::*; fn main() {} -fn Example(ctx: Context, props: ()) -> DomTree { +// include as much as you might accept +struct ButtonProps<'a> { + onhover: Option<&'a dyn Fn()>, + // // A list of any attrs + // attrs: AttrList<'a>, +} + +fn CustomButton(ctx: Context, props: ButtonProps) -> DomTree { + let onfocus = move |evt: ()| log::debug!("Focused"); + ctx.render(rsx! { - div { -

"title1" "title2"

- -

alkjsd

- - - h1 { tag: "type", abc: 123, class: "big small wide short", - "title1" - "title1" - "title1" - "title" - } - - h1 ("title") { - tag: "type", - abc: 123, - class: "big small wide short", - } - - // + button { + // ..props.attrs, + class: "abc123", + style: { a: 2, b: 3, c: 4 }, + onclick: move |evt| { + log::info("hello world"); + }, + onfocus, + onhover: props.onhover, - - button { "Jill!", - class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" - onclick: move |_| set_name("jill"), - onclick: move |_| set_name("jill"), - } - - button { - class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" - onclick: move |_| set_name("jill"), - onclick: move |_| set_name("jill"), - // this is valid - "Jill!", - // this is also valid - {"Jill!"} - } - - h1 { "Text", class: "inline-block py-4 px-8 mr-6 leading-none" } - - //

- // "Text" - //

- - h1 { - div { - h1 {} - h2 {} - Brick {} - - p {} - p { - tag: "type", - abc: 123, - enabled: true, - class: "big small wide short", - - a { "abcder" }, - h2 { "whatsup", class: "abc-123" }, - CustomComponent { a: 123, b: 456, key: "1" }, - } - - div { class: "big small wide short", - div {}, - div {}, - div {}, - div {}, - } - } - } - - h2 {} - h3 {} - "abcd123" + Custom1 { a: 123 } + Custom2 { a: 456, "abc", h1 {"1"}, h2 {"2"} } + Custom3 { a: "sometext goes here" } + Custom4 { onclick: |evt| log::info("click") } } }) } + +// h1 { +// tag: "type", abc: 123, class: "big small wide short", +// "title1" +// "title1" +// "title1" +// "title" +// } + +// h1 ("title") { +// tag: "type", +// abc: 123, +// class: "big small wide short", +// } + +// // + +// button { "Jill!", +// class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" +// onclick: move |_| set_name("jill"), +// onclick: move |_| set_name("jill"), +// } + +// button { +// class: "inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow" +// onclick: move |_| set_name("jill"), +// onclick: move |_| set_name("jill"), +// // this is valid +// "Jill!", +// // this is also valid +// {"Jill!"} +// } + +// h1 { "Text", class: "inline-block py-4 px-8 mr-6 leading-none" } + +// //

+// // "Text" +// //

+ +// h1 { +// div { +// h1 {} +// h2 {} +// Brick {} + +// p {} +// p { +// tag: "type", +// abc: 123, +// enabled: true, +// class: "big small wide short", + +// a { "abcder" }, +// h2 { "whatsup", class: "abc-123" }, +// CustomComponent { a: 123, b: 456, key: "1" }, +// } + +// div { class: "big small wide short", +// div {}, +// div {}, +// div {}, +// div {}, +// } +// } +// } + +// h2 {} +// h3 {} +// "abcd123"