wip: comment out examples and move lifetime in FC type

This commit is contained in:
Jonathan Kelley committed 2021-02-03 14:07:07 -05:00
1 parent 07341d2c65
commit 62d4ad5878
12 files changed
+300 -175

No files matched your search

+24 -24
View File
@@ -41,34 +41,34 @@ path = "common.rs"
# Examples are manually keyed in # Examples are manually keyed in
# ================================ # ================================
[[example]] # [[example]]
path = "hello_web.rs" # path = "hello_web.rs"
name = "hello_web" # name = "hello_web"
[[example]] # [[example]]
path = "tide_ssr.rs" # path = "tide_ssr.rs"
name = "tide_ssr" # name = "tide_ssr"
[[example]] # [[example]]
path = "doc_generator.rs" # path = "doc_generator.rs"
name = "doc_generator" # name = "doc_generator"
[[example]] # [[example]]
path = "router.rs" # path = "router.rs"
name = "router" # name = "router"
[[example]] # [[example]]
path = "fc_macro.rs" # path = "fc_macro.rs"
name = "fc_macro" # name = "fc_macro"
[[example]] # [[example]]
path = "webview.rs" # path = "webview.rs"
name = "webview" # name = "webview"
[[example]] # [[example]]
path = "blah.rs" # path = "blah.rs"
name = "blah" # name = "blah"
[[example]] # [[example]]
path = "live.rs" # path = "live.rs"
name = "live" # name = "live"
+6 -5
View File
@@ -45,8 +45,6 @@ fn function_component_impl(
// )); // ));
// } // }
let ret_type = quote_spanned!(return_type.span()=> VNode);
let quoted = quote! { let quoted = quote! {
#[doc(hidden)] #[doc(hidden)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
@@ -54,14 +52,17 @@ fn function_component_impl(
use super::*; use super::*;
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct Props { pub struct Props<'a> {
name: String name: &'a str
} }
pub fn component(ctx: &mut Context<Props>) -> #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 { let Props {
name name
} = ctx.props; } = ctx.props;
#block #block
} }
} }
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"rust-analyzer.inlayHints.enable": true "rust-analyzer.inlayHints.enable": false
} }
+1 -1
View File
@@ -11,7 +11,7 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi
[dependencies] [dependencies]
dioxus-html-macro = { path = "../html-macro", version = "0.1.0" } dioxus-html-macro = { path = "../html-macro", version = "0.1.0" }
dioxus-core-macro = { path = "../core-macro" }
# Backs some static data # Backs some static data
once_cell = "1.5.2" once_cell = "1.5.2"
+31 -3
View File
@@ -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: Dioxus-core builds off the many frameworks that came before it. Notably, Dioxus borrows these concepts:
- React: hooks, concurrency, suspense - React: hooks, concurrency, suspense
- Dodrio: bump allocation / custom faster allocators, stack machine for fast changes - Dodrio: bump allocation, double buffering
- Percy: html! macro implementation and agnostic edit list - Percy: html! macro architecture, platform-agnostic edits
- Yew: passion and inspiration ❤️ - Yew: passion and inspiration ❤️
## Goals ## 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: 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 **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. - 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 "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. - 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 ## Design Quirks
- Use of "Context" as a way of mitigating threading issues and the borrow checker. (JS relies on globals) - 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 ```rust
let text = TextRenderer::render(html! {<div>"hello world"</div>}); let text = TextRenderer::render(html! {<div>"hello world"</div>});
// <div>hello world</div> // <div>hello world</div>
``` ```
```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! {
<LiveContext ctx={livecontext}>
<App />
</ LiveContext>
})
}
```
+58 -5
View File
@@ -1,11 +1,64 @@
// #![allow(unused, non_upper_case_globals)] #![allow(unused, non_upper_case_globals)]
// use bumpalo::Bump; use bumpalo::Bump;
// use dioxus_core::nodebuilder::*; use dioxus_core::prelude::VNode;
// use dioxus_core::{nodes::DomTree, prelude::*}; use dioxus_core::prelude::*;
// use std::{collections::HashMap, future::Future, marker::PhantomData}; use dioxus_core::{nodebuilder::*, virtual_dom::Properties};
use once_cell::sync::{Lazy, OnceCell};
use std::{collections::HashMap, future::Future, marker::PhantomData};
fn main() {} fn main() {}
// struct VC<P, F = fn(Context<P>) -> VNode> {
// f: F,
// _a: std::marker::PhantomData<(P, F)>, // cell: OnceCell<T>,
// // init: Cell<Option<F>>
// }
// impl<P, F> VC<P, F> {
// const fn new(init: F) -> VC<P, F> {
// 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! {
// <div>
// </div>
// })
// });
/*
*/
static BILL: Lazy<fn(Context<()>) -> String> = Lazy::new(|| {
//
|c| "BLAH".to_string()
});
// struct FUNC<F = fn() -> T> {}
struct SomeBuilder {}
// struct DummyRenderer { // struct DummyRenderer {
// alloc: Bump, // alloc: Bump,
// } // }
+75 -28
View File
@@ -1,19 +1,19 @@
#![allow(unused, non_upper_case_globals)] #![allow(unused, non_upper_case_globals, non_snake_case)]
use bumpalo::Bump; use bumpalo::Bump;
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
use dioxus_core::{nodebuilder::*, virtual_dom::DomTree}; use dioxus_core::{nodebuilder::*, virtual_dom::DomTree};
use std::{collections::HashMap, future::Future, marker::PhantomData}; use std::{collections::HashMap, future::Future, marker::PhantomData};
fn main() {} fn main() {}
struct Props<'a> {
use_macro: bool,
// todo uh not static // ~~~ Text shared between components via props can be done with lifetimes! ~~~
// incorporate lifetimes into the thing somehow // Super duper efficient :)
text: &'a str, struct Props {
blah: bool,
text: String,
} }
fn Component(ctx: Context<Props>) -> VNode { fn Component<'a>(ctx: &'a Context<Props>) -> VNode<'a> {
// Write asynchronous rendering code that immediately returns a "suspended" VNode // Write asynchronous rendering code that immediately returns a "suspended" VNode
// The concurrent API will then progress this component when the future finishes // The concurrent API will then progress this component when the future finishes
// You can suspend the entire component, or just parts of it // You can suspend the entire component, or just parts of it
@@ -25,30 +25,77 @@ fn Component(ctx: Context<Props>) -> VNode {
} }
}); });
ctx.view(html! {
<div>
// <h1> "Products" </h1>
// // Subnodes can even be suspended
// // When completely rendered, they won't cause the component itself to re-render, just their slot
// <p> { product_list } </p>
</div>
})
}
fn BuilderComp(ctx: Context<Props>) -> VNode {
// VNodes can be constructed via a builder or the html! macro // VNodes can be constructed via a builder or the html! macro
// However, both of these are "lazy" - they need to be evaluated (aka, "viewed") // 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 // We can "view" them with Context for ultimate speed while inside components
if ctx.props.use_macro { ctx.view(|bump| {
ctx.view(|bump| { div(bump)
div(bump) .attr("class", "edit")
.attr("class", "edit") .child(text("Hello"))
.child(text("Hello")) .child(text(ctx.props.text.as_str()))
.child(text(ctx.props.text)) .finish()
.finish() })
}) }
} else {
// "View" indicates exactly *when* allocations take place, everything is lazy up to this point #[fc]
ctx.view(html! { fn EffcComp(ctx: &Context, name: &str) -> VNode {
<div> // VNodes can be constructed via a builder or the html! macro
// TODO! // However, both of these are "lazy" - they need to be evaluated (aka, "viewed")
// Get all this working again // We can "view" them with Context for ultimate speed while inside components
// <h1>"Products"</h1> // use "phase" style allocation;
// // Subnodes can even be suspended /*
// // When completely rendered, they won't cause the component itself to re-render, just their slot nodes...
// <p> {product_list} </p> text...
</div> attrs...
}) <div> // node0
} <div> </div> // node1
{// support some expression} // node 2
</div>
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! {
<div>
// your template goes here
// feel free to directly use "name"
</div>
})
}
fn FullySuspended(ctx: Context<Props>) -> VNode {
ctx.suspend(async {
let i: i32 = 0;
// full suspended works great with just returning VNodes!
let tex = match i {
1 => html! { <div> </div> },
2 => html! { <div> </div> },
_ => html! { <div> </div> },
};
if ctx.props.blah {
html! { <div> </div> }
} else {
tex
}
})
} }
/// An example of a datafetching service /// An example of a datafetching service
+5 -5
View File
@@ -81,11 +81,8 @@ pub mod prelude {
pub use nodes::*; pub use nodes::*;
// pub use nodes::iterables::IterableNodes; // 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 /// This type alias is an internal way of abstracting over the static functions that represent components.
// The DomTree is lazy and needs to be evaluated with an arena for any allocation to take place pub type FC<P> = for<'a> fn(&'a Context<P>) -> VNode<'a>;
// Hmmm, maybe get statics to work again?
// pub type FC<'a, T> = fn(&'a Context<T>) -> VNode<'a>;
pub use crate::virtual_dom::{__domtree_helper, FC};
// TODO @Jon, fix this // TODO @Jon, fix this
// hack the VNode type until VirtualNode is fixed in the macro crate // 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 // Re-export from the macro crate
pub use dioxus_html_macro::html; pub use dioxus_html_macro::html;
// Re-export the FC macro
pub use dioxus_core_macro::fc;
} }
-1
View File
@@ -2,7 +2,6 @@
use crate::nodes::{Attribute, Listener, NodeKey, VNode}; use crate::nodes::{Attribute, Listener, NodeKey, VNode};
type Node<'g> = VNode<'g>; type Node<'g> = VNode<'g>;
// use crate::{node::NodeKey, Attribute, Listener, Node, RootRender, VdomWeak};
use bumpalo::Bump; use bumpalo::Bump;
/// A virtual DOM element builder. /// A virtual DOM element builder.
+20 -24
View File
@@ -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. /// 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. /// The root component can access things like routing in its context.
pub fn new<F: DomTree>(root: FC<(), F>) -> Self { pub fn new(root: FC<()>) -> Self {
Self::new_with_props(root) 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 /// 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. /// to toss out the entire tree.
pub fn new_with_props<P: Properties, F: DomTree>(root: FC<P, F>) -> Self { pub fn new_with_props<P: Properties>(root: FC<P>) -> Self {
Self { Self {
components: Arena::new(), components: Arena::new(),
event_queue: vec![], 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<P: Properties, F: DomTree> = fn(&Context<P>) -> 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 /// The `Component` trait refers to any struct or funciton that can be used as a component
/// We automatically implement Component for FC<T> /// We automatically implement Component for FC<T>
pub trait Component { pub trait Component {
@@ -138,7 +125,7 @@ pub trait Component {
// Auto implement component for a FC // Auto implement component for a FC
// Calling the FC is the same as "rendering" it // Calling the FC is the same as "rendering" it
impl<'a, P: Properties, F: DomTree> Component for FC<P, F> { impl<P: Properties> Component for FC<P> {
type Props = P; type Props = P;
fn builder(&self) -> Self::Props { fn builder(&self) -> Self::Props {
@@ -235,14 +222,14 @@ pub struct HookState {}
/// } /// }
/// } /// }
/// ``` /// ```
pub struct Context<T> { // todo: force lifetime of source into T as a valid lifetime too
// pub struct Context<'source, T> { // 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. /// 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<T> { impl<'a, T> Context<'a, T> {
// impl<'a, T> Context<'a, T> { // impl<'a, T> Context<'a, T> {
/// Access the children elements passed into the component /// Access the children elements passed into the component
pub fn children(&self) -> Vec<VNode> { pub fn children(&self) -> Vec<VNode> {
@@ -260,9 +247,18 @@ impl<'a, T> Context<T> {
|| {} || {}
} }
/// Create VNodes from macros that use efficient allcators /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
/// The VDOM is configured to use a special allocator for VNodes ///
pub fn view(&self, v: impl Fn(&'a Bump) -> VNode<'a>) -> VNode<'a> { /// ```ignore
/// fn Component(ctx: Context<Props>) -> VNode {
/// // Lazy assemble the VNode tree
/// let lazy_tree = html! {<div>"Hello World"</div>};
///
/// // 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!() todo!()
} }
+48 -48
View File
@@ -1,57 +1,57 @@
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
fn main() { fn main() {
let mut s = Context { props: &() }; // let mut s = Context { props: &() };
let g = Component(&mut s); // let g = Component(&mut s);
} }
struct CompState { // struct CompState {
tasks: Vec<()>, // tasks: Vec<()>,
} // }
enum Actions { // enum Actions {
Add, // Add,
MoveUp, // MoveUp,
MoveDown, // MoveDown,
Remvoe, // Remvoe,
} // }
static Component: FC<()> = |ctx| { // static Component: FC<()> = |ctx| {
let (tasks, dispatch) = use_reducer( // let (tasks, dispatch) = use_reducer(
ctx, // ctx,
|| CompState { tasks: Vec::new() }, // || CompState { tasks: Vec::new() },
|state, action: Actions| match action { // |state, action: Actions| match action {
Actions::Add => state, // Actions::Add => state,
Actions::MoveUp => state, // Actions::MoveUp => state,
Actions::MoveDown => state, // Actions::MoveDown => state,
Actions::Remvoe => state, // Actions::Remvoe => state,
}, // },
); // );
let tasklist = { (0..10).map(|f| html! { <li></li> }) }.collect::<Vec<_>>(); // let tasklist = { (0..10).map(|f| html! { <li></li> }) }.collect::<Vec<_>>();
html! { // html! {
<div> // <div>
<div> // <div>
<h1>"Tasks: "</h1> // <h1>"Tasks: "</h1>
<ul> // <ul>
{tasklist} // {tasklist}
</ul> // </ul>
</div> // </div>
<div> // <div>
<button onclick=|_| dispatch(Action::Add)>{"Add"}</button> // <button onclick=|_| dispatch(Action::Add)>{"Add"}</button>
<button onclick=|_| dispatch(Action::MoveUp)>{"MoveUp"}</button> // <button onclick=|_| dispatch(Action::MoveUp)>{"MoveUp"}</button>
<button onclick=|_| dispatch(Action::MoveDown)>{"MoveDown"}</button> // <button onclick=|_| dispatch(Action::MoveDown)>{"MoveDown"}</button>
<button onclick=|_| dispatch(Action::Remvoe)>{"Remvoe"}</button> // <button onclick=|_| dispatch(Action::Remvoe)>{"Remvoe"}</button>
</div> // </div>
</div> // </div>
} // }
}; // };
fn use_reducer<Props, State, Action>( // fn use_reducer<Props, State, Action>(
ctx: &mut Context<Props>, // ctx: &mut Context<Props>,
init: fn() -> State, // init: fn() -> State,
reducer: fn(State, Action) -> State, // reducer: fn(State, Action) -> State,
) -> (State, impl Fn(Action)) { // ) -> (State, impl Fn(Action)) {
let ii = init(); // let ii = init();
(ii, |_| {}) // (ii, |_| {})
} // }
+31 -30
View File
@@ -38,38 +38,39 @@ impl<T> WebviewRenderer<T> {
} }
pub fn launch(self, props: T) { pub fn launch(self, props: T) {
let mut ctx = Context { props: &props }; todo!()
let WebviewRenderer { root } = self; // let mut ctx = Context { props: &props };
let content = root(&mut ctx); // let WebviewRenderer { root } = self;
let html_content = content.to_string(); // let content = root(&mut ctx);
/* // let html_content = content.to_string();
TODO: @Jon // /*
Launch the webview with a premade VDom app // TODO: @Jon
*/ // Launch the webview with a premade VDom app
// */
web_view::builder() // web_view::builder()
.title("My Project") // .title("My Project")
.content(web_view::Content::Html(html_content)) // .content(web_view::Content::Html(html_content))
.size(320, 480) // .size(320, 480)
.resizable(true) // .resizable(true)
.debug(true) // .debug(true)
.user_data(()) // .user_data(())
.invoke_handler(|_webview, _arg| Ok(())) // .invoke_handler(|_webview, _arg| Ok(()))
.run() // .run()
.unwrap(); // .unwrap();
} }
} }
mod receiver { // mod receiver {
use dioxus_core::prelude::*; // use dioxus_core::prelude::*;
/// The receiver app is a container that builds a connection to the host process that shuttles events and patches. // /// 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| { // pub(crate) static ReceiverApp: FC<()> = |ctx| {
// // //
html! { // html! {
<div> // <div>
{} // {}
</div> // </div>
} // }
}; // };
} // }