From 9f7f43b6614aaef2d7dded7058e81934f28f5dec Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 22 Mar 2021 23:52:54 -0400 Subject: [PATCH] Feat: building large apps, revamp macro --- Cargo.toml | 1 + packages/core-macro/src/htm.rs | 4 +- packages/core-macro/src/rsxt.rs | 91 +++++--- packages/core/examples/alternative.rs | 104 ++-------- packages/core/examples/borrowed.rs | 4 +- packages/core/examples/contextapi.rs | 4 +- packages/core/examples/fc.rs | 48 ++++- packages/core/examples/nested.rs | 4 +- packages/core/examples/rsx_usage.rs | 1 - packages/core/src/context.rs | 11 +- packages/core/src/events.rs | 13 +- packages/core/src/lib.rs | 2 + packages/core/src/nodebuilder.rs | 276 +++++++++++-------------- packages/core/src/nodes.rs | 2 +- packages/docsite/Cargo.toml | 10 + packages/docsite/README.md | 10 + packages/docsite/src/components/app.rs | 0 packages/docsite/src/main.rs | 82 ++++++++ packages/ssr/src/lib.rs | 4 + packages/web/examples/rsxt.rs | 8 +- 20 files changed, 386 insertions(+), 293 deletions(-) create mode 100644 packages/docsite/Cargo.toml create mode 100644 packages/docsite/README.md create mode 100644 packages/docsite/src/components/app.rs create mode 100644 packages/docsite/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index f73d060ec..235754743 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "packages/web", "packages/liveview", "packages/3d", + "packages/docsite", ] # Built-in diff --git a/packages/core-macro/src/htm.rs b/packages/core-macro/src/htm.rs index 37566c2fb..315d77124 100644 --- a/packages/core-macro/src/htm.rs +++ b/packages/core-macro/src/htm.rs @@ -58,11 +58,11 @@ impl ToTokens for HtmlRender { // create a lazy tree that accepts a bump allocator let final_tokens = quote! { - move |ctx| { + dioxus::prelude::LazyNodes::new(move |ctx| { let bump = ctx.bump; #new_toks - } + }) }; final_tokens.to_tokens(out_tokens); diff --git a/packages/core-macro/src/rsxt.rs b/packages/core-macro/src/rsxt.rs index 66e34df60..4c9a7e8d5 100644 --- a/packages/core-macro/src/rsxt.rs +++ b/packages/core-macro/src/rsxt.rs @@ -30,6 +30,10 @@ ctx.render(rsx!{ }) ``` +optionally, include the allocator directly + +rsx!(ctx, div { "hello"} ) + each element is given by tag { [Attr] } */ @@ -50,18 +54,31 @@ use { // Parse any stream coming from the rsx! macro // ============================================== pub struct RsxRender { + custom_context: Option, el: Element, } impl Parse for RsxRender { fn parse(input: ParseStream) -> Result { + let fork = input.fork(); + + let custom_context = fork + .parse::() + .and_then(|ident| { + fork.parse::().map(|_| { + input.advance_to(&fork); + ident + }) + }) + .ok(); + // cannot accept multiple elements // can only accept one root per component // fragments can be used as // todo // enable fragements by autocoerrcing into list let el: Element = input.parse()?; - Ok(Self { el }) + Ok(Self { el, custom_context }) } } @@ -71,11 +88,19 @@ impl ToTokens for RsxRender { // let new_toks = ToToksCtx::new(&self.kind).to_token_stream(); // create a lazy tree that accepts a bump allocator - let final_tokens = quote! { - move |ctx: &dioxus::prelude::NodeCtx<'_>| -> dioxus::prelude::VNode<'_>{ - let bump = ctx.bump; - #new_toks - } + let final_tokens = match &self.custom_context { + Some(ident) => quote! { + #ident.render(dioxus::prelude::LazyNodes::new(move |ctx|{ + let bump = ctx.bump; + #new_toks + })) + }, + None => quote! { + dioxus::prelude::LazyNodes::new(move |ctx|{ + let bump = ctx.bump; + #new_toks + }) + }, }; final_tokens.to_tokens(out_tokens); @@ -102,29 +127,32 @@ impl ToTokens for ToToksCtx<&Node> { } impl Parse for Node { - fn parse(s: ParseStream) -> Result { - let fork1 = s.fork(); - let fork2 = s.fork(); + fn parse(stream: ParseStream) -> Result { + let fork1 = stream.fork(); + let fork2 = stream.fork(); // todo: map issues onto the second fork if any arise // it's unlikely that textnodes or components would fail? let ret = if let Ok(text) = fork1.parse::() { - s.advance_to(&fork1); + stream.advance_to(&fork1); Self::Text(text) } else if let Ok(element) = fork2.parse::() { - s.advance_to(&fork2); + stream.advance_to(&fork2); Self::Element(element) - } else if let Ok(comp) = s.parse::() { + } else if let Ok(comp) = stream.parse::() { Self::Component(comp) } else { - return Err(Error::new(s.span(), "Failed to parse as a valid child")); + return Err(Error::new( + stream.span(), + "Failed to parse as a valid child", + )); }; // consume comma if it exists // we don't actually care if there *are* commas after elements/text - if s.peek(Token![,]) { - let _ = s.parse::(); + if stream.peek(Token![,]) { + let _ = stream.parse::(); } Ok(ret) } @@ -301,7 +329,7 @@ impl ToTokens for ToToksCtx<&Element> { } match &self.inner.children { MaybeExpr::Expr(expr) => tokens.append_all(quote! { - .children(#expr) + .iter_child(#expr) }), MaybeExpr::Literal(nodes) => { let mut children = nodes.iter(); @@ -340,7 +368,7 @@ impl Parse for Element { let mut attrs: Vec = vec![]; let mut children: Vec = vec![]; - parse_element_content(content, &mut attrs, &mut children); + parse_element_content(content, &mut attrs, &mut children)?; let children = MaybeExpr::Literal(children); @@ -353,7 +381,11 @@ impl Parse for Element { } // used by both vcomponet and velement to parse the contents of the elements into attras and children -fn parse_element_content(content: ParseBuffer, attrs: &mut Vec, children: &mut Vec) { +fn parse_element_content( + stream: ParseBuffer, + attrs: &mut Vec, + children: &mut Vec, +) -> Result<()> { 'parsing: loop { // todo move this around into a more functional style // [1] Break if empty @@ -363,38 +395,47 @@ fn parse_element_content(content: ParseBuffer, attrs: &mut Vec, children: // [last] Fail if none worked // [1] Break if empty - if content.is_empty() { + if stream.is_empty() { break 'parsing; } // [2] Try to consume an attr - let fork = content.fork(); + let fork = stream.fork(); if let Ok(attr) = fork.parse::() { // make sure to advance or your computer will become a spaceheater :) - content.advance_to(&fork); + stream.advance_to(&fork); attrs.push(attr); continue 'parsing; } // [3] Try to consume a child node - let fork = content.fork(); + let fork = stream.fork(); if let Ok(node) = fork.parse::() { // make sure to advance or your computer will become a spaceheater :) - content.advance_to(&fork); + stream.advance_to(&fork); children.push(node); continue 'parsing; } // [4] TODO: Parsing brackets - // let fork = content.fork(); + + if stream.peek(token::Brace) { + // this can fail (mismatched brackets) + let content; + syn::braced!(content in &stream); + continue 'parsing; + // let fork = content.fork(); + // stream.advance_to(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) + panic!("Entry is not an attr or element\n {}", stream) } + Ok(()) } /// ======================================= diff --git a/packages/core/examples/alternative.rs b/packages/core/examples/alternative.rs index 637c5d4f0..25ea11f33 100644 --- a/packages/core/examples/alternative.rs +++ b/packages/core/examples/alternative.rs @@ -1,93 +1,17 @@ -// //! An alternative function syntax -// //! - -// use bumpalo::Bump; -// use dioxus_core::prelude::VNode; - fn main() {} -// struct Context2<'a, P> { -// _props: &'a P, // _p: PhantomData<&'a ()>, -// rops: &'a P, // _p: PhantomData<&'a ()>, -// } -// impl<'a, P> Context2<'a, P> { -// fn render(self, _f: impl FnOnce(&'a Bump) -> VNode<'a>) -> DTree { -// DTree {} -// } +use dioxus_core::prelude::*; -// fn props(&self) -> &'a P { -// todo!() -// } - -// pub fn use_hook<'scope, InternalHookState: 'static, Output: 'a>( -// &'scope self, -// _initializer: impl FnOnce() -> InternalHookState, -// _runner: impl FnOnce(&'a mut InternalHookState) -> Output, -// _cleanup: impl FnOnce(InternalHookState), -// ) -> Output { -// todo!() -// } -// } - -// trait Properties {} - -// struct DTree; -// // type FC2<'a, T: 'a> = fn(Context2) -> DTree; -// fn virtual_child<'a, T: 'a>(_bump: &'a Bump, _props: T, _f: FC2) -> VNode<'a> { -// todo!() -// } - -// struct Props { -// c: String, -// } - -// fn Example(ctx: Context2) -> DTree { -// let val = use_state(&ctx, || String::from("asd")); -// let props = ctx.props(); - -// ctx.render(move |b| { -// dioxus_core::nodebuilder::div(b) -// .child(dioxus_core::nodebuilder::text(props.c.as_str())) -// .child(virtual_child(b, Props2 { a: val }, AltChild)) -// .finish() -// }) -// } - -// // #[fc] -// fn Example2(ctx: Context2<()>, name: &str, _blah: &str) -> DTree { -// let val = use_state(&ctx, || String::from("asd")); - -// ctx.render(move |ctx| { -// dioxus_core::builder::ElementBuilder::new(b, "div") -// .child(dioxus_core::nodebuilder::text(name)) -// .child(virtual_child(b, Props2 { a: val }, AltChild)) -// .finish() -// }) -// } - -// type FC2<'a, T> = fn(Context2) -> DTree; - -// // still works if you don't take any references in your props (ie, something copy or cloneable) -// static CHILD: FC2 = |_ctx: Context2| { -// // -// todo!() -// }; - -// struct Props2<'a> { -// a: &'a String, -// } -// impl Properties for Props2<'_> {} - -// fn AltChild(ctx: Context2) -> DTree { -// ctx.render(|_b| { -// // -// todo!() -// }) -// } - -// fn use_state<'a, 'c, P, T: 'static, F: FnOnce() -> T>( -// _ctx: &'_ Context2<'a, P>, -// _initial_state_fn: F, -// ) -> &'a T { -// todo!() -// } +static Example: FC<()> = |ctx, props| { + ctx.render(dioxus_core::prelude::LazyNodes::new(move |ctx| { + let bump = ctx.bump; + dioxus::builder::ElementBuilder::new(ctx, "h1") + .children([{ + use bumpalo::core_alloc::fmt::Write; + let mut s = bumpalo::collections::String::new_in(bump); + write!(s, "hello"); + dioxus::builder::text2(s) + }]) + .finish() + })) +}; diff --git a/packages/core/examples/borrowed.rs b/packages/core/examples/borrowed.rs index 45f6ed3dd..e076a23cf 100644 --- a/packages/core/examples/borrowed.rs +++ b/packages/core/examples/borrowed.rs @@ -22,7 +22,7 @@ struct ListItem { fn app(ctx: Context, props: &Props) -> DomTree { let (_f, setter) = use_state(&ctx, || 0); - ctx.render(move |c| { + ctx.render(dioxus::prelude::LazyNodes::new(move |c| { let mut root = builder::ElementBuilder::new(c, "div"); for child in &props.items { // notice that the child directly borrows from our vec @@ -39,7 +39,7 @@ fn app(ctx: Context, props: &Props) -> DomTree { )); } root.finish() - }) + })) } type StateSetter = dyn Fn(T); diff --git a/packages/core/examples/contextapi.rs b/packages/core/examples/contextapi.rs index bef23122a..b3f01c2df 100644 --- a/packages/core/examples/contextapi.rs +++ b/packages/core/examples/contextapi.rs @@ -13,7 +13,7 @@ struct Props { static Example: FC = |ctx, props| { let value = ctx.use_context(|c: &SomeContext| c.items.last().unwrap()); - ctx.render(move |bump| { + ctx.render(LazyNodes::new(move |bump| { builder::ElementBuilder::new(bump, "button") .on("click", move |_| { println!("Value is {}", props.name); @@ -24,7 +24,7 @@ static Example: FC = |ctx, props| { println!("Value is {}", props.name); }) .finish() - }) + })) // ctx.render(html! { //
//