Merge pull request #33 from DioxusLabs/jk/no_borrow_lifetimes
Jk/no borrow lifetimes
This commit is contained in:
18 files changed
+415
-399
No files matched your search
@@ -24,7 +24,7 @@ criterion_group!(mbenches, create_rows);
|
|||||||
criterion_main!(mbenches);
|
criterion_main!(mbenches);
|
||||||
|
|
||||||
fn create_rows(c: &mut Criterion) {
|
fn create_rows(c: &mut Criterion) {
|
||||||
static App: FC<()> = |(cx, _)| {
|
static App: FC<()> = |cx, _| {
|
||||||
let mut rng = SmallRng::from_entropy();
|
let mut rng = SmallRng::from_entropy();
|
||||||
let rows = (0..10_000_usize).map(|f| {
|
let rows = (0..10_000_usize).map(|f| {
|
||||||
let label = Label::new(&mut rng);
|
let label = Label::new(&mut rng);
|
||||||
@@ -58,7 +58,7 @@ struct RowProps {
|
|||||||
row_id: usize,
|
row_id: usize,
|
||||||
label: Label,
|
label: Label,
|
||||||
}
|
}
|
||||||
fn Row((cx, props): Scope<RowProps>) -> Element {
|
fn Row(cx: Context, props: &RowProps) -> Element {
|
||||||
let [adj, col, noun] = props.label.0;
|
let [adj, col, noun] = props.label.0;
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
tr {
|
tr {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_core_macro::*;
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
struct ChildProps<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
}
|
||||||
@@ -7,12 +7,13 @@ fn main() {
|
|||||||
let _ = VirtualDom::new(Parent);
|
let _ = VirtualDom::new(Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Parent((cx, _): Scope<()>) -> Element {
|
fn Parent(cx: Context, props: &()) -> Element {
|
||||||
let value = cx.use_hook(|_| String::new(), |f| &*f);
|
let value = cx.use_hook(|_| String::new(), |f| &*f);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
Child { name: value }
|
Child { name: value }
|
||||||
|
Fragment { "asd" }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -22,7 +23,7 @@ struct ChildProps<'a> {
|
|||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child((cx, props): Scope<ChildProps>) -> Element {
|
fn Child(cx: Context, props: &ChildProps) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 { "it's nested" }
|
h1 { "it's nested" }
|
||||||
@@ -36,7 +37,7 @@ struct Grandchild<'a> {
|
|||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child2((cx, props): Scope<Grandchild>) -> Element {
|
fn Child2(cx: Context, props: &Grandchild) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div { "Hello {props.name}!" }
|
div { "Hello {props.name}!" }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,39 +6,6 @@
|
|||||||
//! that ensures compile-time required and optional fields on cx.
|
//! that ensures compile-time required and optional fields on cx.
|
||||||
|
|
||||||
use crate::innerlude::{Context, Element, LazyNodes, ScopeChildren};
|
use crate::innerlude::{Context, Element, LazyNodes, ScopeChildren};
|
||||||
/// A component is a wrapper around a Context and some Props that share a lifetime
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// With memoized state:
|
|
||||||
/// ```rust
|
|
||||||
/// struct State {}
|
|
||||||
///
|
|
||||||
/// fn Example((cx, props): Scope<State>) -> DomTree {
|
|
||||||
/// // ...
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// With borrowed state:
|
|
||||||
/// ```rust
|
|
||||||
/// struct State<'a> {
|
|
||||||
/// name: &'a str
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn Example<'a>((cx, props): Scope<'a, State>) -> DomTree<'a> {
|
|
||||||
/// // ...
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// With owned state as a closure:
|
|
||||||
/// ```rust
|
|
||||||
/// static Example: FC<()> = |(cx, props)| {
|
|
||||||
/// // ...
|
|
||||||
/// };
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub type Scope<'a, T> = (Context<'a>, &'a T);
|
|
||||||
|
|
||||||
pub struct FragmentProps<'a> {
|
pub struct FragmentProps<'a> {
|
||||||
children: ScopeChildren<'a>,
|
children: ScopeChildren<'a>,
|
||||||
@@ -99,7 +66,7 @@ impl<'a> Properties for FragmentProps<'a> {
|
|||||||
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
||||||
///
|
///
|
||||||
#[allow(non_upper_case_globals, non_snake_case)]
|
#[allow(non_upper_case_globals, non_snake_case)]
|
||||||
pub fn Fragment<'a>((cx, props): Scope<'a, FragmentProps<'a>>) -> Element {
|
pub fn Fragment<'a>(cx: Context<'a>, props: &'a FragmentProps<'a>) -> Element {
|
||||||
cx.render(Some(LazyNodes::new(|f| {
|
cx.render(Some(LazyNodes::new(|f| {
|
||||||
f.fragment_from_iter(&props.children)
|
f.fragment_from_iter(&props.children)
|
||||||
})))
|
})))
|
||||||
@@ -170,6 +137,7 @@ impl EmptyBuilder {
|
|||||||
|
|
||||||
/// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
|
/// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
|
||||||
/// to initialize a component's props.
|
/// to initialize a component's props.
|
||||||
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
|
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Context<'a>, &'a T) -> Element) -> T::Builder {
|
||||||
|
// pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
|
||||||
T::builder()
|
T::builder()
|
||||||
}
|
}
|
||||||
+16
-13
@@ -90,6 +90,7 @@
|
|||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
|
use slab::Slab;
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
|
|
||||||
/// Our DiffMachine is an iterative tree differ.
|
/// Our DiffMachine is an iterative tree differ.
|
||||||
@@ -124,11 +125,13 @@ impl<'bump> DiffState<'bump> {
|
|||||||
|
|
||||||
impl<'bump> ScopeArena {
|
impl<'bump> ScopeArena {
|
||||||
pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
|
pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
|
||||||
if let Some(component) = self.get_scope(id) {
|
// if let Some(component) = self.get_scope(id) {
|
||||||
let (old, new) = (component.wip_head(), component.fin_head());
|
|
||||||
state.stack.push(DiffInstruction::Diff { new, old });
|
let (old, new) = (self.wip_head(id), self.fin_head(id));
|
||||||
self.work(state, || false);
|
|
||||||
}
|
state.stack.push(DiffInstruction::Diff { old, new });
|
||||||
|
self.work(state, || false);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Progress the diffing for this "fiber"
|
/// Progress the diffing for this "fiber"
|
||||||
@@ -609,7 +612,7 @@ impl<'bump> ScopeArena {
|
|||||||
|
|
||||||
// make sure the component's caller function is up to date
|
// make sure the component's caller function is up to date
|
||||||
let scope = self.get_scope(&scope_addr).unwrap();
|
let scope = self.get_scope(&scope_addr).unwrap();
|
||||||
scope.update_vcomp(new);
|
let mut items = scope.items.borrow_mut();
|
||||||
|
|
||||||
// React doesn't automatically memoize, but we do.
|
// React doesn't automatically memoize, but we do.
|
||||||
let props_are_the_same = todo!("reworking component memoization");
|
let props_are_the_same = todo!("reworking component memoization");
|
||||||
@@ -1145,8 +1148,8 @@ impl<'bump> ScopeArena {
|
|||||||
}
|
}
|
||||||
VNode::Component(el) => {
|
VNode::Component(el) => {
|
||||||
let scope_id = el.associated_scope.get().unwrap();
|
let scope_id = el.associated_scope.get().unwrap();
|
||||||
let scope = self.get_scope(&scope_id).unwrap();
|
// let scope = self.get_scope(&scope_id).unwrap();
|
||||||
search_node = Some(scope.root_node());
|
search_node = Some(self.root_node(&scope_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1163,8 +1166,8 @@ impl<'bump> ScopeArena {
|
|||||||
}
|
}
|
||||||
VNode::Component(el) => {
|
VNode::Component(el) => {
|
||||||
let scope_id = el.associated_scope.get().unwrap();
|
let scope_id = el.associated_scope.get().unwrap();
|
||||||
let scope = self.get_scope(&scope_id).unwrap();
|
// let scope = self.get_scope(&scope_id).unwrap();
|
||||||
search_node = Some(scope.root_node());
|
search_node = Some(self.root_node(&scope_id));
|
||||||
}
|
}
|
||||||
VNode::Linked(link) => {
|
VNode::Linked(link) => {
|
||||||
todo!("linked")
|
todo!("linked")
|
||||||
@@ -1248,8 +1251,8 @@ impl<'bump> ScopeArena {
|
|||||||
|
|
||||||
VNode::Component(c) => {
|
VNode::Component(c) => {
|
||||||
let scope_id = c.associated_scope.get().unwrap();
|
let scope_id = c.associated_scope.get().unwrap();
|
||||||
let scope = self.get_scope(&scope_id).unwrap();
|
// let scope = self.get_scope(&scope_id).unwrap();
|
||||||
let root = scope.root_node();
|
let root = self.root_node(&scope_id);
|
||||||
self.remove_nodes(state, Some(root), gen_muts);
|
self.remove_nodes(state, Some(root), gen_muts);
|
||||||
|
|
||||||
log::debug!("Destroying scope {:?}", scope_id);
|
log::debug!("Destroying scope {:?}", scope_id);
|
||||||
@@ -1280,7 +1283,7 @@ impl<'bump> ScopeArena {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a listener closure to a scope during diff.
|
/// Adds a listener closure to a scope during diff.
|
||||||
fn attach_listener_to_scope(&'bump self, listener: &'bump Listener<'bump>, scope: &ScopeState) {
|
fn attach_listener_to_scope(&'bump self, listener: &'bump Listener<'bump>, scope: &Scope) {
|
||||||
let long_listener = unsafe { std::mem::transmute(listener) };
|
let long_listener = unsafe { std::mem::transmute(listener) };
|
||||||
scope.items.borrow_mut().listeners.push(long_listener)
|
scope.items.borrow_mut().listeners.push(long_listener)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,10 +49,6 @@ impl<'bump> DiffStack<'bump> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.instructions.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(&mut self) -> Option<DiffInstruction<'bump>> {
|
pub fn pop(&mut self) -> Option<DiffInstruction<'bump>> {
|
||||||
self.instructions.pop()
|
self.instructions.pop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub(crate) mod innerlude {
|
|||||||
pub use crate::virtual_dom::*;
|
pub use crate::virtual_dom::*;
|
||||||
|
|
||||||
pub type Element = Option<NodeLink>;
|
pub type Element = Option<NodeLink>;
|
||||||
pub type FC<P> = for<'a> fn(Scope<'a, P>) -> Element;
|
pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
@@ -50,9 +50,11 @@ pub use crate::innerlude::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::component::{fc_to_builder, Fragment, Properties, Scope};
|
pub use crate::component::{fc_to_builder, Fragment, Properties};
|
||||||
pub use crate::innerlude::Context;
|
pub use crate::innerlude::Context;
|
||||||
pub use crate::innerlude::{DioxusElement, Element, LazyNodes, NodeFactory, ScopeChildren, FC};
|
pub use crate::innerlude::{
|
||||||
|
DioxusElement, Element, LazyNodes, NodeFactory, Scope, ScopeChildren, FC,
|
||||||
|
};
|
||||||
pub use crate::nodes::VNode;
|
pub use crate::nodes::VNode;
|
||||||
pub use crate::VirtualDom;
|
pub use crate::VirtualDom;
|
||||||
}
|
}
|
||||||
|
|||||||
+88
-100
@@ -4,7 +4,7 @@
|
|||||||
//! cheap and *very* fast to construct - building a full tree should be quick.
|
//! cheap and *very* fast to construct - building a full tree should be quick.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
innerlude::{empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId, ScopeState},
|
innerlude::{empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId},
|
||||||
lazynodes::LazyNodes,
|
lazynodes::LazyNodes,
|
||||||
};
|
};
|
||||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||||
@@ -20,7 +20,9 @@ use std::{
|
|||||||
///
|
///
|
||||||
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
|
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
|
||||||
/// is essentially a unique key to guarantee safe usage of the Node.
|
/// is essentially a unique key to guarantee safe usage of the Node.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct NodeLink {
|
pub struct NodeLink {
|
||||||
|
pub(crate) link_idx: usize,
|
||||||
pub(crate) gen_id: u32,
|
pub(crate) gen_id: u32,
|
||||||
pub(crate) scope_id: ScopeId,
|
pub(crate) scope_id: ScopeId,
|
||||||
}
|
}
|
||||||
@@ -198,6 +200,7 @@ impl<'src> VNode<'src> {
|
|||||||
VNode::Linked(c) => VNode::Linked(NodeLink {
|
VNode::Linked(c) => VNode::Linked(NodeLink {
|
||||||
gen_id: c.gen_id,
|
gen_id: c.gen_id,
|
||||||
scope_id: c.scope_id,
|
scope_id: c.scope_id,
|
||||||
|
link_idx: c.link_idx,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,20 +210,22 @@ impl Debug for VNode<'_> {
|
|||||||
fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
match &self {
|
match &self {
|
||||||
VNode::Element(el) => s
|
VNode::Element(el) => s
|
||||||
.debug_struct("VElement")
|
.debug_struct("VNode::VElement")
|
||||||
.field("name", &el.tag_name)
|
.field("name", &el.tag_name)
|
||||||
.field("key", &el.key)
|
.field("key", &el.key)
|
||||||
.finish(),
|
.finish(),
|
||||||
|
|
||||||
VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
|
VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
|
||||||
VNode::Anchor(_) => write!(s, "VAnchor"),
|
VNode::Anchor(_) => write!(s, "VNode::VAnchor"),
|
||||||
|
|
||||||
VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
|
VNode::Fragment(frag) => {
|
||||||
VNode::Suspended { .. } => write!(s, "VSuspended"),
|
write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
|
||||||
VNode::Component(comp) => write!(s, "VComponent {{ fc: {:?}}}", comp.user_fc),
|
}
|
||||||
|
VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
|
||||||
|
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
||||||
VNode::Linked(c) => write!(
|
VNode::Linked(c) => write!(
|
||||||
s,
|
s,
|
||||||
"VCached {{ gen_id: {}, scope_id: {:?} }}",
|
"VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
|
||||||
c.gen_id, c.scope_id
|
c.gen_id, c.scope_id
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -344,31 +349,29 @@ pub struct Listener<'bump> {
|
|||||||
pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(Box<dyn Any + Send>) + 'bump>>>,
|
pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(Box<dyn Any + Send>) + 'bump>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type VCompCaller<'src> = BumpBox<'src, dyn Fn(Context) -> Element + 'src>;
|
||||||
/// Virtual Components for custom user-defined components
|
/// Virtual Components for custom user-defined components
|
||||||
/// Only supports the functional syntax
|
/// Only supports the functional syntax
|
||||||
pub struct VComponent<'src> {
|
pub struct VComponent<'src> {
|
||||||
pub key: Option<&'src str>,
|
pub key: Option<&'src str>,
|
||||||
|
|
||||||
pub associated_scope: Cell<Option<ScopeId>>,
|
pub associated_scope: Cell<Option<ScopeId>>,
|
||||||
// pub associated_scope: Cell<Option<*mut ScopeInner>>,
|
|
||||||
|
|
||||||
// Function pointer to the FC that was used to generate this component
|
// Function pointer to the FC that was used to generate this component
|
||||||
pub user_fc: *const (),
|
pub user_fc: *const (),
|
||||||
|
|
||||||
pub(crate) can_memoize: bool,
|
pub(crate) can_memoize: bool,
|
||||||
|
|
||||||
|
pub(crate) hard_allocation: Cell<Option<*const ()>>,
|
||||||
|
|
||||||
// Raw pointer into the bump arena for the props of the component
|
// Raw pointer into the bump arena for the props of the component
|
||||||
pub(crate) raw_props: *const (),
|
pub(crate) bump_props: *const (),
|
||||||
|
|
||||||
// during the "teardown" process we'll take the caller out so it can be dropped properly
|
// during the "teardown" process we'll take the caller out so it can be dropped properly
|
||||||
pub(crate) caller: Option<VCompCaller<'src>>,
|
pub(crate) caller: Option<VCompCaller<'src>>,
|
||||||
pub(crate) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
|
pub(crate) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum VCompCaller<'src> {
|
|
||||||
Borrowed(BumpBox<'src, dyn for<'b> Fn(&'b ScopeState) -> Element + 'src>),
|
|
||||||
Owned(Box<dyn for<'b> Fn(&'b ScopeState) -> Element>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VSuspended<'a> {
|
pub struct VSuspended<'a> {
|
||||||
pub task_id: u64,
|
pub task_id: u64,
|
||||||
pub dom_id: Cell<Option<ElementId>>,
|
pub dom_id: Cell<Option<ElementId>>,
|
||||||
@@ -511,7 +514,7 @@ impl<'a> NodeFactory<'a> {
|
|||||||
|
|
||||||
pub fn component<P>(
|
pub fn component<P>(
|
||||||
&self,
|
&self,
|
||||||
component: fn(Scope<'a, P>) -> Element,
|
component: fn(Context<'a>, &'a P) -> Element,
|
||||||
props: P,
|
props: P,
|
||||||
key: Option<Arguments>,
|
key: Option<Arguments>,
|
||||||
) -> VNode<'a>
|
) -> VNode<'a>
|
||||||
@@ -528,99 +531,84 @@ impl<'a> NodeFactory<'a> {
|
|||||||
- if the props aren't static, then we convert them into a box which we pass off between renders
|
- if the props aren't static, then we convert them into a box which we pass off between renders
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// let bump = self.bump();
|
||||||
|
|
||||||
|
// // later, we'll do a hard allocation
|
||||||
|
// let raw_ptr = bump.alloc(props);
|
||||||
let bump = self.bump();
|
let bump = self.bump();
|
||||||
|
let props = bump.alloc(props);
|
||||||
// let p = BumpBox::new_in(x, a)
|
let bump_props = props as *mut P as *mut ();
|
||||||
|
|
||||||
// the best place to allocate the props are the other component's arena
|
|
||||||
// the second best place is the global allocator
|
|
||||||
|
|
||||||
// // if the props are static
|
|
||||||
// let boxed = if P::IS_STATIC {
|
|
||||||
// todo!()
|
|
||||||
// } else {
|
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let caller = Box::new(|f: &ScopeInner| -> Element {
|
|
||||||
// //
|
|
||||||
// component((f, &props))
|
|
||||||
// });
|
|
||||||
|
|
||||||
let user_fc = component as *const ();
|
let user_fc = component as *const ();
|
||||||
|
|
||||||
// let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
|
let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
|
||||||
// move |other: &VComponent| {
|
move |other: &VComponent| {
|
||||||
// if user_fc == other.user_fc {
|
if user_fc == other.user_fc {
|
||||||
// // Safety
|
// Safety
|
||||||
// // - We guarantee that FC<P> is the same by function pointer
|
// - We guarantee that FC<P> is the same by function pointer
|
||||||
// // - Because FC<P> is the same, then P must be the same (even with generics)
|
// - Because FC<P> is the same, then P must be the same (even with generics)
|
||||||
// // - Non-static P are autoderived to memoize as false
|
// - Non-static P are autoderived to memoize as false
|
||||||
// // - This comparator is only called on a corresponding set of bumpframes
|
// - This comparator is only called on a corresponding set of bumpframes
|
||||||
// let props_memoized = unsafe {
|
let props_memoized = unsafe {
|
||||||
// let real_other: &P = &*(other.raw_props as *const _ as *const P);
|
let real_other: &P = &*(other.bump_props as *const _ as *const P);
|
||||||
// props.memoize(real_other)
|
props.memoize(real_other)
|
||||||
// };
|
};
|
||||||
|
|
||||||
// // It's only okay to memoize if there are no children and the props can be memoized
|
// It's only okay to memoize if there are no children and the props can be memoized
|
||||||
// // Implementing memoize is unsafe and done automatically with the props trait
|
// Implementing memoize is unsafe and done automatically with the props trait
|
||||||
// props_memoized
|
props_memoized
|
||||||
// } else {
|
} else {
|
||||||
// false
|
false
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
// let comparator = Some(unsafe { BumpBox::from_raw(comparator) });
|
|
||||||
|
let drop_props = {
|
||||||
|
// create a closure to drop the props
|
||||||
|
let mut has_dropped = false;
|
||||||
|
|
||||||
|
let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
|
||||||
|
move || unsafe {
|
||||||
|
log::debug!("dropping props!");
|
||||||
|
if !has_dropped {
|
||||||
|
let real_other = bump_props as *mut _ as *mut P;
|
||||||
|
let b = BumpBox::from_raw(real_other);
|
||||||
|
std::mem::drop(b);
|
||||||
|
|
||||||
|
has_dropped = true;
|
||||||
|
} else {
|
||||||
|
panic!("Drop props called twice - this is an internal failure of Dioxus");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let drop_props = unsafe { BumpBox::from_raw(drop_props) };
|
||||||
|
|
||||||
|
RefCell::new(Some(drop_props))
|
||||||
|
};
|
||||||
|
|
||||||
let key = key.map(|f| self.raw_text(f).0);
|
let key = key.map(|f| self.raw_text(f).0);
|
||||||
|
|
||||||
let caller = match P::IS_STATIC {
|
let caller: &'a mut dyn Fn(&Scope) -> Element =
|
||||||
true => {
|
bump.alloc(move |scope: &Scope| -> Element {
|
||||||
// it just makes sense to box the props
|
log::debug!("calling component renderr {:?}", scope.our_arena_idx);
|
||||||
let boxed_props: Box<P> = Box::new(props);
|
let props: &'_ P = unsafe { &*(bump_props as *const P) };
|
||||||
let props_we_know_are_static = todo!();
|
let res = component(scope, props);
|
||||||
VCompCaller::Owned(Box::new(|f| {
|
// let res = component((Context { scope }, props));
|
||||||
//
|
unsafe { std::mem::transmute(res) }
|
||||||
|
});
|
||||||
|
|
||||||
let p = todo!();
|
let can_memoize = P::IS_STATIC;
|
||||||
|
|
||||||
todo!()
|
VNode::Component(bump.alloc(VComponent {
|
||||||
}))
|
user_fc,
|
||||||
}
|
comparator,
|
||||||
false => VCompCaller::Borrowed({
|
bump_props,
|
||||||
//
|
caller,
|
||||||
|
key,
|
||||||
todo!()
|
can_memoize,
|
||||||
// let caller = bump.alloc()
|
drop_props,
|
||||||
}),
|
associated_scope: Cell::new(None),
|
||||||
};
|
}))
|
||||||
|
|
||||||
todo!()
|
|
||||||
// let caller: &'a mut dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> =
|
|
||||||
// bump.alloc(move |scope: &ScopeInner| -> Element {
|
|
||||||
// log::debug!("calling component renderr {:?}", scope.our_arena_idx);
|
|
||||||
// let props: &'_ P = unsafe { &*(raw_props as *const P) };
|
|
||||||
|
|
||||||
// let scp: &'a ScopeInner = unsafe { std::mem::transmute(scope) };
|
|
||||||
// let s: Scope<'a, P> = (scp, props);
|
|
||||||
|
|
||||||
// let res: Element = component(s);
|
|
||||||
// unsafe { std::mem::transmute(res) }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// let caller = unsafe { BumpBox::from_raw(caller) };
|
|
||||||
|
|
||||||
// VNode::Component(bump.alloc(VComponent {
|
|
||||||
// user_fc,
|
|
||||||
// comparator,
|
|
||||||
// raw_props,
|
|
||||||
// caller,
|
|
||||||
// is_static: P::IS_STATIC,
|
|
||||||
// key,
|
|
||||||
// can_memoize: P::IS_STATIC,
|
|
||||||
// drop_props,
|
|
||||||
// associated_scope: Cell::new(None),
|
|
||||||
// }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listener(
|
pub fn listener(
|
||||||
|
|||||||
+56
-73
@@ -30,7 +30,7 @@ use bumpalo::{boxed::Box as BumpBox, Bump};
|
|||||||
/// cx.render(rsx!{ div {"Hello, {props.name}"} })
|
/// cx.render(rsx!{ div {"Hello, {props.name}"} })
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub type Context<'a> = &'a ScopeState;
|
pub type Context<'a> = &'a Scope;
|
||||||
|
|
||||||
/// Every component in Dioxus is represented by a `Scope`.
|
/// Every component in Dioxus is represented by a `Scope`.
|
||||||
///
|
///
|
||||||
@@ -41,7 +41,7 @@ pub type Context<'a> = &'a ScopeState;
|
|||||||
///
|
///
|
||||||
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
|
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
|
||||||
/// use case they might have.
|
/// use case they might have.
|
||||||
pub struct ScopeState {
|
pub struct Scope {
|
||||||
// Book-keeping about our spot in the arena
|
// Book-keeping about our spot in the arena
|
||||||
|
|
||||||
// safety:
|
// safety:
|
||||||
@@ -49,7 +49,7 @@ pub struct ScopeState {
|
|||||||
// pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
|
// pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
|
||||||
// this is just a bit of a hack to not need an Rc to the ScopeArena.
|
// this is just a bit of a hack to not need an Rc to the ScopeArena.
|
||||||
// todo: replace this will ScopeId and provide a connection to scope arena directly
|
// todo: replace this will ScopeId and provide a connection to scope arena directly
|
||||||
pub(crate) parent_scope: Option<*mut ScopeState>,
|
pub(crate) parent_scope: Option<*mut Scope>,
|
||||||
|
|
||||||
pub(crate) our_arena_idx: ScopeId,
|
pub(crate) our_arena_idx: ScopeId,
|
||||||
|
|
||||||
@@ -59,14 +59,16 @@ pub struct ScopeState {
|
|||||||
|
|
||||||
pub(crate) is_subtree_root: Cell<bool>,
|
pub(crate) is_subtree_root: Cell<bool>,
|
||||||
|
|
||||||
// The double-buffering situation that we will use
|
pub(crate) generation: Cell<u32>,
|
||||||
pub(crate) frames: [Bump; 2],
|
|
||||||
|
|
||||||
pub(crate) vcomp: *const VComponent<'static>,
|
// The double-buffering situation that we will use
|
||||||
|
pub(crate) frames: [BumpFrame; 2],
|
||||||
|
|
||||||
pub(crate) old_root: RefCell<Option<NodeLink>>,
|
pub(crate) old_root: RefCell<Option<NodeLink>>,
|
||||||
pub(crate) new_root: RefCell<Option<NodeLink>>,
|
pub(crate) new_root: RefCell<Option<NodeLink>>,
|
||||||
|
|
||||||
|
pub(crate) caller: *mut dyn Fn(&Scope) -> Element,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
we care about:
|
we care about:
|
||||||
- listeners (and how to call them when an event is triggered)
|
- listeners (and how to call them when an event is triggered)
|
||||||
@@ -85,12 +87,6 @@ pub struct ScopeState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct SelfReferentialItems<'a> {
|
pub struct SelfReferentialItems<'a> {
|
||||||
// nodes stored by "cx.render"
|
|
||||||
pub(crate) cached_nodes_old: Vec<VNode<'a>>,
|
|
||||||
pub(crate) cached_nodes_new: Vec<VNode<'a>>,
|
|
||||||
|
|
||||||
pub(crate) generation: Cell<u32>,
|
|
||||||
|
|
||||||
pub(crate) listeners: Vec<&'a Listener<'a>>,
|
pub(crate) listeners: Vec<&'a Listener<'a>>,
|
||||||
pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
|
pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
|
||||||
pub(crate) suspended_nodes: FxHashMap<u64, &'a VSuspended<'a>>,
|
pub(crate) suspended_nodes: FxHashMap<u64, &'a VSuspended<'a>>,
|
||||||
@@ -99,26 +95,7 @@ pub struct SelfReferentialItems<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Public methods exposed to libraries and components
|
// Public methods exposed to libraries and components
|
||||||
impl ScopeState {
|
impl Scope {
|
||||||
/// Get the root VNode for this Scope.
|
|
||||||
///
|
|
||||||
/// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```rust
|
|
||||||
/// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
|
|
||||||
/// dom.rebuild();
|
|
||||||
///
|
|
||||||
/// let base = dom.base_scope();
|
|
||||||
///
|
|
||||||
/// if let VNode::VElement(node) = base.root_node() {
|
|
||||||
/// assert_eq!(node.tag_name, "div");
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn root_node(&self) -> &VNode {
|
|
||||||
self.fin_head()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the subtree ID that this scope belongs to.
|
/// Get the subtree ID that this scope belongs to.
|
||||||
///
|
///
|
||||||
/// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
|
/// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
|
||||||
@@ -239,7 +216,7 @@ impl ScopeState {
|
|||||||
///
|
///
|
||||||
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
|
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
|
||||||
pub fn bump(&self) -> &Bump {
|
pub fn bump(&self) -> &Bump {
|
||||||
&self.wip_frame()
|
&self.wip_frame().bump
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
||||||
@@ -258,18 +235,18 @@ impl ScopeState {
|
|||||||
/// }
|
/// }
|
||||||
///```
|
///```
|
||||||
pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
|
pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
|
||||||
let bump = &self.wip_frame();
|
let bump = &self.wip_frame().bump;
|
||||||
let factory = NodeFactory { bump };
|
let factory = NodeFactory { bump };
|
||||||
let node = lazy_nodes.map(|f| f.call(factory))?;
|
let node = lazy_nodes.map(|f| f.call(factory))?;
|
||||||
|
|
||||||
self.items
|
let idx = self
|
||||||
.borrow_mut()
|
.wip_frame()
|
||||||
.cached_nodes_old
|
.add_node(unsafe { std::mem::transmute(node) });
|
||||||
.push(unsafe { std::mem::transmute(node) });
|
|
||||||
|
|
||||||
Some(NodeLink {
|
Some(NodeLink {
|
||||||
gen_id: self.items.borrow().generation.get(),
|
gen_id: self.generation.get(),
|
||||||
scope_id: self.our_arena_idx,
|
scope_id: self.our_arena_idx,
|
||||||
|
link_idx: idx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,11 +404,7 @@ impl ScopeState {
|
|||||||
self.hooks.push_hook(initializer(self.hooks.len()));
|
self.hooks.push_hook(initializer(self.hooks.len()));
|
||||||
}
|
}
|
||||||
|
|
||||||
runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
|
const HOOK_ERR_MSG: &str = r###"
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const HOOK_ERR_MSG: &str = r###"
|
|
||||||
Unable to retrieve the hook that was initialized at this index.
|
Unable to retrieve the hook that was initialized at this index.
|
||||||
Consult the `rules of hooks` to understand how to use hooks properly.
|
Consult the `rules of hooks` to understand how to use hooks properly.
|
||||||
|
|
||||||
@@ -439,29 +412,24 @@ You likely used the hook in a conditional. Hooks rely on consistent ordering bet
|
|||||||
Functions prefixed with "use" should never be called conditionally.
|
Functions prefixed with "use" should never be called conditionally.
|
||||||
"###;
|
"###;
|
||||||
|
|
||||||
|
runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Important internal methods
|
// Important internal methods
|
||||||
impl ScopeState {
|
impl Scope {
|
||||||
/// Give out our self-referential item with our own borrowed lifetime
|
|
||||||
pub(crate) fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
|
|
||||||
todo!()
|
|
||||||
// let cur_head = &self.finished_frame().head_node;
|
|
||||||
// unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Give out our self-referential item with our own borrowed lifetime
|
|
||||||
pub(crate) fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
|
|
||||||
todo!()
|
|
||||||
// let cur_head = &self.wip_frame().head_node;
|
|
||||||
// unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The "work in progress frame" represents the frame that is currently being worked on.
|
/// The "work in progress frame" represents the frame that is currently being worked on.
|
||||||
pub(crate) fn wip_frame(&self) -> &Bump {
|
pub(crate) fn wip_frame(&self) -> &BumpFrame {
|
||||||
todo!()
|
match self.generation.get() & 1 == 0 {
|
||||||
// match self.cur_generation.get() & 1 == 0 {
|
true => &self.frames[0],
|
||||||
// true => &self.frames[0],
|
false => &self.frames[1],
|
||||||
// false => &self.frames[1],
|
}
|
||||||
// }
|
}
|
||||||
|
pub(crate) fn fin_frame(&self) -> &BumpFrame {
|
||||||
|
match self.generation.get() & 1 == 1 {
|
||||||
|
true => &self.frames[0],
|
||||||
|
false => &self.frames[1],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn reset_wip_frame(&self) {
|
pub unsafe fn reset_wip_frame(&self) {
|
||||||
@@ -469,8 +437,10 @@ impl ScopeState {
|
|||||||
let bump = self.wip_frame() as *const _ as *mut Bump;
|
let bump = self.wip_frame() as *const _ as *mut Bump;
|
||||||
let g = &mut *bump;
|
let g = &mut *bump;
|
||||||
g.reset();
|
g.reset();
|
||||||
|
}
|
||||||
|
|
||||||
// self.wip_frame_mut().bump.reset()
|
pub fn cycle_frame(&self) {
|
||||||
|
self.generation.set(self.generation.get() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A safe wrapper around calling listeners
|
/// A safe wrapper around calling listeners
|
||||||
@@ -527,14 +497,27 @@ impl ScopeState {
|
|||||||
// Some(cur)
|
// Some(cur)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn update_vcomp(&self, vcomp: &VComponent) {
|
pub struct BumpFrame {
|
||||||
let f: *const _ = vcomp;
|
pub bump: Bump,
|
||||||
todo!()
|
pub nodes: RefCell<Vec<VNode<'static>>>,
|
||||||
// self.vcomp = unsafe { std::mem::transmute(f) };
|
}
|
||||||
|
impl BumpFrame {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let bump = Bump::new();
|
||||||
|
|
||||||
|
let node = &*bump.alloc(VText {
|
||||||
|
text: "asd",
|
||||||
|
dom_id: Default::default(),
|
||||||
|
is_static: false,
|
||||||
|
});
|
||||||
|
let nodes = RefCell::new(vec![VNode::Text(unsafe { std::mem::transmute(node) })]);
|
||||||
|
Self { bump, nodes }
|
||||||
}
|
}
|
||||||
|
fn add_node(&self, node: VNode<'static>) -> usize {
|
||||||
pub(crate) fn load_vcomp<'a>(&'a mut self) -> &'a VComponent<'a> {
|
let mut nodes = self.nodes.borrow_mut();
|
||||||
unsafe { std::mem::transmute(&*self.vcomp) }
|
nodes.push(node);
|
||||||
|
nodes.len() - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+153
-20
@@ -1,4 +1,8 @@
|
|||||||
use std::cell::{Cell, RefCell};
|
use slab::Slab;
|
||||||
|
use std::{
|
||||||
|
borrow::BorrowMut,
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
};
|
||||||
|
|
||||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
use futures_channel::mpsc::UnboundedSender;
|
||||||
@@ -19,8 +23,9 @@ pub struct Heuristic {
|
|||||||
// has an internal heuristics engine to pre-allocate arenas to the right size
|
// has an internal heuristics engine to pre-allocate arenas to the right size
|
||||||
pub(crate) struct ScopeArena {
|
pub(crate) struct ScopeArena {
|
||||||
bump: Bump,
|
bump: Bump,
|
||||||
scopes: Vec<*mut ScopeState>,
|
scopes: Vec<*mut Scope>,
|
||||||
free_scopes: Vec<ScopeId>,
|
free_scopes: Vec<ScopeId>,
|
||||||
|
nodes: RefCell<Slab<*const VNode<'static>>>,
|
||||||
pub(crate) sender: UnboundedSender<SchedulerMsg>,
|
pub(crate) sender: UnboundedSender<SchedulerMsg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,19 +35,20 @@ impl ScopeArena {
|
|||||||
bump: Bump::new(),
|
bump: Bump::new(),
|
||||||
scopes: Vec::new(),
|
scopes: Vec::new(),
|
||||||
free_scopes: Vec::new(),
|
free_scopes: Vec::new(),
|
||||||
|
nodes: RefCell::new(Slab::new()),
|
||||||
sender,
|
sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_scope(&self, id: &ScopeId) -> Option<&ScopeState> {
|
pub fn get_scope(&self, id: &ScopeId) -> Option<&Scope> {
|
||||||
unsafe { Some(&*self.scopes[id.0]) }
|
unsafe { Some(&*self.scopes[id.0]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_key(
|
pub fn new_with_key(
|
||||||
&mut self,
|
&mut self,
|
||||||
fc_ptr: *const (),
|
fc_ptr: *const (),
|
||||||
vcomp: &VComponent,
|
caller: *mut dyn Fn(&Scope) -> Element,
|
||||||
parent_scope: Option<*mut ScopeState>,
|
parent_scope: Option<*mut Scope>,
|
||||||
height: u32,
|
height: u32,
|
||||||
subtree: u32,
|
subtree: u32,
|
||||||
) -> ScopeId {
|
) -> ScopeId {
|
||||||
@@ -53,50 +59,71 @@ impl ScopeArena {
|
|||||||
todo!("override the scope contents");
|
todo!("override the scope contents");
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
let id = ScopeId(self.scopes.len());
|
let scope_id = ScopeId(self.scopes.len());
|
||||||
|
|
||||||
let vcomp = unsafe { std::mem::transmute(vcomp as *const VComponent) };
|
let old_root = NodeLink {
|
||||||
|
link_idx: 0,
|
||||||
|
gen_id: 0,
|
||||||
|
scope_id,
|
||||||
|
};
|
||||||
|
let new_root = NodeLink {
|
||||||
|
link_idx: 0,
|
||||||
|
gen_id: 0,
|
||||||
|
scope_id,
|
||||||
|
};
|
||||||
|
|
||||||
let new_scope = ScopeState {
|
let new_scope = Scope {
|
||||||
sender: self.sender.clone(),
|
sender: self.sender.clone(),
|
||||||
parent_scope,
|
parent_scope,
|
||||||
our_arena_idx: id,
|
our_arena_idx: scope_id,
|
||||||
height,
|
height,
|
||||||
subtree: Cell::new(subtree),
|
subtree: Cell::new(subtree),
|
||||||
is_subtree_root: Cell::new(false),
|
is_subtree_root: Cell::new(false),
|
||||||
frames: [Bump::default(), Bump::default()],
|
frames: [BumpFrame::new(), BumpFrame::new()],
|
||||||
vcomp,
|
|
||||||
|
|
||||||
hooks: Default::default(),
|
hooks: Default::default(),
|
||||||
shared_contexts: Default::default(),
|
shared_contexts: Default::default(),
|
||||||
|
caller,
|
||||||
|
generation: 0.into(),
|
||||||
|
|
||||||
|
old_root: RefCell::new(Some(old_root)),
|
||||||
|
new_root: RefCell::new(Some(new_root)),
|
||||||
|
// old_root: RefCell::new(Some(old_root)),
|
||||||
|
// new_root: RefCell::new(None),
|
||||||
items: RefCell::new(SelfReferentialItems {
|
items: RefCell::new(SelfReferentialItems {
|
||||||
listeners: Default::default(),
|
listeners: Default::default(),
|
||||||
borrowed_props: Default::default(),
|
borrowed_props: Default::default(),
|
||||||
suspended_nodes: Default::default(),
|
suspended_nodes: Default::default(),
|
||||||
tasks: Default::default(),
|
tasks: Default::default(),
|
||||||
pending_effects: Default::default(),
|
pending_effects: Default::default(),
|
||||||
cached_nodes_old: Default::default(),
|
|
||||||
generation: Default::default(),
|
|
||||||
cached_nodes_new: todo!(),
|
|
||||||
}),
|
}),
|
||||||
old_root: todo!(),
|
|
||||||
new_root: todo!(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let stable = self.bump.alloc(new_scope);
|
let stable = self.bump.alloc(new_scope);
|
||||||
self.scopes.push(stable);
|
self.scopes.push(stable);
|
||||||
id
|
scope_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_remove(&self, id: &ScopeId) -> Option<ScopeState> {
|
pub fn try_remove(&self, id: &ScopeId) -> Option<Scope> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reserve_node(&self, node: &VNode) -> ElementId {
|
pub fn reserve_node(&self, node: &VNode) -> ElementId {
|
||||||
todo!()
|
let mut els = self.nodes.borrow_mut();
|
||||||
// self.node_reservations.insert(id);
|
let entry = els.vacant_entry();
|
||||||
|
let key = entry.key();
|
||||||
|
let id = ElementId(key);
|
||||||
|
let node = node as *const _;
|
||||||
|
let node = unsafe { std::mem::transmute(node) };
|
||||||
|
entry.insert(node);
|
||||||
|
id
|
||||||
|
|
||||||
|
// let nodes = self.nodes.borrow_mut();
|
||||||
|
// let id = nodes.insert(());
|
||||||
|
// let node_id = ElementId(id);
|
||||||
|
// node = Some(node_id);
|
||||||
|
// node_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_garbage(&self, id: ElementId) {
|
pub fn collect_garbage(&self, id: ElementId) {
|
||||||
@@ -154,4 +181,110 @@ impl ScopeArena {
|
|||||||
.map(|li| unsafe { &*li })
|
.map(|li| unsafe { &*li })
|
||||||
.for_each(|listener| drop(listener.callback.borrow_mut().take()));
|
.for_each(|listener| drop(listener.callback.borrow_mut().take()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn run_scope(&self, id: &ScopeId) -> bool {
|
||||||
|
let scope = self
|
||||||
|
.get_scope(id)
|
||||||
|
.expect("The base scope should never be moved");
|
||||||
|
|
||||||
|
// Cycle to the next frame and then reset it
|
||||||
|
// This breaks any latent references, invalidating every pointer referencing into it.
|
||||||
|
// Remove all the outdated listeners
|
||||||
|
self.ensure_drop_safety(id);
|
||||||
|
|
||||||
|
// Safety:
|
||||||
|
// - We dropped the listeners, so no more &mut T can be used while these are held
|
||||||
|
// - All children nodes that rely on &mut T are replaced with a new reference
|
||||||
|
unsafe { scope.hooks.reset() };
|
||||||
|
|
||||||
|
// Safety:
|
||||||
|
// - We've dropped all references to the wip bump frame with "ensure_drop_safety"
|
||||||
|
unsafe { scope.reset_wip_frame() };
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut items = scope.items.borrow_mut();
|
||||||
|
|
||||||
|
// just forget about our suspended nodes while we're at it
|
||||||
|
items.suspended_nodes.clear();
|
||||||
|
|
||||||
|
// guarantee that we haven't screwed up - there should be no latent references anywhere
|
||||||
|
debug_assert!(items.listeners.is_empty());
|
||||||
|
debug_assert!(items.suspended_nodes.is_empty());
|
||||||
|
debug_assert!(items.borrowed_props.is_empty());
|
||||||
|
|
||||||
|
log::debug!("Borrowed stuff is successfully cleared");
|
||||||
|
|
||||||
|
// temporarily cast the vcomponent to the right lifetime
|
||||||
|
// let vcomp = scope.load_vcomp();
|
||||||
|
}
|
||||||
|
|
||||||
|
let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller };
|
||||||
|
|
||||||
|
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
||||||
|
scope.wip_frame().nodes.borrow_mut().clear();
|
||||||
|
if let Some(key) = render(scope) {
|
||||||
|
dbg!(key);
|
||||||
|
|
||||||
|
dbg!(&scope.wip_frame().nodes.borrow_mut());
|
||||||
|
// let mut old = scope.old_root.borrow_mut();
|
||||||
|
// let mut new = scope.new_root.borrow_mut();
|
||||||
|
|
||||||
|
// let new_old = new.clone();
|
||||||
|
// *old = new_old;
|
||||||
|
// *new = Some(key);
|
||||||
|
|
||||||
|
// dbg!(&old);
|
||||||
|
// dbg!(&new);
|
||||||
|
|
||||||
|
// the user's component succeeded. We can safely cycle to the next frame
|
||||||
|
// scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
|
||||||
|
scope.cycle_frame();
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wip_head(&self, id: &ScopeId) -> &VNode {
|
||||||
|
let scope = self.get_scope(id).unwrap();
|
||||||
|
let wip_frame = scope.wip_frame();
|
||||||
|
let nodes = wip_frame.nodes.borrow();
|
||||||
|
let node = nodes.get(0).unwrap();
|
||||||
|
unsafe { std::mem::transmute(node) }
|
||||||
|
// let root = scope.old_root.borrow();
|
||||||
|
// let link = root.as_ref().unwrap();
|
||||||
|
// dbg!(link);
|
||||||
|
|
||||||
|
// // for now, components cannot pass portals through each other
|
||||||
|
// assert_eq!(link.scope_id, *id);
|
||||||
|
// // assert_eq!(link.gen_id, scope.generation.get() - 1);
|
||||||
|
|
||||||
|
// // let items = scope.items.borrow();
|
||||||
|
// let nodes = scope.wip_frame().nodes.borrow();
|
||||||
|
// let node = nodes.get(link.link_idx).unwrap();
|
||||||
|
// unsafe { std::mem::transmute(node) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fin_head(&self, id: &ScopeId) -> &VNode {
|
||||||
|
let scope = self.get_scope(id).unwrap();
|
||||||
|
let wip_frame = scope.fin_frame();
|
||||||
|
let nodes = wip_frame.nodes.borrow();
|
||||||
|
let node = nodes.get(0).unwrap();
|
||||||
|
unsafe { std::mem::transmute(node) }
|
||||||
|
// let scope = self.get_scope(id).unwrap();
|
||||||
|
// let root = scope.new_root.borrow();
|
||||||
|
// let link = root.as_ref().unwrap();
|
||||||
|
|
||||||
|
// // for now, components cannot pass portals through each other
|
||||||
|
// assert_eq!(link.scope_id, *id);
|
||||||
|
// // assert_eq!(link.gen_id, scope.generation.get());
|
||||||
|
|
||||||
|
// let nodes = scope.fin_frame().nodes.borrow();
|
||||||
|
// let node = nodes.get(link.link_idx).unwrap();
|
||||||
|
// unsafe { std::mem::transmute(node) }
|
||||||
|
}
|
||||||
|
pub fn root_node(&self, id: &ScopeId) -> &VNode {
|
||||||
|
self.wip_head(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,10 +7,6 @@ pub fn empty_cell() -> Cell<Option<ElementId>> {
|
|||||||
Cell::new(None)
|
Cell::new(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_name_of<T>(_: T) -> &'static str {
|
|
||||||
std::any::type_name::<T>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A component's unique identifier.
|
/// A component's unique identifier.
|
||||||
///
|
///
|
||||||
/// `ScopeId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
|
/// `ScopeId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
|
||||||
|
|||||||
@@ -20,21 +20,13 @@
|
|||||||
//! Additional functionality is defined in the respective files.
|
//! Additional functionality is defined in the respective files.
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
use bumpalo::Bump;
|
|
||||||
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||||
use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
|
use futures_util::{Future, StreamExt};
|
||||||
use fxhash::FxHashMap;
|
|
||||||
use fxhash::FxHashSet;
|
use fxhash::FxHashSet;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
use slab::Slab;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use std::{
|
use std::{any::Any, collections::VecDeque};
|
||||||
any::{Any, TypeId},
|
|
||||||
cell::{Cell, UnsafeCell},
|
|
||||||
collections::{HashSet, VecDeque},
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
/// An integrated virtual node system that progresses events and diffs UI trees.
|
||||||
///
|
///
|
||||||
@@ -69,10 +61,7 @@ use std::{
|
|||||||
pub struct VirtualDom {
|
pub struct VirtualDom {
|
||||||
base_scope: ScopeId,
|
base_scope: ScopeId,
|
||||||
|
|
||||||
_root_props: Rc<dyn Any>,
|
_root_caller: *mut dyn Fn(&Scope) -> Element,
|
||||||
|
|
||||||
// we need to keep the allocation around, but we don't necessarily use it
|
|
||||||
_root_caller: Box<dyn Any>,
|
|
||||||
|
|
||||||
pub(crate) scopes: ScopeArena,
|
pub(crate) scopes: ScopeArena,
|
||||||
|
|
||||||
@@ -156,30 +145,22 @@ impl VirtualDom {
|
|||||||
sender: UnboundedSender<SchedulerMsg>,
|
sender: UnboundedSender<SchedulerMsg>,
|
||||||
receiver: UnboundedReceiver<SchedulerMsg>,
|
receiver: UnboundedReceiver<SchedulerMsg>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut scopes = ScopeArena::new(sender);
|
let mut scopes = ScopeArena::new(sender.clone());
|
||||||
|
|
||||||
let base_scope = scopes.new_with_key(
|
let caller = Box::new(move |f: &Scope| -> Element { root(f, &root_props) });
|
||||||
//
|
let caller_ref: *mut dyn Fn(&Scope) -> Element = Box::into_raw(caller);
|
||||||
root as _,
|
let base_scope = scopes.new_with_key(root as _, caller_ref, None, 0, 0);
|
||||||
todo!(),
|
|
||||||
// boxed_comp.as_ref(),
|
|
||||||
None,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
scopes,
|
scopes,
|
||||||
base_scope,
|
base_scope,
|
||||||
receiver,
|
receiver,
|
||||||
sender,
|
// todo: clean this up manually?
|
||||||
|
_root_caller: caller_ref,
|
||||||
_root_props: todo!(),
|
|
||||||
_root_caller: todo!(),
|
|
||||||
|
|
||||||
pending_messages: VecDeque::new(),
|
pending_messages: VecDeque::new(),
|
||||||
pending_futures: Default::default(),
|
pending_futures: Default::default(),
|
||||||
dirty_scopes: Default::default(),
|
dirty_scopes: Default::default(),
|
||||||
|
sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +170,7 @@ impl VirtualDom {
|
|||||||
/// directly.
|
/// directly.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
pub fn base_scope(&self) -> &ScopeState {
|
pub fn base_scope(&self) -> &Scope {
|
||||||
self.get_scope(&self.base_scope).unwrap()
|
self.get_scope(&self.base_scope).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +180,7 @@ impl VirtualDom {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub fn get_scope<'a>(&'a self, id: &ScopeId) -> Option<&'a ScopeState> {
|
pub fn get_scope<'a>(&'a self, id: &ScopeId) -> Option<&'a Scope> {
|
||||||
self.scopes.get_scope(id)
|
self.scopes.get_scope(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,9 +383,11 @@ impl VirtualDom {
|
|||||||
|
|
||||||
log::debug!("about to run scope {:?}", scopeid);
|
log::debug!("about to run scope {:?}", scopeid);
|
||||||
|
|
||||||
if self.run_scope(&scopeid) {
|
if self.scopes.run_scope(&scopeid) {
|
||||||
let scope = self.scopes.get_scope(&scopeid).unwrap();
|
let (old, new) = (
|
||||||
let (old, new) = (scope.wip_head(), scope.fin_head());
|
self.scopes.wip_head(&scopeid),
|
||||||
|
self.scopes.fin_head(&scopeid),
|
||||||
|
);
|
||||||
diff_state.stack.scope_stack.push(scopeid);
|
diff_state.stack.scope_stack.push(scopeid);
|
||||||
diff_state.stack.push(DiffInstruction::Diff { new, old });
|
diff_state.stack.push(DiffInstruction::Diff { new, old });
|
||||||
}
|
}
|
||||||
@@ -457,13 +440,20 @@ impl VirtualDom {
|
|||||||
/// apply_edits(edits);
|
/// apply_edits(edits);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn rebuild(&mut self) -> Mutations {
|
pub fn rebuild(&mut self) -> Mutations {
|
||||||
// todo: I think we need to append a node or something
|
let mut diff_machine = DiffState::new(Mutations::new());
|
||||||
// diff_machine
|
|
||||||
// .stack
|
|
||||||
// .create_node(cur_component.frames.fin_head(), MountType::Append);
|
|
||||||
|
|
||||||
let scope = self.base_scope;
|
let scope_id = self.base_scope;
|
||||||
self.hard_diff(&scope).unwrap()
|
if self.scopes.run_scope(&scope_id) {
|
||||||
|
diff_machine
|
||||||
|
.stack
|
||||||
|
.create_node(self.scopes.fin_head(&scope_id), MountType::Append);
|
||||||
|
|
||||||
|
diff_machine.stack.scope_stack.push(scope_id);
|
||||||
|
|
||||||
|
self.scopes.work(&mut diff_machine, || false);
|
||||||
|
}
|
||||||
|
|
||||||
|
diff_machine.mutations
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a manual diff of the VirtualDOM between states.
|
/// Compute a manual diff of the VirtualDOM between states.
|
||||||
@@ -503,73 +493,20 @@ impl VirtualDom {
|
|||||||
pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
|
pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
|
||||||
log::debug!("hard diff {:?}", scope_id);
|
log::debug!("hard diff {:?}", scope_id);
|
||||||
|
|
||||||
if self.run_scope(scope_id) {
|
if self.scopes.run_scope(scope_id) {
|
||||||
let mut diff_machine = DiffState::new(Mutations::new());
|
let mut diff_machine = DiffState::new(Mutations::new());
|
||||||
|
|
||||||
diff_machine.force_diff = true;
|
diff_machine.force_diff = true;
|
||||||
|
|
||||||
self.scopes.diff_scope(&mut diff_machine, scope_id);
|
self.scopes.diff_scope(&mut diff_machine, scope_id);
|
||||||
|
|
||||||
|
dbg!(&diff_machine.mutations);
|
||||||
|
|
||||||
Some(diff_machine.mutations)
|
Some(diff_machine.mutations)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_scope(&self, id: &ScopeId) -> bool {
|
|
||||||
let scope = self
|
|
||||||
.scopes
|
|
||||||
.get_scope(id)
|
|
||||||
.expect("The base scope should never be moved");
|
|
||||||
|
|
||||||
// Cycle to the next frame and then reset it
|
|
||||||
// This breaks any latent references, invalidating every pointer referencing into it.
|
|
||||||
// Remove all the outdated listeners
|
|
||||||
self.scopes.ensure_drop_safety(id);
|
|
||||||
|
|
||||||
// Safety:
|
|
||||||
// - We dropped the listeners, so no more &mut T can be used while these are held
|
|
||||||
// - All children nodes that rely on &mut T are replaced with a new reference
|
|
||||||
unsafe { scope.hooks.reset() };
|
|
||||||
|
|
||||||
// Safety:
|
|
||||||
// - We've dropped all references to the wip bump frame with "ensure_drop_safety"
|
|
||||||
unsafe { scope.reset_wip_frame() };
|
|
||||||
|
|
||||||
let mut items = scope.items.borrow_mut();
|
|
||||||
|
|
||||||
// just forget about our suspended nodes while we're at it
|
|
||||||
items.suspended_nodes.clear();
|
|
||||||
|
|
||||||
// guarantee that we haven't screwed up - there should be no latent references anywhere
|
|
||||||
debug_assert!(items.listeners.is_empty());
|
|
||||||
debug_assert!(items.suspended_nodes.is_empty());
|
|
||||||
debug_assert!(items.borrowed_props.is_empty());
|
|
||||||
|
|
||||||
log::debug!("Borrowed stuff is successfully cleared");
|
|
||||||
|
|
||||||
// temporarily cast the vcomponent to the right lifetime
|
|
||||||
// let vcomp = scope.load_vcomp();
|
|
||||||
|
|
||||||
let render: &dyn Fn(&ScopeState) -> Element = todo!();
|
|
||||||
|
|
||||||
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
|
||||||
if let Some(key) = render(scope) {
|
|
||||||
// todo!("attach the niode");
|
|
||||||
// let new_head = builder.into_vnode(NodeFactory {
|
|
||||||
// bump: &scope.frames.wip_frame().bump,
|
|
||||||
// });
|
|
||||||
// log::debug!("Render is successful");
|
|
||||||
|
|
||||||
// the user's component succeeded. We can safely cycle to the next frame
|
|
||||||
// scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
|
|
||||||
// scope.frames.cycle_frame();
|
|
||||||
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum SchedulerMsg {
|
pub enum SchedulerMsg {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fn test_borrowed_state() {
|
|||||||
let _ = VirtualDom::new(Parent);
|
let _ = VirtualDom::new(Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Parent((cx, _): Scope<()>) -> Element {
|
fn Parent(cx: Context, props: &()) -> Element {
|
||||||
let value = cx.use_hook(|_| String::new(), |f| &*f);
|
let value = cx.use_hook(|_| String::new(), |f| &*f);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
@@ -28,7 +28,7 @@ struct ChildProps<'a> {
|
|||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child((cx, props): Scope<ChildProps>) -> Element {
|
fn Child(cx: Context, props: &ChildProps) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 { "it's nested" }
|
h1 { "it's nested" }
|
||||||
@@ -42,7 +42,7 @@ struct Grandchild<'a> {
|
|||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child2((cx, props): Scope<Grandchild>) -> Element {
|
fn Child2(cx: Context, props: &Grandchild) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div { "Hello {props.name}!" }
|
div { "Hello {props.name}!" }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ fn new_dom<P: 'static + Send>(app: FC<P>, props: P) -> VirtualDom {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_original_diff() {
|
fn test_original_diff() {
|
||||||
static APP: FC<()> = |(cx, props)| {
|
static APP: FC<()> = |cx, props| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
@@ -57,17 +57,17 @@ fn test_original_diff() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create() {
|
fn create() {
|
||||||
static APP: FC<()> = |(cx, props)| {
|
static APP: FC<()> = |cx, props| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
"Hello, world!"
|
"Hello, world!"
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
// Fragment {
|
Fragment {
|
||||||
// "hello"
|
"hello"
|
||||||
// "world"
|
"world"
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ fn create() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_list() {
|
fn create_list() {
|
||||||
static APP: FC<()> = |(cx, props)| {
|
static APP: FC<()> = |cx, props| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
{(0..3).map(|f| rsx!{ div {
|
{(0..3).map(|f| rsx!{ div {
|
||||||
"hello"
|
"hello"
|
||||||
@@ -169,7 +169,7 @@ fn create_list() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_simple() {
|
fn create_simple() {
|
||||||
static APP: FC<()> = |(cx, props)| {
|
static APP: FC<()> = |cx, props| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {}
|
div {}
|
||||||
div {}
|
div {}
|
||||||
@@ -207,7 +207,7 @@ fn create_simple() {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn create_components() {
|
fn create_components() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
Child { "abc1" }
|
Child { "abc1" }
|
||||||
Child { "abc2" }
|
Child { "abc2" }
|
||||||
@@ -220,7 +220,7 @@ fn create_components() {
|
|||||||
children: ScopeChildren<'a>,
|
children: ScopeChildren<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child<'a>((cx, props): Scope<'a, ChildProps<'a>>) -> Element {
|
fn Child<'a>(cx: Context<'a>, props: &ChildProps<'a>) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 {}
|
h1 {}
|
||||||
div { {&props.children} }
|
div { {&props.children} }
|
||||||
@@ -273,7 +273,7 @@ fn create_components() {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn anchors() {
|
fn anchors() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
{true.then(|| rsx!{ div { "hello" } })}
|
{true.then(|| rsx!{ div { "hello" } })}
|
||||||
{false.then(|| rsx!{ div { "goodbye" } })}
|
{false.then(|| rsx!{ div { "goodbye" } })}
|
||||||
@@ -302,17 +302,18 @@ fn anchors() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn suspended() {
|
fn suspended() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
todo!()
|
||||||
let val = use_suspense(cx, || async {}, |p| todo!());
|
// static App: FC<()> = |cx, props| {
|
||||||
|
// let val = use_suspense(cx, || async {}, |p| todo!());
|
||||||
|
|
||||||
cx.render(rsx! { {val} })
|
// cx.render(rsx! { {val} })
|
||||||
};
|
// };
|
||||||
|
|
||||||
let mut dom = new_dom(App, ());
|
// let mut dom = new_dom(App, ());
|
||||||
let mutations = dom.rebuild();
|
// let mutations = dom.rebuild();
|
||||||
|
|
||||||
assert_eq!(
|
// assert_eq!(
|
||||||
mutations.edits,
|
// mutations.edits,
|
||||||
[CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
|
// [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ mod test_logging;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn please_work() {
|
fn please_work() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
hidden: "true"
|
hidden: "true"
|
||||||
@@ -27,7 +27,7 @@ fn please_work() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static Child: FC<()> = |(cx, props)| {
|
static Child: FC<()> = |cx, props| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div { "child" }
|
div { "child" }
|
||||||
})
|
})
|
||||||
@@ -35,6 +35,4 @@ fn please_work() {
|
|||||||
|
|
||||||
let mut dom = VirtualDom::new(App);
|
let mut dom = VirtualDom::new(App);
|
||||||
dom.rebuild();
|
dom.rebuild();
|
||||||
|
|
||||||
println!("{}", dom);
|
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ fn manual_diffing() {
|
|||||||
value: Shared<&'static str>,
|
value: Shared<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static App: FC<AppProps> = |(cx, props)| {
|
static App: FC<AppProps> = |cx, props| {
|
||||||
let val = props.value.lock().unwrap();
|
let val = props.value.lock().unwrap();
|
||||||
cx.render(rsx! { div { "{val}" } })
|
cx.render(rsx! { div { "{val}" } })
|
||||||
};
|
};
|
||||||
@@ -37,7 +37,7 @@ fn manual_diffing() {
|
|||||||
|
|
||||||
*value.lock().unwrap() = "goodbye";
|
*value.lock().unwrap() = "goodbye";
|
||||||
|
|
||||||
let edits = dom.diff();
|
let edits = dom.rebuild();
|
||||||
|
|
||||||
log::debug!("edits: {:?}", edits);
|
log::debug!("edits: {:?}", edits);
|
||||||
}
|
}
|
||||||
@@ -13,12 +13,12 @@ mod test_logging;
|
|||||||
fn shared_state_test() {
|
fn shared_state_test() {
|
||||||
struct MySharedState(&'static str);
|
struct MySharedState(&'static str);
|
||||||
|
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
cx.provide_state(MySharedState("world!"));
|
cx.provide_state(MySharedState("world!"));
|
||||||
cx.render(rsx!(Child {}))
|
cx.render(rsx!(Child {}))
|
||||||
};
|
};
|
||||||
|
|
||||||
static Child: FC<()> = |(cx, props)| {
|
static Child: FC<()> = |cx, props| {
|
||||||
let shared = cx.consume_state::<MySharedState>()?;
|
let shared = cx.consume_state::<MySharedState>()?;
|
||||||
cx.render(rsx!("Hello, {shared.0}"))
|
cx.render(rsx!("Hello, {shared.0}"))
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use dioxus_html as dioxus_elements;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn app_runs() {
|
fn app_runs() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
//
|
//
|
||||||
cx.render(rsx!( div{"hello"} ))
|
cx.render(rsx!( div{"hello"} ))
|
||||||
};
|
};
|
||||||
@@ -28,7 +28,7 @@ fn app_runs() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fragments_work() {
|
fn fragments_work() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
div{"hello"}
|
div{"hello"}
|
||||||
div{"goodbye"}
|
div{"goodbye"}
|
||||||
@@ -42,7 +42,7 @@ fn fragments_work() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lists_work() {
|
fn lists_work() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
h1 {"hello"}
|
h1 {"hello"}
|
||||||
{(0..6).map(|f| rsx!(span{ "{f}" }))}
|
{(0..6).map(|f| rsx!(span{ "{f}" }))}
|
||||||
@@ -55,7 +55,7 @@ fn lists_work() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn conditional_rendering() {
|
fn conditional_rendering() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
h1 {"hello"}
|
h1 {"hello"}
|
||||||
{true.then(|| rsx!(span{ "a" }))}
|
{true.then(|| rsx!(span{ "a" }))}
|
||||||
@@ -72,13 +72,13 @@ fn conditional_rendering() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn child_components() {
|
fn child_components() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
static App: FC<()> = |cx, props| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
{true.then(|| rsx!(Child { }))}
|
{true.then(|| rsx!(Child { }))}
|
||||||
{false.then(|| rsx!(Child { }))}
|
{false.then(|| rsx!(Child { }))}
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
static Child: FC<()> = |(cx, props)| {
|
static Child: FC<()> = |cx, props| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
h1 {"hello"}
|
h1 {"hello"}
|
||||||
h1 {"goodbye"}
|
h1 {"goodbye"}
|
||||||
@@ -91,13 +91,14 @@ fn child_components() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn suspended_works() {
|
fn suspended_works() {
|
||||||
static App: FC<()> = |(cx, props)| {
|
todo!()
|
||||||
let title = use_suspense(cx, || async { "bob" }, move |cx, f| todo!());
|
// static App: FC<()> = |cx, props| {
|
||||||
// let title = use_suspense(cx, || async { "bob" }, move |cx, f| rsx! { "{f}"});
|
// let title = use_suspense(cx, || async { "bob" }, move |cx, f| todo!());
|
||||||
cx.render(rsx!("hello" { title }))
|
// // let title = use_suspense(cx, || async { "bob" }, move |cx, f| rsx! { "{f}"});
|
||||||
};
|
// cx.render(rsx!("hello" { title }))
|
||||||
|
// };
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(App);
|
// let mut vdom = VirtualDom::new(App);
|
||||||
let edits = vdom.rebuild();
|
// let edits = vdom.rebuild();
|
||||||
dbg!(edits);
|
// dbg!(edits);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user