From 62d4ad58787185032100a2d25e79b70f6ec97a3c Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 3 Feb 2021 14:07:07 -0500 Subject: [PATCH] wip: comment out examples and move lifetime in FC type --- examples/Cargo.toml | 48 ++++++------- packages/core-macro/src/lib.rs | 11 +-- packages/core/.vscode/settings.json | 2 +- packages/core/Cargo.toml | 2 +- packages/core/README.md | 34 ++++++++- packages/core/examples/dummy.rs | 63 ++++++++++++++-- packages/core/examples/macrosrc.rs | 103 +++++++++++++++++++-------- packages/core/src/lib.rs | 10 +-- packages/core/src/nodebuilder.rs | 1 - packages/core/src/virtual_dom.rs | 44 ++++++------ packages/hooks/examples/lifecycle.rs | 96 ++++++++++++------------- packages/webview/src/lib.rs | 61 ++++++++-------- 12 files changed, 300 insertions(+), 175 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d779810f8..5fcbf981a 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -41,34 +41,34 @@ path = "common.rs" # Examples are manually keyed in # ================================ -[[example]] -path = "hello_web.rs" -name = "hello_web" +# [[example]] +# path = "hello_web.rs" +# name = "hello_web" -[[example]] -path = "tide_ssr.rs" -name = "tide_ssr" +# [[example]] +# path = "tide_ssr.rs" +# name = "tide_ssr" -[[example]] -path = "doc_generator.rs" -name = "doc_generator" +# [[example]] +# path = "doc_generator.rs" +# name = "doc_generator" -[[example]] -path = "router.rs" -name = "router" +# [[example]] +# path = "router.rs" +# name = "router" -[[example]] -path = "fc_macro.rs" -name = "fc_macro" +# [[example]] +# path = "fc_macro.rs" +# name = "fc_macro" -[[example]] -path = "webview.rs" -name = "webview" +# [[example]] +# path = "webview.rs" +# name = "webview" -[[example]] -path = "blah.rs" -name = "blah" +# [[example]] +# path = "blah.rs" +# name = "blah" -[[example]] -path = "live.rs" -name = "live" +# [[example]] +# path = "live.rs" +# name = "live" diff --git a/packages/core-macro/src/lib.rs b/packages/core-macro/src/lib.rs index dabd6357a..6de70a470 100644 --- a/packages/core-macro/src/lib.rs +++ b/packages/core-macro/src/lib.rs @@ -45,8 +45,6 @@ fn function_component_impl( // )); // } - let ret_type = quote_spanned!(return_type.span()=> VNode); - let quoted = quote! { #[doc(hidden)] #[allow(non_camel_case_types)] @@ -54,14 +52,17 @@ fn function_component_impl( use super::*; #[derive(PartialEq)] - pub struct Props { - name: String + pub struct Props<'a> { + name: &'a str } - pub fn component(ctx: &mut Context) -> #ret_type { + pub fn component<'a>(ctx: &'a Context<'a, Props>) -> VNode<'a> { + // Destructure the props into the parent scope + // todo: handle expansion of lifetimes let Props { name } = ctx.props; + #block } } diff --git a/packages/core/.vscode/settings.json b/packages/core/.vscode/settings.json index 2f418701b..80f51cff8 100644 --- a/packages/core/.vscode/settings.json +++ b/packages/core/.vscode/settings.json @@ -1,3 +1,3 @@ { - "rust-analyzer.inlayHints.enable": true + "rust-analyzer.inlayHints.enable": false } \ No newline at end of file diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 410bb970c..46400244e 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -11,7 +11,7 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi [dependencies] dioxus-html-macro = { path = "../html-macro", version = "0.1.0" } - +dioxus-core-macro = { path = "../core-macro" } # Backs some static data once_cell = "1.5.2" diff --git a/packages/core/README.md b/packages/core/README.md index 53a934881..90b9e7e2d 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -6,8 +6,8 @@ This is the core crate for the Dioxus Virtual DOM. This README will focus on the Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus borrows these concepts: - React: hooks, concurrency, suspense -- Dodrio: bump allocation / custom faster allocators, stack machine for fast changes -- Percy: html! macro implementation and agnostic edit list +- Dodrio: bump allocation, double buffering +- Percy: html! macro architecture, platform-agnostic edits - Yew: passion and inspiration ❤️ ## Goals @@ -15,6 +15,7 @@ Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus We have big goals for Dioxus. The final implementation must: - Be **fast**. Allocators are typically slow in WASM/Rust, so we should have a smart way of allocating. +- Be extremely memory efficient. Servers should handle tens of thousands of simultaneous VDoms with no problem. - Be concurrent. Components should be able to pause rendering using a threading mechanism. - Support "broadcasting". Edit lists should be separate from the Renderer implementation. - Support SSR. VNodes should render to a string that can be served via a web server. @@ -30,10 +31,37 @@ We have big goals for Dioxus. The final implementation must: ## Design Quirks - Use of "Context" as a way of mitigating threading issues and the borrow checker. (JS relies on globals) -- html! is lazy - needs to be used with a partner function to actually allocate the html. +- html! is lazy - needs to be used with a partner function to actually allocate the html. (Good be a good thing or a bad thing) ```rust let text = TextRenderer::render(html! {
"hello world"
}); //
hello world
``` +```rust + +fn main() { + tide::new() + .get("blah", serve_app("../")) + .get("blah", ws_handler(serve_app)) +} + + +fn serve_app(ctx: &Context<()>) -> VNode { + let livecontext = LiveContext::new() + .with_handler("graph", graph_component) + .with_handler("graph", graph_component) + .with_handler("graph", graph_component) + .with_handler("graph", graph_component) + .with_handler("graph", graph_component) + .with_handler("graph", graph_component) + .build(); + + ctx.view(html! { + + + + }) +} + +``` diff --git a/packages/core/examples/dummy.rs b/packages/core/examples/dummy.rs index 1cc6a8706..930cc9f8b 100644 --- a/packages/core/examples/dummy.rs +++ b/packages/core/examples/dummy.rs @@ -1,11 +1,64 @@ -// #![allow(unused, non_upper_case_globals)] -// use bumpalo::Bump; -// use dioxus_core::nodebuilder::*; -// use dioxus_core::{nodes::DomTree, prelude::*}; -// use std::{collections::HashMap, future::Future, marker::PhantomData}; +#![allow(unused, non_upper_case_globals)] +use bumpalo::Bump; +use dioxus_core::prelude::VNode; +use dioxus_core::prelude::*; +use dioxus_core::{nodebuilder::*, virtual_dom::Properties}; +use once_cell::sync::{Lazy, OnceCell}; +use std::{collections::HashMap, future::Future, marker::PhantomData}; fn main() {} +// struct VC) -> VNode> { +// f: F, +// _a: std::marker::PhantomData<(P, F)>, // cell: OnceCell, +// // init: Cell> +// } +// impl VC { +// const fn new(init: F) -> VC { +// Self { +// _a: std::marker::PhantomData {}, +// f: init, +// } +// } +// fn builder() -> P { +// // P::new() +// } +// } + +// // Build a new functional component +// static SomeComp: VC<()> = VC::new(|ctx| { +// // This is a component, apparently +// // still not useful because we can't have bounds + +// ctx.view(html! { +//
+ +//
+// }) +// }); + +/* + + + + + + + + + + + +*/ +static BILL: Lazy) -> String> = Lazy::new(|| { + // + |c| "BLAH".to_string() +}); + +// struct FUNC T> {} + +struct SomeBuilder {} + // struct DummyRenderer { // alloc: Bump, // } diff --git a/packages/core/examples/macrosrc.rs b/packages/core/examples/macrosrc.rs index 893744763..455b489c9 100644 --- a/packages/core/examples/macrosrc.rs +++ b/packages/core/examples/macrosrc.rs @@ -1,19 +1,19 @@ -#![allow(unused, non_upper_case_globals)] +#![allow(unused, non_upper_case_globals, non_snake_case)] use bumpalo::Bump; use dioxus_core::prelude::*; use dioxus_core::{nodebuilder::*, virtual_dom::DomTree}; use std::{collections::HashMap, future::Future, marker::PhantomData}; fn main() {} -struct Props<'a> { - use_macro: bool, - // todo uh not static - // incorporate lifetimes into the thing somehow - text: &'a str, +// ~~~ Text shared between components via props can be done with lifetimes! ~~~ +// Super duper efficient :) +struct Props { + blah: bool, + text: String, } -fn Component(ctx: Context) -> VNode { +fn Component<'a>(ctx: &'a Context) -> VNode<'a> { // Write asynchronous rendering code that immediately returns a "suspended" VNode // The concurrent API will then progress this component when the future finishes // You can suspend the entire component, or just parts of it @@ -25,30 +25,77 @@ fn Component(ctx: Context) -> VNode { } }); + ctx.view(html! { +
+ //

"Products"

+ // // Subnodes can even be suspended + // // When completely rendered, they won't cause the component itself to re-render, just their slot + //

{ product_list }

+
+ }) +} + +fn BuilderComp(ctx: Context) -> VNode { // VNodes can be constructed via a builder or the html! macro // However, both of these are "lazy" - they need to be evaluated (aka, "viewed") // We can "view" them with Context for ultimate speed while inside components - if ctx.props.use_macro { - ctx.view(|bump| { - div(bump) - .attr("class", "edit") - .child(text("Hello")) - .child(text(ctx.props.text)) - .finish() - }) - } else { - // "View" indicates exactly *when* allocations take place, everything is lazy up to this point - ctx.view(html! { -
- // TODO! - // Get all this working again - //

"Products"

- // // Subnodes can even be suspended - // // When completely rendered, they won't cause the component itself to re-render, just their slot - //

{product_list}

-
- }) - } + ctx.view(|bump| { + div(bump) + .attr("class", "edit") + .child(text("Hello")) + .child(text(ctx.props.text.as_str())) + .finish() + }) +} + +#[fc] +fn EffcComp(ctx: &Context, name: &str) -> VNode { + // VNodes can be constructed via a builder or the html! macro + // However, both of these are "lazy" - they need to be evaluated (aka, "viewed") + // We can "view" them with Context for ultimate speed while inside components + // use "phase" style allocation; + /* + nodes... + text... + attrs... +
// node0 +
// node1 + {// support some expression} // node 2 +
+ let node0; + let node1; + let node2 = evaluate{}.into(); + let g= |bump| {1}; + g(bump).into() + + */ + + // should we automatically view the output or leave it? + ctx.view(html! { +
+ // your template goes here + // feel free to directly use "name" +
+ }) +} + +fn FullySuspended(ctx: Context) -> VNode { + ctx.suspend(async { + let i: i32 = 0; + + // full suspended works great with just returning VNodes! + let tex = match i { + 1 => html! {
}, + 2 => html! {
}, + _ => html! {
}, + }; + + if ctx.props.blah { + html! {
} + } else { + tex + } + }) } /// An example of a datafetching service diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 3f2c35978..5357c328a 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -81,11 +81,8 @@ pub mod prelude { pub use nodes::*; // pub use nodes::iterables::IterableNodes; - // FC is a shorthand for statics to easily declare a function that takes a context and produces a dom tree - // The DomTree is lazy and needs to be evaluated with an arena for any allocation to take place - // Hmmm, maybe get statics to work again? - // pub type FC<'a, T> = fn(&'a Context) -> VNode<'a>; - pub use crate::virtual_dom::{__domtree_helper, FC}; + /// This type alias is an internal way of abstracting over the static functions that represent components. + pub type FC

= for<'a> fn(&'a Context

) -> VNode<'a>; // TODO @Jon, fix this // hack the VNode type until VirtualNode is fixed in the macro crate @@ -93,4 +90,7 @@ pub mod prelude { // Re-export from the macro crate pub use dioxus_html_macro::html; + + // Re-export the FC macro + pub use dioxus_core_macro::fc; } diff --git a/packages/core/src/nodebuilder.rs b/packages/core/src/nodebuilder.rs index e8281fe4d..a152da27e 100644 --- a/packages/core/src/nodebuilder.rs +++ b/packages/core/src/nodebuilder.rs @@ -2,7 +2,6 @@ use crate::nodes::{Attribute, Listener, NodeKey, VNode}; type Node<'g> = VNode<'g>; -// use crate::{node::NodeKey, Attribute, Listener, Node, RootRender, VdomWeak}; use bumpalo::Bump; /// A virtual DOM element builder. diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index c959b6548..ccc971399 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -71,7 +71,7 @@ impl VirtualDom { /// /// This means that the root component must either consumes its own context, or statics are used to generate the page. /// The root component can access things like routing in its context. - pub fn new(root: FC<(), F>) -> Self { + pub fn new(root: FC<()>) -> Self { Self::new_with_props(root) } @@ -80,7 +80,7 @@ impl VirtualDom { /// /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive /// to toss out the entire tree. - pub fn new_with_props(root: FC) -> Self { + pub fn new_with_props(root: FC

) -> Self { Self { components: Arena::new(), event_queue: vec![], @@ -116,19 +116,6 @@ where } } -/// This type alias is an internal way of abstracting over the static functions that represent components. -pub type FC = fn(&Context

) -> F; - -/// A gross AF helper that connects the dots for lifetimes -/// sigh.. todo: move this into the html macro or core crates -/// https://github.com/rust-lang/rust/issues/41078 -#[inline] -pub fn __domtree_helper( - arg: impl for<'a> FnOnce(&'a Bump) -> VNode<'a>, -) -> impl for<'a> FnOnce(&'a Bump) -> VNode<'a> { - arg -} - /// The `Component` trait refers to any struct or funciton that can be used as a component /// We automatically implement Component for FC pub trait Component { @@ -138,7 +125,7 @@ pub trait Component { // Auto implement component for a FC // Calling the FC is the same as "rendering" it -impl<'a, P: Properties, F: DomTree> Component for FC { +impl Component for FC

{ type Props = P; fn builder(&self) -> Self::Props { @@ -235,14 +222,14 @@ pub struct HookState {} /// } /// } /// ``` -pub struct Context { - // pub struct Context<'source, T> { +// todo: force lifetime of source into T as a valid lifetime too +// it's definitely possible, just needs some more messing around +pub struct Context<'source, T> { /// Direct access to the properties used to create this component. - pub props: T, - // pub props: &'source T, + pub props: &'source T, } -impl<'a, T> Context { +impl<'a, T> Context<'a, T> { // impl<'a, T> Context<'a, T> { /// Access the children elements passed into the component pub fn children(&self) -> Vec { @@ -260,9 +247,18 @@ impl<'a, T> Context { || {} } - /// Create VNodes from macros that use efficient allcators - /// The VDOM is configured to use a special allocator for VNodes - pub fn view(&self, v: impl Fn(&'a Bump) -> VNode<'a>) -> VNode<'a> { + /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator. + /// + /// ```ignore + /// fn Component(ctx: Context) -> VNode { + /// // Lazy assemble the VNode tree + /// let lazy_tree = html! {

"Hello World"
}; + /// + /// // Actually build the tree and allocate it + /// ctx.view(lazy_tree) + /// } + ///``` + pub fn view(&self, v: impl FnOnce(&'a Bump) -> VNode<'a>) -> VNode<'a> { todo!() } diff --git a/packages/hooks/examples/lifecycle.rs b/packages/hooks/examples/lifecycle.rs index d2fca44e4..5b3cd7093 100644 --- a/packages/hooks/examples/lifecycle.rs +++ b/packages/hooks/examples/lifecycle.rs @@ -1,57 +1,57 @@ use dioxus_core::prelude::*; fn main() { - let mut s = Context { props: &() }; - let g = Component(&mut s); + // let mut s = Context { props: &() }; + // let g = Component(&mut s); } -struct CompState { - tasks: Vec<()>, -} -enum Actions { - Add, - MoveUp, - MoveDown, - Remvoe, -} +// struct CompState { +// tasks: Vec<()>, +// } +// enum Actions { +// Add, +// MoveUp, +// MoveDown, +// Remvoe, +// } -static Component: FC<()> = |ctx| { - let (tasks, dispatch) = use_reducer( - ctx, - || CompState { tasks: Vec::new() }, - |state, action: Actions| match action { - Actions::Add => state, - Actions::MoveUp => state, - Actions::MoveDown => state, - Actions::Remvoe => state, - }, - ); +// static Component: FC<()> = |ctx| { +// let (tasks, dispatch) = use_reducer( +// ctx, +// || CompState { tasks: Vec::new() }, +// |state, action: Actions| match action { +// Actions::Add => state, +// Actions::MoveUp => state, +// Actions::MoveDown => state, +// Actions::Remvoe => state, +// }, +// ); - let tasklist = { (0..10).map(|f| html! {
  • }) }.collect::>(); +// let tasklist = { (0..10).map(|f| html! {
  • }) }.collect::>(); - html! { -
    -
    -

    "Tasks: "

    -
      - {tasklist} -
    -
    -
    - - - - -
    -
    - } -}; +// html! { +//
    +//
    +//

    "Tasks: "

    +//
      +// {tasklist} +//
    +//
    +//
    +// +// +// +// +//
    +//
    +// } +// }; -fn use_reducer( - ctx: &mut Context, - init: fn() -> State, - reducer: fn(State, Action) -> State, -) -> (State, impl Fn(Action)) { - let ii = init(); - (ii, |_| {}) -} +// fn use_reducer( +// ctx: &mut Context, +// init: fn() -> State, +// reducer: fn(State, Action) -> State, +// ) -> (State, impl Fn(Action)) { +// let ii = init(); +// (ii, |_| {}) +// } diff --git a/packages/webview/src/lib.rs b/packages/webview/src/lib.rs index 0a87cedcc..1120c678d 100644 --- a/packages/webview/src/lib.rs +++ b/packages/webview/src/lib.rs @@ -38,38 +38,39 @@ impl WebviewRenderer { } pub fn launch(self, props: T) { - let mut ctx = Context { props: &props }; - let WebviewRenderer { root } = self; - let content = root(&mut ctx); - let html_content = content.to_string(); - /* - TODO: @Jon - Launch the webview with a premade VDom app - */ + todo!() + // let mut ctx = Context { props: &props }; + // let WebviewRenderer { root } = self; + // let content = root(&mut ctx); + // let html_content = content.to_string(); + // /* + // TODO: @Jon + // Launch the webview with a premade VDom app + // */ - web_view::builder() - .title("My Project") - .content(web_view::Content::Html(html_content)) - .size(320, 480) - .resizable(true) - .debug(true) - .user_data(()) - .invoke_handler(|_webview, _arg| Ok(())) - .run() - .unwrap(); + // web_view::builder() + // .title("My Project") + // .content(web_view::Content::Html(html_content)) + // .size(320, 480) + // .resizable(true) + // .debug(true) + // .user_data(()) + // .invoke_handler(|_webview, _arg| Ok(())) + // .run() + // .unwrap(); } } -mod receiver { - use dioxus_core::prelude::*; +// mod receiver { +// use dioxus_core::prelude::*; - /// The receiver app is a container that builds a connection to the host process that shuttles events and patches. - pub(crate) static ReceiverApp: FC<()> = |ctx| { - // - html! { -
    - {} -
    - } - }; -} +// /// The receiver app is a container that builds a connection to the host process that shuttles events and patches. +// pub(crate) static ReceiverApp: FC<()> = |ctx| { +// // +// html! { +//
    +// {} +//
    +// } +// }; +// }