feat: wire up linked nodes
This commit is contained in:
1 parent
fd93ee89c1
commit
c10c1f418b
6 files changed
+325
-131
No files matched your search
@@ -47,6 +47,7 @@ fern = { version = "0.6.0", features = ["colored"] }
|
|||||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||||
simple_logger = "1.13.0"
|
simple_logger = "1.13.0"
|
||||||
dioxus-core-macro = { path = "../core-macro", version = "0.1.2" }
|
dioxus-core-macro = { path = "../core-macro", version = "0.1.2" }
|
||||||
|
dioxus-hooks = { path = "../hooks" }
|
||||||
# async-std = { version = "1.9.0", features = ["attributes"] }
|
# async-std = { version = "1.9.0", features = ["attributes"] }
|
||||||
# criterion = "0.3.5"
|
# criterion = "0.3.5"
|
||||||
|
|
||||||
|
|||||||
+69
-61
@@ -240,6 +240,8 @@ impl<'bump> DiffState<'bump> {
|
|||||||
pub fn diff_scope(&mut self, id: &ScopeId) {
|
pub fn diff_scope(&mut self, id: &ScopeId) {
|
||||||
let (old, new) = (self.scopes.wip_head(id), self.scopes.fin_head(id));
|
let (old, new) = (self.scopes.wip_head(id), self.scopes.fin_head(id));
|
||||||
self.stack.push(DiffInstruction::Diff { old, new });
|
self.stack.push(DiffInstruction::Diff { old, new });
|
||||||
|
self.stack.scope_stack.push(*id);
|
||||||
|
self.stack.push_nodes_created(0);
|
||||||
self.work(|| false);
|
self.work(|| false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,13 +283,9 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VNode::Linked(linked) => {
|
VNode::Linked(linked) => {
|
||||||
todo!("load linked");
|
let node = unsafe { &*linked.node };
|
||||||
0
|
let node: &VNode = unsafe { std::mem::transmute(node) };
|
||||||
// let num_on_stack = linked.children.iter().map(|child| {
|
self.push_all_nodes(node)
|
||||||
// self.push_all_nodes( child)
|
|
||||||
// }).sum();
|
|
||||||
// self.mutations.push_root(node.mounted_id());
|
|
||||||
// num_on_stack + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VNode::Fragment(_) | VNode::Component(_) => {
|
VNode::Fragment(_) | VNode::Component(_) => {
|
||||||
@@ -321,16 +319,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MountType::Replace { old } => {
|
MountType::Replace { old } => {
|
||||||
// todo: a bug here where we remove a node that is alread being replaced
|
self.replace_node(old, nodes_created);
|
||||||
if let Some(old_id) = old.try_mounted_id() {
|
|
||||||
self.mutations.replace_with(old_id, nodes_created as u32);
|
|
||||||
self.remove_nodes(Some(old), false);
|
|
||||||
} else {
|
|
||||||
if let Some(id) = self.find_first_element_id(old) {
|
|
||||||
self.mutations.replace_with(id, nodes_created as u32);
|
|
||||||
}
|
|
||||||
self.remove_nodes(Some(old), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MountType::Append => {
|
MountType::Append => {
|
||||||
@@ -386,8 +375,10 @@ impl<'bump> DiffState<'bump> {
|
|||||||
|
|
||||||
fn create_anchor_node(&mut self, anchor: &'bump VAnchor, node: &'bump VNode<'bump>) {
|
fn create_anchor_node(&mut self, anchor: &'bump VAnchor, node: &'bump VNode<'bump>) {
|
||||||
let real_id = self.scopes.reserve_node(node);
|
let real_id = self.scopes.reserve_node(node);
|
||||||
|
|
||||||
self.mutations.create_placeholder(real_id);
|
self.mutations.create_placeholder(real_id);
|
||||||
anchor.dom_id.set(Some(real_id));
|
anchor.dom_id.set(Some(real_id));
|
||||||
|
|
||||||
self.stack.add_child_count(1);
|
self.stack.add_child_count(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,11 +479,13 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_linked_node(&mut self, link: &'bump NodeLink) {
|
fn create_linked_node(&mut self, link: &'bump NodeLink) {
|
||||||
if let Some(cur_scope) = self.stack.current_scope() {
|
if link.scope_id.get().is_none() {
|
||||||
link.scope_id.set(Some(cur_scope));
|
if let Some(cur_scope) = self.stack.current_scope() {
|
||||||
let node: &'bump VNode<'static> = unsafe { &*link.node };
|
link.scope_id.set(Some(cur_scope));
|
||||||
self.create_node(unsafe { std::mem::transmute(node) });
|
}
|
||||||
}
|
}
|
||||||
|
let node: &'bump VNode<'static> = unsafe { &*link.node };
|
||||||
|
self.create_node(unsafe { std::mem::transmute(node) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================
|
// =================================
|
||||||
@@ -692,9 +685,16 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn diff_linked_nodes(&mut self, old: &'bump NodeLink, new: &'bump NodeLink) {
|
fn diff_linked_nodes(&mut self, old: &'bump NodeLink, new: &'bump NodeLink) {
|
||||||
todo!();
|
if !std::ptr::eq(old.node, new.node) {
|
||||||
// new.dom_id.set(old.dom_id.get());
|
// if the ptrs are the same then theyr're the same
|
||||||
// self.attach_linked_node_to_scope( new);
|
let old: &VNode = unsafe { std::mem::transmute(&*old.node) };
|
||||||
|
let new: &VNode = unsafe { std::mem::transmute(&*new.node) };
|
||||||
|
self.diff_node(old, new);
|
||||||
|
}
|
||||||
|
|
||||||
|
if new.scope_id.get().is_none() {
|
||||||
|
todo!("attach the link to the scope - when children are not created");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================
|
// =============================================
|
||||||
@@ -735,7 +735,14 @@ impl<'bump> DiffState<'bump> {
|
|||||||
.create_children(new, MountType::Replace { old: &old[0] });
|
.create_children(new, MountType::Replace { old: &old[0] });
|
||||||
}
|
}
|
||||||
(_, [VNode::Anchor(_)]) => {
|
(_, [VNode::Anchor(_)]) => {
|
||||||
self.replace_and_create_many_with_one(old, &new[0]);
|
let new: &'bump VNode<'bump> = &new[0];
|
||||||
|
if let Some(first_old) = old.get(0) {
|
||||||
|
self.remove_nodes(&old[1..], true);
|
||||||
|
self.stack
|
||||||
|
.create_node(new, MountType::Replace { old: first_old });
|
||||||
|
} else {
|
||||||
|
self.stack.create_node(new, MountType::Append {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let new_is_keyed = new[0].key().is_some();
|
let new_is_keyed = new[0].key().is_some();
|
||||||
@@ -1032,7 +1039,14 @@ impl<'bump> DiffState<'bump> {
|
|||||||
log::debug!("old_key_to_old_index, {:#?}", old_key_to_old_index);
|
log::debug!("old_key_to_old_index, {:#?}", old_key_to_old_index);
|
||||||
log::debug!("new_index_to_old_index, {:#?}", new_index_to_old_index);
|
log::debug!("new_index_to_old_index, {:#?}", new_index_to_old_index);
|
||||||
log::debug!("shared_keys, {:#?}", shared_keys);
|
log::debug!("shared_keys, {:#?}", shared_keys);
|
||||||
self.replace_and_create_many_with_many(old, new);
|
|
||||||
|
if let Some(first_old) = old.get(0) {
|
||||||
|
self.remove_nodes(&old[1..], true);
|
||||||
|
self.stack
|
||||||
|
.create_children(new, MountType::Replace { old: first_old })
|
||||||
|
} else {
|
||||||
|
self.stack.create_children(new, MountType::Append {});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1167,11 +1181,11 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
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.scopes.get_scope(&scope_id).unwrap();
|
|
||||||
search_node = Some(self.scopes.root_node(&scope_id));
|
search_node = Some(self.scopes.root_node(&scope_id));
|
||||||
}
|
}
|
||||||
VNode::Linked(link) => {
|
VNode::Linked(link) => {
|
||||||
todo!("linked")
|
let node = unsafe { std::mem::transmute(&*link.node) };
|
||||||
|
search_node = Some(node);
|
||||||
}
|
}
|
||||||
VNode::Text(t) => break t.dom_id.get(),
|
VNode::Text(t) => break t.dom_id.get(),
|
||||||
VNode::Element(t) => break t.dom_id.get(),
|
VNode::Element(t) => break t.dom_id.get(),
|
||||||
@@ -1181,17 +1195,27 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_and_create_many_with_one(
|
fn replace_node(&mut self, old: &'bump VNode<'bump>, nodes_created: usize) {
|
||||||
&mut self,
|
match old {
|
||||||
old: &'bump [VNode<'bump>],
|
VNode::Text(_) | VNode::Element(_) | VNode::Anchor(_) | VNode::Suspended(_) => {
|
||||||
new: &'bump VNode<'bump>,
|
let id = old.try_mounted_id().expect(&format!("broke on {:?}", old));
|
||||||
) {
|
self.mutations.replace_with(id, nodes_created as u32);
|
||||||
if let Some(first_old) = old.get(0) {
|
}
|
||||||
self.remove_nodes(&old[1..], true);
|
|
||||||
self.stack
|
VNode::Fragment(f) => {
|
||||||
.create_node(new, MountType::Replace { old: first_old });
|
self.replace_node(&f.children[0], nodes_created);
|
||||||
} else {
|
self.remove_nodes(f.children.iter().skip(1), true);
|
||||||
self.stack.create_node(new, MountType::Append {});
|
}
|
||||||
|
|
||||||
|
VNode::Component(c) => {
|
||||||
|
let node = self.scopes.fin_head(&c.associated_scope.get().unwrap());
|
||||||
|
self.replace_node(node, nodes_created);
|
||||||
|
}
|
||||||
|
|
||||||
|
VNode::Linked(l) => {
|
||||||
|
let node: &'bump VNode<'bump> = unsafe { std::mem::transmute(&*l.node) };
|
||||||
|
self.replace_node(node, nodes_created);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,11 +1230,13 @@ impl<'bump> DiffState<'bump> {
|
|||||||
for node in nodes {
|
for node in nodes {
|
||||||
match node {
|
match node {
|
||||||
VNode::Text(t) => {
|
VNode::Text(t) => {
|
||||||
let id = t.dom_id.get().unwrap();
|
// this check exists because our null node will be removed but does not have an ID
|
||||||
self.scopes.collect_garbage(id);
|
if let Some(id) = t.dom_id.get() {
|
||||||
|
self.scopes.collect_garbage(id);
|
||||||
|
|
||||||
if gen_muts {
|
if gen_muts {
|
||||||
self.mutations.remove(id.as_u64());
|
self.mutations.remove(id.as_u64());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VNode::Suspended(s) => {
|
VNode::Suspended(s) => {
|
||||||
@@ -1259,24 +1285,6 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove all the old nodes and replace them with newly created new nodes.
|
|
||||||
///
|
|
||||||
/// The new nodes *will* be created - don't create them yourself!
|
|
||||||
fn replace_and_create_many_with_many(
|
|
||||||
&mut self,
|
|
||||||
|
|
||||||
old: &'bump [VNode<'bump>],
|
|
||||||
new: &'bump [VNode<'bump>],
|
|
||||||
) {
|
|
||||||
if let Some(first_old) = old.get(0) {
|
|
||||||
self.remove_nodes(&old[1..], true);
|
|
||||||
self.stack
|
|
||||||
.create_children(new, MountType::Replace { old: first_old })
|
|
||||||
} else {
|
|
||||||
self.stack.create_children(new, MountType::Append {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a listener closure to a scope during diff.
|
/// Adds a listener closure to a scope during diff.
|
||||||
fn attach_listener_to_scope(&mut self, listener: &'bump Listener<'bump>, scope: &Scope) {
|
fn attach_listener_to_scope(&mut self, listener: &'bump Listener<'bump>, scope: &Scope) {
|
||||||
let long_listener = unsafe { std::mem::transmute(listener) };
|
let long_listener = unsafe { std::mem::transmute(listener) };
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ impl<'src> VNode<'src> {
|
|||||||
VNode::Element(el) => el.dom_id.get(),
|
VNode::Element(el) => el.dom_id.get(),
|
||||||
VNode::Anchor(el) => el.dom_id.get(),
|
VNode::Anchor(el) => el.dom_id.get(),
|
||||||
VNode::Suspended(el) => el.dom_id.get(),
|
VNode::Suspended(el) => el.dom_id.get(),
|
||||||
|
|
||||||
VNode::Linked(_) => None,
|
VNode::Linked(_) => None,
|
||||||
VNode::Fragment(_) => None,
|
VNode::Fragment(_) => None,
|
||||||
VNode::Component(_) => None,
|
VNode::Component(_) => None,
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
|
use bumpalo::Bump;
|
||||||
|
use futures_channel::mpsc::UnboundedSender;
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
|
|
||||||
pub type FcSlot = *const ();
|
pub type FcSlot = *const ();
|
||||||
@@ -61,71 +60,71 @@ impl ScopeArena {
|
|||||||
height: u32,
|
height: u32,
|
||||||
subtree: u32,
|
subtree: u32,
|
||||||
) -> ScopeId {
|
) -> ScopeId {
|
||||||
if let Some(id) = self.free_scopes.borrow_mut().pop() {
|
let scope_id = ScopeId(self.scopes.borrow().len());
|
||||||
// have already called drop on it - the slot is still chillin tho
|
|
||||||
// let scope = unsafe { &mut *self.scopes.borrow()[id.0 as usize] };
|
|
||||||
|
|
||||||
todo!("override the scope contents");
|
let (node_capacity, hook_capacity) = {
|
||||||
|
let heuristics = self.heuristics.borrow();
|
||||||
|
if let Some(heuristic) = heuristics.get(&fc_ptr) {
|
||||||
|
(heuristic.node_arena_size, heuristic.hook_arena_size)
|
||||||
|
} else {
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
|
||||||
|
|
||||||
|
frames[0].nodes.get_mut().push({
|
||||||
|
let vnode = frames[0]
|
||||||
|
.bump
|
||||||
|
.alloc(VNode::Text(frames[0].bump.alloc(VText {
|
||||||
|
dom_id: Default::default(),
|
||||||
|
is_static: false,
|
||||||
|
text: "",
|
||||||
|
})));
|
||||||
|
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||||
|
});
|
||||||
|
|
||||||
|
frames[1].nodes.get_mut().push({
|
||||||
|
let vnode = frames[1]
|
||||||
|
.bump
|
||||||
|
.alloc(VNode::Text(frames[1].bump.alloc(VText {
|
||||||
|
dom_id: Default::default(),
|
||||||
|
is_static: false,
|
||||||
|
text: "",
|
||||||
|
})));
|
||||||
|
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut new_scope = Scope {
|
||||||
|
sender: self.sender.clone(),
|
||||||
|
our_arena_idx: scope_id,
|
||||||
|
parent_scope,
|
||||||
|
height,
|
||||||
|
frames,
|
||||||
|
subtree: Cell::new(subtree),
|
||||||
|
is_subtree_root: Cell::new(false),
|
||||||
|
|
||||||
|
caller,
|
||||||
|
generation: 0.into(),
|
||||||
|
|
||||||
|
hooks: HookList::new(hook_capacity),
|
||||||
|
shared_contexts: Default::default(),
|
||||||
|
|
||||||
|
items: RefCell::new(SelfReferentialItems {
|
||||||
|
listeners: Default::default(),
|
||||||
|
borrowed_props: Default::default(),
|
||||||
|
suspended_nodes: Default::default(),
|
||||||
|
tasks: Default::default(),
|
||||||
|
pending_effects: Default::default(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(id) = self.free_scopes.borrow_mut().pop() {
|
||||||
|
let scope = unsafe { self.get_scope_mut(&id) }.unwrap();
|
||||||
|
std::mem::swap(&mut new_scope, scope);
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
let scope_id = ScopeId(self.scopes.borrow().len());
|
let scope = self.bump.alloc(new_scope);
|
||||||
|
|
||||||
let (node_capacity, hook_capacity) = {
|
|
||||||
let heuristics = self.heuristics.borrow();
|
|
||||||
if let Some(heuristic) = heuristics.get(&fc_ptr) {
|
|
||||||
(heuristic.node_arena_size, heuristic.hook_arena_size)
|
|
||||||
} else {
|
|
||||||
(0, 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
|
|
||||||
|
|
||||||
frames[0].nodes.get_mut().push({
|
|
||||||
let vnode = frames[0]
|
|
||||||
.bump
|
|
||||||
.alloc(VNode::Text(frames[0].bump.alloc(VText {
|
|
||||||
dom_id: Default::default(),
|
|
||||||
is_static: false,
|
|
||||||
text: "",
|
|
||||||
})));
|
|
||||||
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
|
||||||
});
|
|
||||||
|
|
||||||
frames[1].nodes.get_mut().push({
|
|
||||||
let vnode = frames[1]
|
|
||||||
.bump
|
|
||||||
.alloc(VNode::Text(frames[1].bump.alloc(VText {
|
|
||||||
dom_id: Default::default(),
|
|
||||||
is_static: false,
|
|
||||||
text: "",
|
|
||||||
})));
|
|
||||||
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
|
||||||
});
|
|
||||||
|
|
||||||
let scope = self.bump.alloc(Scope {
|
|
||||||
sender: self.sender.clone(),
|
|
||||||
parent_scope,
|
|
||||||
our_arena_idx: scope_id,
|
|
||||||
height,
|
|
||||||
subtree: Cell::new(subtree),
|
|
||||||
is_subtree_root: Cell::new(false),
|
|
||||||
frames,
|
|
||||||
|
|
||||||
hooks: HookList::new(hook_capacity),
|
|
||||||
shared_contexts: Default::default(),
|
|
||||||
caller,
|
|
||||||
generation: 0.into(),
|
|
||||||
|
|
||||||
items: RefCell::new(SelfReferentialItems {
|
|
||||||
listeners: Default::default(),
|
|
||||||
borrowed_props: Default::default(),
|
|
||||||
suspended_nodes: Default::default(),
|
|
||||||
tasks: Default::default(),
|
|
||||||
pending_effects: Default::default(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.scopes.borrow_mut().push(scope);
|
self.scopes.borrow_mut().push(scope);
|
||||||
scope_id
|
scope_id
|
||||||
}
|
}
|
||||||
@@ -274,6 +273,8 @@ impl ScopeArena {
|
|||||||
// self.
|
// self.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make the "wip frame" contents the "finished frame"
|
||||||
|
// any future dipping into completed nodes after "render" will go through "fin head"
|
||||||
scope.cycle_frame();
|
scope.cycle_frame();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
@@ -300,6 +301,6 @@ impl ScopeArena {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_node(&self, id: &ScopeId) -> &VNode {
|
pub fn root_node(&self, id: &ScopeId) -> &VNode {
|
||||||
self.wip_head(id)
|
self.fin_head(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,15 +198,19 @@ impl VirtualDom {
|
|||||||
let caller_ref: *mut dyn Fn(&Scope) -> Element = Box::into_raw(caller);
|
let caller_ref: *mut dyn Fn(&Scope) -> Element = Box::into_raw(caller);
|
||||||
let base_scope = scopes.new_with_key(root as _, caller_ref, None, 0, 0);
|
let base_scope = scopes.new_with_key(root as _, caller_ref, None, 0, 0);
|
||||||
|
|
||||||
|
let pending_messages = VecDeque::new();
|
||||||
|
let mut dirty_scopes = IndexSet::new();
|
||||||
|
dirty_scopes.insert(base_scope);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
scopes: Box::new(scopes),
|
scopes: Box::new(scopes),
|
||||||
base_scope,
|
base_scope,
|
||||||
receiver,
|
receiver,
|
||||||
// todo: clean this up manually?
|
// todo: clean this up manually?
|
||||||
_root_caller: caller_ref,
|
_root_caller: caller_ref,
|
||||||
pending_messages: VecDeque::new(),
|
pending_messages,
|
||||||
pending_futures: Default::default(),
|
pending_futures: Default::default(),
|
||||||
dirty_scopes: Default::default(),
|
dirty_scopes,
|
||||||
sender,
|
sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -462,8 +466,8 @@ impl VirtualDom {
|
|||||||
self.dirty_scopes.remove(&scope);
|
self.dirty_scopes.remove(&scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
// I think the stack should be empty at the end of diffing?
|
// // I think the stack should be empty at the end of diffing?
|
||||||
debug_assert_eq!(stack.scope_stack.len(), 1);
|
// debug_assert_eq!(stack.scope_stack.len(), 1);
|
||||||
|
|
||||||
committed_mutations.push(mutations);
|
committed_mutations.push(mutations);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
//! Tests for the lifecycle of components.
|
//! Tests for the lifecycle of components.
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_core::DomEdit::*;
|
||||||
|
use dioxus_core::ScopeId;
|
||||||
|
|
||||||
use dioxus_core_macro::*;
|
use dioxus_core_macro::*;
|
||||||
|
use dioxus_hooks::*;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@@ -41,3 +45,178 @@ fn manual_diffing() {
|
|||||||
|
|
||||||
log::debug!("edits: {:?}", edits);
|
log::debug!("edits: {:?}", edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn events_generate() {
|
||||||
|
static App: FC<()> = |cx, _| {
|
||||||
|
let mut count = use_state(cx, || 0);
|
||||||
|
|
||||||
|
let inner = match *count {
|
||||||
|
0 => {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
onclick: move |_| count += 1,
|
||||||
|
div {
|
||||||
|
"nested"
|
||||||
|
}
|
||||||
|
"Click me!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.render(inner)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut dom = VirtualDom::new(App);
|
||||||
|
let mut channel = dom.get_scheduler_channel();
|
||||||
|
assert!(dom.has_any_work());
|
||||||
|
|
||||||
|
let edits = dom.work_with_deadline(|| false);
|
||||||
|
assert_eq!(
|
||||||
|
edits[0].edits,
|
||||||
|
[
|
||||||
|
CreateElement {
|
||||||
|
tag: "div",
|
||||||
|
root: 0,
|
||||||
|
},
|
||||||
|
NewEventListener {
|
||||||
|
event_name: "click",
|
||||||
|
scope: ScopeId(0),
|
||||||
|
root: 0,
|
||||||
|
},
|
||||||
|
CreateElement {
|
||||||
|
tag: "div",
|
||||||
|
root: 1,
|
||||||
|
},
|
||||||
|
CreateTextNode {
|
||||||
|
text: "nested",
|
||||||
|
root: 2,
|
||||||
|
},
|
||||||
|
AppendChildren { many: 1 },
|
||||||
|
CreateTextNode {
|
||||||
|
text: "Click me!",
|
||||||
|
root: 3,
|
||||||
|
},
|
||||||
|
AppendChildren { many: 2 },
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn components_generate() {
|
||||||
|
static App: FC<()> = |cx, _| {
|
||||||
|
let mut render_phase = use_state(cx, || 0);
|
||||||
|
render_phase += 1;
|
||||||
|
|
||||||
|
cx.render(match *render_phase {
|
||||||
|
0 => rsx!("Text0"),
|
||||||
|
1 => rsx!(div {}),
|
||||||
|
2 => rsx!("Text2"),
|
||||||
|
3 => rsx!(Child {}),
|
||||||
|
4 => rsx!({ None as Option<()> }),
|
||||||
|
5 => rsx!("text 3"),
|
||||||
|
6 => rsx!({ (0..2).map(|f| rsx!("text {f}")) }),
|
||||||
|
7 => rsx!(Child {}),
|
||||||
|
_ => todo!(),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
static Child: FC<()> = |cx, _| {
|
||||||
|
println!("running child");
|
||||||
|
cx.render(rsx! {
|
||||||
|
h1 {}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut dom = VirtualDom::new(App);
|
||||||
|
let edits = dom.rebuild();
|
||||||
|
assert_eq!(
|
||||||
|
edits.edits,
|
||||||
|
[
|
||||||
|
CreateTextNode {
|
||||||
|
text: "Text0",
|
||||||
|
root: 0,
|
||||||
|
},
|
||||||
|
AppendChildren { many: 1 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let edits = dom.hard_diff(&ScopeId(0)).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
edits.edits,
|
||||||
|
[
|
||||||
|
CreateElement {
|
||||||
|
tag: "div",
|
||||||
|
root: 1,
|
||||||
|
},
|
||||||
|
ReplaceWith { root: 0, m: 1 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let edits = dom.hard_diff(&ScopeId(0)).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
edits.edits,
|
||||||
|
[
|
||||||
|
CreateTextNode {
|
||||||
|
text: "Text2",
|
||||||
|
root: 2,
|
||||||
|
},
|
||||||
|
ReplaceWith { root: 1, m: 1 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let edits = dom.hard_diff(&ScopeId(0)).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
edits.edits,
|
||||||
|
[
|
||||||
|
CreateElement { tag: "h1", root: 3 },
|
||||||
|
ReplaceWith { root: 2, m: 1 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let edits = dom.hard_diff(&ScopeId(0)).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
edits.edits,
|
||||||
|
[CreatePlaceholder { root: 4 }, ReplaceWith { root: 3, m: 1 },]
|
||||||
|
);
|
||||||
|
|
||||||
|
let edits = dom.hard_diff(&ScopeId(0)).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
edits.edits,
|
||||||
|
[
|
||||||
|
CreateTextNode {
|
||||||
|
text: "text 3",
|
||||||
|
root: 5,
|
||||||
|
},
|
||||||
|
ReplaceWith { root: 4, m: 1 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let edits = dom.hard_diff(&ScopeId(0)).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
edits.edits,
|
||||||
|
[
|
||||||
|
CreateTextNode {
|
||||||
|
text: "text 0",
|
||||||
|
root: 6,
|
||||||
|
},
|
||||||
|
CreateTextNode {
|
||||||
|
text: "text 1",
|
||||||
|
root: 7,
|
||||||
|
},
|
||||||
|
ReplaceWith { root: 5, m: 2 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let edits = dom.hard_diff(&ScopeId(0)).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
edits.edits,
|
||||||
|
[
|
||||||
|
CreateElement { tag: "h1", root: 8 },
|
||||||
|
ReplaceWith { root: 6, m: 1 },
|
||||||
|
Remove { root: 7 },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user