mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 20:53:06 +00:00
feat: dedicated mutations module
This commit is contained in:
parent
1622f484b3
commit
db6d0184aa
17 changed files with 610 additions and 515 deletions
|
@ -28,11 +28,6 @@ longest-increasing-subsequence = "0.1.0"
|
||||||
# internall used
|
# internall used
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
# # Serialize the Edits for use in Webview/Liveview instances
|
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
|
||||||
|
|
||||||
appendlist = "1.4.0"
|
|
||||||
|
|
||||||
futures-util = "0.3.15"
|
futures-util = "0.3.15"
|
||||||
|
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
|
@ -43,12 +38,15 @@ futures-channel = "0.3.16"
|
||||||
|
|
||||||
# used for noderefs
|
# used for noderefs
|
||||||
once_cell = "1.8.0"
|
once_cell = "1.8.0"
|
||||||
indexmap = "1.7.0"
|
|
||||||
|
# # Serialize the Edits for use in Webview/Liveview instances
|
||||||
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1.0.42"
|
anyhow = "1.0.42"
|
||||||
async-std = { version = "1.9.0", features = ["attributes"] }
|
async-std = { version = "1.9.0", features = ["attributes"] }
|
||||||
|
criterion = "0.3.5"
|
||||||
dioxus-html = { path = "../html" }
|
dioxus-html = { path = "../html" }
|
||||||
fern = { version = "0.6.0", features = ["colored"] }
|
fern = { version = "0.6.0", features = ["colored"] }
|
||||||
simple_logger = "1.13.0"
|
simple_logger = "1.13.0"
|
||||||
|
@ -57,3 +55,7 @@ simple_logger = "1.13.0"
|
||||||
[features]
|
[features]
|
||||||
default = ["serialize"]
|
default = ["serialize"]
|
||||||
serialize = ["serde"]
|
serialize = ["serde"]
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "create"
|
||||||
|
harness = false
|
||||||
|
|
22
packages/core/benches/create.rs
Normal file
22
packages/core/benches/create.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
|
fn fibonacci(n: u64) -> u64 {
|
||||||
|
match n {
|
||||||
|
0 => 1,
|
||||||
|
1 => 1,
|
||||||
|
n => fibonacci(n - 1) + fibonacci(n - 2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
benches();
|
||||||
|
Criterion::default().configure_from_args().final_summary();
|
||||||
|
// $crate::__warn_about_html_reports_feature();
|
||||||
|
// $crate::__warn_about_cargo_bench_support_feature();
|
||||||
|
}
|
|
@ -84,9 +84,8 @@
|
||||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
||||||
|
|
||||||
use crate::{arena::SharedResources, innerlude::*};
|
use crate::{arena::SharedResources, innerlude::*};
|
||||||
use futures_util::Future;
|
use futures_util::{Future, FutureExt};
|
||||||
use fxhash::{FxBuildHasher, FxHashMap, FxHashSet};
|
use fxhash::{FxBuildHasher, FxHashMap, FxHashSet};
|
||||||
use indexmap::IndexSet;
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -113,7 +112,7 @@ pub struct DiffMachine<'bump> {
|
||||||
|
|
||||||
pub nodes_created_stack: SmallVec<[usize; 10]>,
|
pub nodes_created_stack: SmallVec<[usize; 10]>,
|
||||||
|
|
||||||
pub instructions: SmallVec<[DiffInstruction<'bump>; 10]>,
|
pub instructions: Vec<DiffInstruction<'bump>>,
|
||||||
|
|
||||||
pub scope_stack: SmallVec<[ScopeId; 5]>,
|
pub scope_stack: SmallVec<[ScopeId; 5]>,
|
||||||
|
|
||||||
|
@ -124,8 +123,7 @@ pub struct DiffMachine<'bump> {
|
||||||
|
|
||||||
/// The stack instructions we use to diff and create new nodes.
|
/// The stack instructions we use to diff and create new nodes.
|
||||||
///
|
///
|
||||||
/// Right now, we insert an instruction for every child node we want to create and diff. This can be less efficient than
|
/// These instructions are essentially mini state machines that stay on top of the stack until they are finished.
|
||||||
/// a custom iterator type - but this is current easier to implement. In the future, let's try interact with the stack less.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DiffInstruction<'a> {
|
pub enum DiffInstruction<'a> {
|
||||||
DiffNode {
|
DiffNode {
|
||||||
|
@ -138,28 +136,25 @@ pub enum DiffInstruction<'a> {
|
||||||
new: &'a [VNode<'a>],
|
new: &'a [VNode<'a>],
|
||||||
},
|
},
|
||||||
|
|
||||||
// diff two lists of equally sized children
|
|
||||||
DiffEqual {
|
|
||||||
progress: usize,
|
|
||||||
old: &'a [VNode<'a>],
|
|
||||||
new: &'a [VNode<'a>],
|
|
||||||
},
|
|
||||||
|
|
||||||
Create {
|
Create {
|
||||||
node: &'a VNode<'a>,
|
node: &'a VNode<'a>,
|
||||||
and: MountType<'a>,
|
and: MountType<'a>,
|
||||||
},
|
},
|
||||||
|
|
||||||
CreateChildren {
|
Remove {
|
||||||
progress: usize,
|
child: &'a VNode<'a>,
|
||||||
|
},
|
||||||
|
|
||||||
|
RemoveChildren {
|
||||||
children: &'a [VNode<'a>],
|
children: &'a [VNode<'a>],
|
||||||
and: MountType<'a>,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
Mount {
|
Mount {
|
||||||
and: MountType<'a>,
|
and: MountType<'a>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
PopElement,
|
||||||
|
|
||||||
PopScope,
|
PopScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,8 +163,8 @@ pub enum MountType<'a> {
|
||||||
Absorb,
|
Absorb,
|
||||||
Append,
|
Append,
|
||||||
Replace { old: &'a VNode<'a> },
|
Replace { old: &'a VNode<'a> },
|
||||||
InsertAfter { other_node: &'a VNode<'a> },
|
InsertAfter { other_node: Option<&'a VNode<'a>> },
|
||||||
InsertBefore { other_node: &'a VNode<'a> },
|
InsertBefore { other_node: Option<&'a VNode<'a>> },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'bump> DiffMachine<'bump> {
|
impl<'bump> DiffMachine<'bump> {
|
||||||
|
@ -179,7 +174,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
shared: &'bump SharedResources,
|
shared: &'bump SharedResources,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
instructions: smallvec![],
|
instructions: Vec::with_capacity(1000),
|
||||||
nodes_created_stack: smallvec![],
|
nodes_created_stack: smallvec![],
|
||||||
mutations: edits,
|
mutations: edits,
|
||||||
scope_stack: smallvec![cur_scope],
|
scope_stack: smallvec![cur_scope],
|
||||||
|
@ -209,10 +204,9 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
///
|
///
|
||||||
/// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
|
/// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
|
||||||
pub async fn work(&mut self) -> Result<()> {
|
pub async fn work(&mut self) -> Result<()> {
|
||||||
// todo: don't move the reused instructions around
|
|
||||||
// defer to individual functions so the compiler produces better code
|
// defer to individual functions so the compiler produces better code
|
||||||
// large functions tend to be difficult for the compiler to work with
|
// large functions tend to be difficult for the compiler to work with
|
||||||
while let Some(instruction) = self.instructions.last_mut() {
|
while let Some(instruction) = self.instructions.pop() {
|
||||||
log::debug!("Handling diff instruction: {:?}", instruction);
|
log::debug!("Handling diff instruction: {:?}", instruction);
|
||||||
|
|
||||||
// todo: call this less frequently, there is a bit of overhead involved
|
// todo: call this less frequently, there is a bit of overhead involved
|
||||||
|
@ -220,74 +214,50 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
DiffInstruction::PopScope => {
|
DiffInstruction::PopScope => {
|
||||||
self.instructions.pop();
|
|
||||||
self.scope_stack.pop();
|
self.scope_stack.pop();
|
||||||
}
|
}
|
||||||
|
DiffInstruction::PopElement => {
|
||||||
|
self.mutations.pop();
|
||||||
|
}
|
||||||
|
|
||||||
DiffInstruction::DiffNode { old, new, .. } => {
|
DiffInstruction::DiffNode { old, new, .. } => {
|
||||||
let (old, new) = (*old, *new);
|
|
||||||
self.instructions.pop();
|
|
||||||
|
|
||||||
self.diff_node(old, new);
|
self.diff_node(old, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::DiffEqual { progress, old, new } => {
|
|
||||||
debug_assert_eq!(old.len(), new.len());
|
|
||||||
|
|
||||||
if let (Some(old_child), Some(new_child)) =
|
|
||||||
(old.get(*progress), new.get(*progress))
|
|
||||||
{
|
|
||||||
*progress += 1;
|
|
||||||
self.diff_node(old_child, new_child);
|
|
||||||
} else {
|
|
||||||
self.instructions.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is slightly more complicated, we need to find a way to pause our LIS code
|
|
||||||
DiffInstruction::DiffChildren { old, new } => {
|
DiffInstruction::DiffChildren { old, new } => {
|
||||||
let (old, new) = (*old, *new);
|
|
||||||
self.instructions.pop();
|
|
||||||
|
|
||||||
self.diff_children(old, new);
|
self.diff_children(old, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::Create { node, and } => {
|
DiffInstruction::Create { node, and } => {
|
||||||
let (node, and) = (*node, *and);
|
|
||||||
self.instructions.pop();
|
|
||||||
|
|
||||||
self.nodes_created_stack.push(0);
|
self.nodes_created_stack.push(0);
|
||||||
self.instructions.push(DiffInstruction::Mount { and });
|
self.instructions.push(DiffInstruction::Mount { and });
|
||||||
|
|
||||||
self.create_node(node);
|
self.create_node(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::CreateChildren {
|
DiffInstruction::Remove { child } => {
|
||||||
progress,
|
for child in RealChildIterator::new(child, self.vdom) {
|
||||||
children,
|
self.mutations.push_root(child.direct_id());
|
||||||
and,
|
self.mutations.remove();
|
||||||
} => {
|
}
|
||||||
let and = *and;
|
|
||||||
|
|
||||||
if *progress == 0 {
|
|
||||||
self.nodes_created_stack.push(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(child) = children.get(*progress) {
|
DiffInstruction::RemoveChildren { children } => {
|
||||||
*progress += 1;
|
for child in RealChildIterator::new_from_slice(children, self.vdom) {
|
||||||
|
self.mutations.push_root(child.direct_id());
|
||||||
if *progress == children.len() {
|
self.mutations.remove();
|
||||||
self.instructions.pop();
|
|
||||||
self.instructions.push(DiffInstruction::Mount { and });
|
|
||||||
}
|
|
||||||
|
|
||||||
self.create_node(child);
|
|
||||||
} else if children.len() == 0 {
|
|
||||||
self.instructions.pop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DiffInstruction::Mount { and } => {
|
DiffInstruction::Mount { and } => {
|
||||||
|
self.mount(and);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mount(&mut self, and: MountType) {
|
||||||
let nodes_created = self.nodes_created_stack.pop().unwrap();
|
let nodes_created = self.nodes_created_stack.pop().unwrap();
|
||||||
match and {
|
match and {
|
||||||
// add the nodes from this virtual list to the parent
|
// add the nodes from this virtual list to the parent
|
||||||
|
@ -296,26 +266,22 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
*self.nodes_created_stack.last_mut().unwrap() += nodes_created;
|
*self.nodes_created_stack.last_mut().unwrap() += nodes_created;
|
||||||
}
|
}
|
||||||
MountType::Append => {
|
MountType::Append => {
|
||||||
self.edit_append_children(nodes_created as u32);
|
self.mutations.edits.push(AppendChildren {
|
||||||
|
many: nodes_created as u32,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
MountType::Replace { old } => {
|
MountType::Replace { old } => {
|
||||||
todo!()
|
todo!()
|
||||||
// self.edit_replace_with(with as u32, many as u32);
|
// self.mutations.replace_with(with as u32, many as u32);
|
||||||
}
|
}
|
||||||
MountType::InsertAfter { other_node } => {
|
MountType::InsertAfter { other_node } => {
|
||||||
self.edit_insert_after(nodes_created as u32);
|
self.mutations.insert_after(nodes_created as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
MountType::InsertBefore { other_node } => {
|
MountType::InsertBefore { other_node } => {
|
||||||
self.edit_insert_before(nodes_created as u32);
|
self.mutations.insert_before(nodes_created as u32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.instructions.pop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================
|
// =================================
|
||||||
|
@ -335,21 +301,21 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
|
|
||||||
fn create_text_node(&mut self, vtext: &'bump VText<'bump>) {
|
fn create_text_node(&mut self, vtext: &'bump VText<'bump>) {
|
||||||
let real_id = self.vdom.reserve_node();
|
let real_id = self.vdom.reserve_node();
|
||||||
self.edit_create_text_node(vtext.text, real_id);
|
self.mutations.create_text_node(vtext.text, real_id);
|
||||||
vtext.dom_id.set(Some(real_id));
|
vtext.dom_id.set(Some(real_id));
|
||||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_suspended_node(&mut self, suspended: &'bump VSuspended) {
|
fn create_suspended_node(&mut self, suspended: &'bump VSuspended) {
|
||||||
let real_id = self.vdom.reserve_node();
|
let real_id = self.vdom.reserve_node();
|
||||||
self.edit_create_placeholder(real_id);
|
self.mutations.create_placeholder(real_id);
|
||||||
suspended.node.set(Some(real_id));
|
suspended.node.set(Some(real_id));
|
||||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_anchor_node(&mut self, anchor: &'bump VAnchor) {
|
fn create_anchor_node(&mut self, anchor: &'bump VAnchor) {
|
||||||
let real_id = self.vdom.reserve_node();
|
let real_id = self.vdom.reserve_node();
|
||||||
self.edit_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.nodes_created_stack.last_mut().unwrap() += 1;
|
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||||
}
|
}
|
||||||
|
@ -366,7 +332,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
} = element;
|
} = element;
|
||||||
|
|
||||||
let real_id = self.vdom.reserve_node();
|
let real_id = self.vdom.reserve_node();
|
||||||
self.edit_create_element(tag_name, *namespace, real_id);
|
self.mutations.create_element(tag_name, *namespace, real_id);
|
||||||
|
|
||||||
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
*self.nodes_created_stack.last_mut().unwrap() += 1;
|
||||||
|
|
||||||
|
@ -377,28 +343,21 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
listeners.iter().for_each(|listener| {
|
listeners.iter().for_each(|listener| {
|
||||||
self.fix_listener(listener);
|
self.fix_listener(listener);
|
||||||
listener.mounted_node.set(Some(real_id));
|
listener.mounted_node.set(Some(real_id));
|
||||||
self.edit_new_event_listener(listener, cur_scope.clone());
|
self.mutations
|
||||||
|
.new_event_listener(listener, cur_scope.clone());
|
||||||
});
|
});
|
||||||
|
|
||||||
for attr in *attributes {
|
for attr in *attributes {
|
||||||
self.edit_set_attribute(attr);
|
self.mutations.set_attribute(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if children.len() > 0 {
|
if children.len() > 0 {
|
||||||
self.instructions.push(DiffInstruction::CreateChildren {
|
self.create_children_instructions(children, MountType::Append);
|
||||||
children,
|
|
||||||
progress: 0,
|
|
||||||
and: MountType::Append,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
|
fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
|
||||||
self.instructions.push(DiffInstruction::CreateChildren {
|
self.create_children_instructions(frag.children, MountType::Absorb);
|
||||||
children: frag.children,
|
|
||||||
progress: 0,
|
|
||||||
and: MountType::Absorb,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
|
fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
|
||||||
|
@ -417,6 +376,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
height,
|
height,
|
||||||
ScopeChildren(vcomponent.children),
|
ScopeChildren(vcomponent.children),
|
||||||
self.vdom.clone(),
|
self.vdom.clone(),
|
||||||
|
vcomponent.name,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -479,23 +439,13 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
(Component(old), Component(new)) => self.diff_component_nodes(old, new),
|
(Component(old), Component(new)) => self.diff_component_nodes(old, new),
|
||||||
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
|
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
|
||||||
(Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()),
|
(Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()),
|
||||||
|
(Suspended(old), Suspended(new)) => new.node.set(old.node.get()),
|
||||||
|
|
||||||
|
// Anything else is just a basic replace and create
|
||||||
(
|
(
|
||||||
Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_),
|
Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_) | Suspended(_),
|
||||||
Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_),
|
Component(_) | Fragment(_) | Text(_) | Element(_) | Anchor(_) | Suspended(_),
|
||||||
) => {
|
) => self.replace_and_create_many_with_many(old_node, new_node),
|
||||||
self.replace_and_create_many_with_many([old_node], [new_node]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: these don't properly clean up any data
|
|
||||||
(Suspended(old), new) => {
|
|
||||||
self.replace_and_create_many_with_many([old_node], [new_node]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// a node that was once real is now suspended
|
|
||||||
(old, Suspended(_)) => {
|
|
||||||
self.replace_and_create_many_with_many([old_node], [new_node]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,9 +453,9 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
let root = old.dom_id.get().unwrap();
|
let root = old.dom_id.get().unwrap();
|
||||||
|
|
||||||
if old.text != new.text {
|
if old.text != new.text {
|
||||||
self.edit_push_root(root);
|
self.mutations.push_root(root);
|
||||||
self.edit_set_text(new.text);
|
self.mutations.set_text(new.text);
|
||||||
self.edit_pop();
|
self.mutations.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
new.dom_id.set(Some(root));
|
new.dom_id.set(Some(root));
|
||||||
|
@ -545,17 +495,17 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
|
for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
|
||||||
if old_attr.value != new_attr.value {
|
if old_attr.value != new_attr.value {
|
||||||
please_commit(&mut self.mutations.edits);
|
please_commit(&mut self.mutations.edits);
|
||||||
self.edit_set_attribute(new_attr);
|
self.mutations.set_attribute(new_attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO: provide some sort of report on how "good" the diffing was
|
// TODO: provide some sort of report on how "good" the diffing was
|
||||||
please_commit(&mut self.mutations.edits);
|
please_commit(&mut self.mutations.edits);
|
||||||
for attribute in old.attributes {
|
for attribute in old.attributes {
|
||||||
self.edit_remove_attribute(attribute);
|
self.mutations.remove_attribute(attribute);
|
||||||
}
|
}
|
||||||
for attribute in new.attributes {
|
for attribute in new.attributes {
|
||||||
self.edit_set_attribute(attribute)
|
self.mutations.set_attribute(attribute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,8 +522,8 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
|
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
|
||||||
if old_l.event != new_l.event {
|
if old_l.event != new_l.event {
|
||||||
please_commit(&mut self.mutations.edits);
|
please_commit(&mut self.mutations.edits);
|
||||||
self.edit_remove_event_listener(old_l.event);
|
self.mutations.remove_event_listener(old_l.event);
|
||||||
self.edit_new_event_listener(new_l, cur_scope);
|
self.mutations.new_event_listener(new_l, cur_scope);
|
||||||
}
|
}
|
||||||
new_l.mounted_node.set(old_l.mounted_node.get());
|
new_l.mounted_node.set(old_l.mounted_node.get());
|
||||||
self.fix_listener(new_l);
|
self.fix_listener(new_l);
|
||||||
|
@ -581,11 +531,11 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
} else {
|
} else {
|
||||||
please_commit(&mut self.mutations.edits);
|
please_commit(&mut self.mutations.edits);
|
||||||
for listener in old.listeners {
|
for listener in old.listeners {
|
||||||
self.edit_remove_event_listener(listener.event);
|
self.mutations.remove_event_listener(listener.event);
|
||||||
}
|
}
|
||||||
for listener in new.listeners {
|
for listener in new.listeners {
|
||||||
listener.mounted_node.set(Some(root));
|
listener.mounted_node.set(Some(root));
|
||||||
self.edit_new_event_listener(listener, cur_scope);
|
self.mutations.new_event_listener(listener, cur_scope);
|
||||||
|
|
||||||
// Make sure the listener gets attached to the scope list
|
// Make sure the listener gets attached to the scope list
|
||||||
self.fix_listener(listener);
|
self.fix_listener(listener);
|
||||||
|
@ -593,7 +543,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_comitted {
|
if has_comitted {
|
||||||
self.edit_pop();
|
self.mutations.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.diff_children(old.children, new.children);
|
self.diff_children(old.children, new.children);
|
||||||
|
@ -646,8 +596,8 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
|
|
||||||
// // remove any leftovers
|
// // remove any leftovers
|
||||||
// for to_remove in old_iter {
|
// for to_remove in old_iter {
|
||||||
// self.edit_push_root(to_remove.direct_id());
|
// self.mutations.push_root(to_remove.direct_id());
|
||||||
// self.edit_remove();
|
// self.mutations.remove();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // seems like we could combine this into a single instruction....
|
// // seems like we could combine this into a single instruction....
|
||||||
|
@ -670,6 +620,26 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
self.diff_children(old.children, new.children);
|
self.diff_children(old.children, new.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// Utilites for creating new diff instructions
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
fn create_children_instructions(
|
||||||
|
&mut self,
|
||||||
|
children: &'bump [VNode<'bump>],
|
||||||
|
and: MountType<'bump>,
|
||||||
|
) {
|
||||||
|
self.nodes_created_stack.push(0);
|
||||||
|
self.instructions.push(DiffInstruction::Mount { and });
|
||||||
|
|
||||||
|
for child in children.into_iter().rev() {
|
||||||
|
self.instructions.push(DiffInstruction::Create {
|
||||||
|
and: MountType::Absorb,
|
||||||
|
node: child,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Destroy a scope and all of its descendents.
|
/// Destroy a scope and all of its descendents.
|
||||||
///
|
///
|
||||||
/// Calling this will run the destuctors on all hooks in the tree.
|
/// Calling this will run the destuctors on all hooks in the tree.
|
||||||
|
@ -727,11 +697,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
|
|
||||||
// Completely adding new nodes, removing any placeholder if it exists
|
// Completely adding new nodes, removing any placeholder if it exists
|
||||||
(IS_EMPTY, IS_NOT_EMPTY) => {
|
(IS_EMPTY, IS_NOT_EMPTY) => {
|
||||||
self.instructions.push(DiffInstruction::CreateChildren {
|
self.create_children_instructions(new, MountType::Append);
|
||||||
children: new,
|
|
||||||
progress: 0,
|
|
||||||
and: MountType::Append,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Completely removing old nodes and putting an anchor in its place
|
// Completely removing old nodes and putting an anchor in its place
|
||||||
|
@ -755,11 +721,10 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
|
|
||||||
// Replace the anchor with whatever new nodes are coming down the pipe
|
// Replace the anchor with whatever new nodes are coming down the pipe
|
||||||
(VNode::Anchor(anchor), _) => {
|
(VNode::Anchor(anchor), _) => {
|
||||||
self.instructions.push(DiffInstruction::CreateChildren {
|
self.create_children_instructions(
|
||||||
children: new,
|
new,
|
||||||
progress: 0,
|
MountType::Replace { old: first_old },
|
||||||
and: MountType::Replace { old: first_old },
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace whatever nodes are sitting there with the anchor
|
// Replace whatever nodes are sitting there with the anchor
|
||||||
|
@ -905,13 +870,13 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
if shared_prefix_count == old.len() {
|
if shared_prefix_count == old.len() {
|
||||||
// Load the last element
|
// Load the last element
|
||||||
let last_node = self.find_last_element(new.last().unwrap()).direct_id();
|
let last_node = self.find_last_element(new.last().unwrap()).direct_id();
|
||||||
self.edit_push_root(last_node);
|
self.mutations.push_root(last_node);
|
||||||
|
|
||||||
// Create the new children and insert them after
|
// Create the new children and insert them after
|
||||||
//
|
//
|
||||||
todo!();
|
todo!();
|
||||||
// let meta = self.create_children(&new[shared_prefix_count..]);
|
// let meta = self.create_children(&new[shared_prefix_count..]);
|
||||||
// self.edit_insert_after(meta.added_to_stack);
|
// self.mutations.insert_after(meta.added_to_stack);
|
||||||
|
|
||||||
return KeyedPrefixResult::Finished;
|
return KeyedPrefixResult::Finished;
|
||||||
}
|
}
|
||||||
|
@ -937,7 +902,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
for child in new {
|
for child in new {
|
||||||
todo!();
|
todo!();
|
||||||
// let meta = self.create_vnode(child);
|
// let meta = self.create_vnode(child);
|
||||||
// self.edit_append_children(meta.added_to_stack);
|
// self.mutations.append_children(meta.added_to_stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1077,7 +1042,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
root = Some(new_anchor);
|
root = Some(new_anchor);
|
||||||
|
|
||||||
// let anchor_el = self.find_first_element(new_anchor);
|
// let anchor_el = self.find_first_element(new_anchor);
|
||||||
// self.edit_push_root(anchor_el.direct_id());
|
// self.mutations.push_root(anchor_el.direct_id());
|
||||||
// // let mut pushed = false;
|
// // let mut pushed = false;
|
||||||
|
|
||||||
'inner: loop {
|
'inner: loop {
|
||||||
|
@ -1100,27 +1065,27 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
// now move all the nodes into the right spot
|
// now move all the nodes into the right spot
|
||||||
for child in RealChildIterator::new(next_new, self.vdom) {
|
for child in RealChildIterator::new(next_new, self.vdom) {
|
||||||
let el = child.direct_id();
|
let el = child.direct_id();
|
||||||
self.edit_push_root(el);
|
self.mutations.push_root(el);
|
||||||
self.edit_insert_before(1);
|
self.mutations.insert_before(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.instructions.push(DiffInstruction::Create {
|
self.instructions.push(DiffInstruction::Create {
|
||||||
node: next_new,
|
node: next_new,
|
||||||
and: MountType::InsertBefore {
|
and: MountType::InsertBefore {
|
||||||
other_node: new_anchor,
|
other_node: Some(new_anchor),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.edit_pop();
|
self.mutations.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
let final_lis_node = root.unwrap();
|
let final_lis_node = root.unwrap();
|
||||||
let final_el_node = self.find_last_element(final_lis_node);
|
let final_el_node = self.find_last_element(final_lis_node);
|
||||||
let final_el = final_el_node.direct_id();
|
let final_el = final_el_node.direct_id();
|
||||||
self.edit_push_root(final_el);
|
self.mutations.push_root(final_el);
|
||||||
|
|
||||||
let mut last_iter = new.iter().rev().enumerate();
|
let mut last_iter = new.iter().rev().enumerate();
|
||||||
let last_key = final_lis_node.key().unwrap();
|
let last_key = final_lis_node.key().unwrap();
|
||||||
|
@ -1145,8 +1110,8 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
// now move all the nodes into the right spot
|
// now move all the nodes into the right spot
|
||||||
for child in RealChildIterator::new(last_node, self.vdom) {
|
for child in RealChildIterator::new(last_node, self.vdom) {
|
||||||
let el = child.direct_id();
|
let el = child.direct_id();
|
||||||
self.edit_push_root(el);
|
self.mutations.push_root(el);
|
||||||
self.edit_insert_after(1);
|
self.mutations.insert_after(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("key is not contained {:?}", key);
|
eprintln!("key is not contained {:?}", key);
|
||||||
|
@ -1154,10 +1119,10 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
// insert it before the current milestone
|
// insert it before the current milestone
|
||||||
todo!();
|
todo!();
|
||||||
// let meta = self.create_vnode(last_node);
|
// let meta = self.create_vnode(last_node);
|
||||||
// self.edit_insert_after(meta.added_to_stack);
|
// self.mutations.insert_after(meta.added_to_stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.edit_pop();
|
self.mutations.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diff the suffix of keyed children that share the same keys in the same order.
|
// Diff the suffix of keyed children that share the same keys in the same order.
|
||||||
|
@ -1198,19 +1163,17 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
match old.len().cmp(&new.len()) {
|
match old.len().cmp(&new.len()) {
|
||||||
// old.len > new.len -> removing some nodes
|
// old.len > new.len -> removing some nodes
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
// diff them together
|
// Generate instructions to diff the existing elements
|
||||||
for (new_child, old_child) in new.iter().zip(old.iter()) {
|
for (new_child, old_child) in new.iter().zip(old.iter()).rev() {
|
||||||
self.diff_node(old_child, new_child);
|
self.instructions.push(DiffInstruction::DiffNode {
|
||||||
|
new: new_child,
|
||||||
|
old: old_child,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: we would emit fewer instructions if we just did a replace many
|
self.instructions.push(DiffInstruction::RemoveChildren {
|
||||||
// remove whatever is still dangling
|
children: &old[new.len()..],
|
||||||
for item in &old[new.len()..] {
|
});
|
||||||
for i in RealChildIterator::new(item, self.vdom) {
|
|
||||||
self.edit_push_root(i.direct_id());
|
|
||||||
self.edit_remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// old.len < new.len -> adding some nodes
|
// old.len < new.len -> adding some nodes
|
||||||
|
@ -1218,27 +1181,30 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
//
|
//
|
||||||
// we need to save the last old element and then replace it with all the new ones
|
// we need to save the last old element and then replace it with all the new ones
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
// Add the new elements to the last old element while it still exists
|
// Generate instructions to diff the existing elements
|
||||||
let last = self.find_last_element(old.last().unwrap());
|
for (new_child, old_child) in new.iter().zip(old.iter()).rev() {
|
||||||
self.edit_push_root(last.direct_id());
|
self.instructions.push(DiffInstruction::DiffNode {
|
||||||
|
new: new_child,
|
||||||
// create the rest and insert them
|
old: old_child,
|
||||||
todo!();
|
});
|
||||||
// let meta = self.create_children(&new[old.len()..]);
|
|
||||||
// self.edit_insert_after(meta.added_to_stack);
|
|
||||||
|
|
||||||
self.edit_pop();
|
|
||||||
|
|
||||||
// diff the rest
|
|
||||||
for (new_child, old_child) in new.iter().zip(old.iter()) {
|
|
||||||
self.diff_node(old_child, new_child)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate instructions to add in the new elements
|
||||||
|
self.create_children_instructions(
|
||||||
|
&new[old.len()..],
|
||||||
|
MountType::InsertAfter {
|
||||||
|
other_node: old.last(),
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// old.len == new.len -> no nodes added/removed, but perhaps changed
|
// old.len == new.len -> no nodes added/removed, but perhaps changed
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
for (new_child, old_child) in new.iter().zip(old.iter()) {
|
for (new_child, old_child) in new.iter().zip(old.iter()).rev() {
|
||||||
self.diff_node(old_child, new_child);
|
self.instructions.push(DiffInstruction::DiffNode {
|
||||||
|
new: new_child,
|
||||||
|
old: old_child,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1247,24 +1213,24 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
// ======================
|
// ======================
|
||||||
// Support methods
|
// Support methods
|
||||||
// ======================
|
// ======================
|
||||||
// Remove all of a node's children.
|
// // Remove all of a node's children.
|
||||||
//
|
// //
|
||||||
// The change list stack must have this shape upon entry to this function:
|
// // The change list stack must have this shape upon entry to this function:
|
||||||
//
|
// //
|
||||||
// [... parent]
|
// // [... parent]
|
||||||
//
|
// //
|
||||||
// When this function returns, the change list stack is in the same state.
|
// // When this function returns, the change list stack is in the same state.
|
||||||
fn remove_all_children(&mut self, old: &'bump [VNode<'bump>]) {
|
// fn remove_all_children(&mut self, old: &'bump [VNode<'bump>]) {
|
||||||
// debug_assert!(self.traversal_is_committed());
|
// // debug_assert!(self.traversal_is_committed());
|
||||||
log::debug!("REMOVING CHILDREN");
|
// log::debug!("REMOVING CHILDREN");
|
||||||
for _child in old {
|
// for _child in old {
|
||||||
// registry.remove_subtree(child);
|
// // registry.remove_subtree(child);
|
||||||
}
|
// }
|
||||||
// Fast way to remove all children: set the node's textContent to an empty
|
// // Fast way to remove all children: set the node's textContent to an empty
|
||||||
// string.
|
// // string.
|
||||||
todo!()
|
// todo!()
|
||||||
// self.set_inner_text("");
|
// // self.set_inner_text("");
|
||||||
}
|
// }
|
||||||
// Remove the current child and all of its following siblings.
|
// Remove the current child and all of its following siblings.
|
||||||
//
|
//
|
||||||
// The change list stack must have this shape upon entry to this function:
|
// The change list stack must have this shape upon entry to this function:
|
||||||
|
@ -1324,8 +1290,16 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_child(&mut self, node: &'bump VNode<'bump>) {
|
// fn remove_child(&mut self, node: &'bump VNode<'bump>) {
|
||||||
self.replace_and_create_many_with_many(Some(node), None);
|
// self.replace_and_create_many_with_many(Some(node), None);
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn replace_many_with_many(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_one_with_many(&mut self, old: &'bump VNode<'bump>, new: &'bump [VNode<'bump>]) {
|
||||||
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove all the old nodes and replace them with newly created new nodes.
|
/// Remove all the old nodes and replace them with newly created new nodes.
|
||||||
|
@ -1333,11 +1307,13 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
/// The new nodes *will* be created - don't create them yourself!
|
/// The new nodes *will* be created - don't create them yourself!
|
||||||
fn replace_and_create_many_with_many(
|
fn replace_and_create_many_with_many(
|
||||||
&mut self,
|
&mut self,
|
||||||
old_nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
|
old_node: &'bump VNode<'bump>,
|
||||||
new_nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
|
new_node: &'bump VNode<'bump>,
|
||||||
|
// old_nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
|
||||||
|
// new_nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
|
||||||
) {
|
) {
|
||||||
let mut nodes_to_replace = Vec::new();
|
let mut nodes_to_replace = Vec::new();
|
||||||
let mut nodes_to_search = old_nodes.into_iter().collect::<Vec<_>>();
|
let mut nodes_to_search = vec![old_node];
|
||||||
let mut scopes_obliterated = Vec::new();
|
let mut scopes_obliterated = Vec::new();
|
||||||
while let Some(node) = nodes_to_search.pop() {
|
while let Some(node) = nodes_to_search.pop() {
|
||||||
match &node {
|
match &node {
|
||||||
|
@ -1368,20 +1344,25 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
// self.create_garbage(node);
|
// self.create_garbage(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = nodes_to_replace.len();
|
// let n = nodes_to_replace.len();
|
||||||
for node in nodes_to_replace {
|
// for node in nodes_to_replace {
|
||||||
self.edit_push_root(node);
|
// self.mutations.push_root(node);
|
||||||
}
|
// }
|
||||||
|
|
||||||
let mut nodes_created = 0;
|
// let mut nodes_created = 0;
|
||||||
for node in new_nodes {
|
// for node in new_nodes {
|
||||||
todo!();
|
// todo!();
|
||||||
// let meta = self.create_vnode(node);
|
// let meta = self.create_vnode(node);
|
||||||
// nodes_created += meta.added_to_stack;
|
// nodes_created += meta.added_to_stack;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// if 0 nodes are created, then it gets interperted as a deletion
|
// if 0 nodes are created, then it gets interperted as a deletion
|
||||||
self.edit_replace_with(n as u32, nodes_created);
|
// self.mutations.replace_with(n as u32, nodes_created);
|
||||||
|
|
||||||
|
// self.instructions.push(DiffInstruction::CreateChildren {
|
||||||
|
// and: MountType::Replace { old: None },
|
||||||
|
// children:
|
||||||
|
// });
|
||||||
|
|
||||||
// obliterate!
|
// obliterate!
|
||||||
for scope in scopes_obliterated {
|
for scope in scopes_obliterated {
|
||||||
|
@ -1411,12 +1392,12 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
old_node: &'bump VNode<'bump>,
|
old_node: &'bump VNode<'bump>,
|
||||||
new_node: &'bump VNode<'bump>,
|
new_node: &'bump VNode<'bump>,
|
||||||
) {
|
) {
|
||||||
self.edit_push_root(anchor);
|
self.mutations.push_root(anchor);
|
||||||
todo!();
|
todo!();
|
||||||
// let meta = self.create_vnode(new_node);
|
// let meta = self.create_vnode(new_node);
|
||||||
// self.edit_replace_with(1, meta.added_to_stack);
|
// self.mutations.replace_with(1, meta.added_to_stack);
|
||||||
// self.create_garbage(old_node);
|
// self.create_garbage(old_node);
|
||||||
self.edit_pop();
|
self.mutations.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_vnode(&mut self, node: &'bump VNode<'bump>) {
|
fn remove_vnode(&mut self, node: &'bump VNode<'bump>) {
|
||||||
|
@ -1470,138 +1451,6 @@ impl<'bump> DiffMachine<'bump> {
|
||||||
// if we have, then we're trying to alias it, which is not allowed
|
// if we have, then we're trying to alias it, which is not allowed
|
||||||
unsafe { self.vdom.get_scope(*id) }
|
unsafe { self.vdom.get_scope(*id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
|
||||||
pub(crate) fn edit_push_root(&mut self, root: ElementId) {
|
|
||||||
let id = root.as_u64();
|
|
||||||
self.mutations.edits.push(PushRoot { id });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn edit_pop(&mut self) {
|
|
||||||
self.mutations.edits.push(PopRoot {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add Nodes to the dom
|
|
||||||
// add m nodes from the stack
|
|
||||||
pub(crate) fn edit_append_children(&mut self, many: u32) {
|
|
||||||
self.mutations.edits.push(AppendChildren { many });
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace the n-m node on the stack with the m nodes
|
|
||||||
// ends with the last element of the chain on the top of the stack
|
|
||||||
pub(crate) fn edit_replace_with(&mut self, n: u32, m: u32) {
|
|
||||||
self.mutations.edits.push(ReplaceWith { n, m });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn edit_insert_after(&mut self, n: u32) {
|
|
||||||
self.mutations.edits.push(InsertAfter { n });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn edit_insert_before(&mut self, n: u32) {
|
|
||||||
self.mutations.edits.push(InsertBefore { n });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove Nodesfrom the dom
|
|
||||||
pub(crate) fn edit_remove(&mut self) {
|
|
||||||
self.mutations.edits.push(Remove);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create
|
|
||||||
pub(crate) fn edit_create_text_node(&mut self, text: &'bump str, id: ElementId) {
|
|
||||||
let id = id.as_u64();
|
|
||||||
self.mutations.edits.push(CreateTextNode { text, id });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn edit_create_element(
|
|
||||||
&mut self,
|
|
||||||
tag: &'static str,
|
|
||||||
ns: Option<&'static str>,
|
|
||||||
id: ElementId,
|
|
||||||
) {
|
|
||||||
let id = id.as_u64();
|
|
||||||
match ns {
|
|
||||||
Some(ns) => self.mutations.edits.push(CreateElementNs { id, ns, tag }),
|
|
||||||
None => self.mutations.edits.push(CreateElement { id, tag }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
|
|
||||||
pub(crate) fn edit_create_placeholder(&mut self, id: ElementId) {
|
|
||||||
let id = id.as_u64();
|
|
||||||
self.mutations.edits.push(CreatePlaceholder { id });
|
|
||||||
}
|
|
||||||
|
|
||||||
// events
|
|
||||||
pub(crate) fn edit_new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
|
|
||||||
let Listener {
|
|
||||||
event,
|
|
||||||
mounted_node,
|
|
||||||
..
|
|
||||||
} = listener;
|
|
||||||
|
|
||||||
let element_id = mounted_node.get().unwrap().as_u64();
|
|
||||||
|
|
||||||
self.mutations.edits.push(NewEventListener {
|
|
||||||
scope,
|
|
||||||
event_name: event,
|
|
||||||
mounted_node_id: element_id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn edit_remove_event_listener(&mut self, event: &'static str) {
|
|
||||||
self.mutations.edits.push(RemoveEventListener { event });
|
|
||||||
}
|
|
||||||
|
|
||||||
// modify
|
|
||||||
pub(crate) fn edit_set_text(&mut self, text: &'bump str) {
|
|
||||||
self.mutations.edits.push(SetText { text });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn edit_set_attribute(&mut self, attribute: &'bump Attribute) {
|
|
||||||
let Attribute {
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
is_static,
|
|
||||||
is_volatile,
|
|
||||||
namespace,
|
|
||||||
} = attribute;
|
|
||||||
// field: &'static str,
|
|
||||||
// value: &'bump str,
|
|
||||||
// ns: Option<&'static str>,
|
|
||||||
self.mutations.edits.push(SetAttribute {
|
|
||||||
field: name,
|
|
||||||
value,
|
|
||||||
ns: *namespace,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn edit_set_attribute_ns(
|
|
||||||
&mut self,
|
|
||||||
attribute: &'bump Attribute,
|
|
||||||
namespace: &'bump str,
|
|
||||||
) {
|
|
||||||
let Attribute {
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
is_static,
|
|
||||||
is_volatile,
|
|
||||||
// namespace,
|
|
||||||
..
|
|
||||||
} = attribute;
|
|
||||||
// field: &'static str,
|
|
||||||
// value: &'bump str,
|
|
||||||
// ns: Option<&'static str>,
|
|
||||||
self.mutations.edits.push(SetAttribute {
|
|
||||||
field: name,
|
|
||||||
value,
|
|
||||||
ns: Some(namespace),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn edit_remove_attribute(&mut self, attribute: &Attribute) {
|
|
||||||
let name = attribute.name;
|
|
||||||
self.mutations.edits.push(RemoveAttribute { name });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we create new nodes, we need to propagate some information back up the call chain.
|
// When we create new nodes, we need to propagate some information back up the call chain.
|
||||||
|
@ -1664,6 +1513,14 @@ impl<'a> RealChildIterator<'a> {
|
||||||
stack: smallvec::smallvec![(0, starter)],
|
stack: smallvec::smallvec![(0, starter)],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_from_slice(nodes: &'a [VNode<'a>], scopes: &'a SharedResources) -> Self {
|
||||||
|
let mut stack = smallvec::smallvec![];
|
||||||
|
for node in nodes {
|
||||||
|
stack.push((0, node));
|
||||||
|
}
|
||||||
|
Self { scopes, stack }
|
||||||
|
}
|
||||||
// keep the memory around
|
// keep the memory around
|
||||||
pub fn reset_with(&mut self, node: &'a VNode<'a>) {
|
pub fn reset_with(&mut self, node: &'a VNode<'a>) {
|
||||||
self.stack.clear();
|
self.stack.clear();
|
||||||
|
@ -1774,16 +1631,3 @@ fn compare_strs(a: &str, b: &str) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DfsIterator<'a> {
|
|
||||||
idx: usize,
|
|
||||||
node: Option<(&'a VNode<'a>, &'a VNode<'a>)>,
|
|
||||||
nodes: Option<(&'a [VNode<'a>], &'a [VNode<'a>])>,
|
|
||||||
}
|
|
||||||
impl<'a> Iterator for DfsIterator<'a> {
|
|
||||||
type Item = (&'a VNode<'a>, &'a VNode<'a>);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::innerlude::ScopeId;
|
||||||
|
|
||||||
/// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the
|
/// A `DomEdit` represents a serialzied form of the VirtualDom's trait-based API. This allows streaming edits across the
|
||||||
/// network or through FFI boundaries.
|
/// network or through FFI boundaries.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serialize",
|
feature = "serialize",
|
||||||
derive(serde::Serialize, serde::Deserialize),
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::{Cell, UnsafeCell},
|
cell::{Cell, RefCell, UnsafeCell},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct HookList {
|
pub struct HookList {
|
||||||
vals: appendlist::AppendList<InnerHook<Box<dyn Any>>>,
|
vals: RefCell<Vec<InnerHook<Box<dyn Any>>>>,
|
||||||
idx: Cell<usize>,
|
idx: Cell<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,35 +27,17 @@ impl<T> InnerHook<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HookList {
|
impl HookList {
|
||||||
/// Unsafely get a mutable reference to any of the hooks
|
|
||||||
///
|
|
||||||
/// This is unsafe because an &mut T might be aliased if the hook data is already borrowed/in use in the component
|
|
||||||
///
|
|
||||||
/// This method should be reserved for internal methods that are guaranteed that this hook is not aliased anyhwere
|
|
||||||
/// inside the component body, or outside into children components.
|
|
||||||
///
|
|
||||||
/// This method is currently used only by the suspense system whose hook implementation guarantees that all &T is dropped
|
|
||||||
/// before the suspense handler is ran.
|
|
||||||
pub(crate) unsafe fn get_mut<T: 'static>(&self, idx: usize) -> Option<&mut T> {
|
|
||||||
self.vals.get(idx).and_then(|inn| {
|
|
||||||
let raw_box = unsafe { &mut *inn.cell.get() };
|
|
||||||
raw_box.downcast_mut::<T>()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
|
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
|
||||||
self.vals.get(self.idx.get()).and_then(|inn| {
|
self.vals.borrow().get(self.idx.get()).and_then(|inn| {
|
||||||
self.idx.set(self.idx.get() + 1);
|
self.idx.set(self.idx.get() + 1);
|
||||||
let raw_box = unsafe { &mut *inn.cell.get() };
|
let raw_box = unsafe { &mut *inn.cell.get() };
|
||||||
raw_box.downcast_mut::<T>()
|
raw_box.downcast_mut::<T>()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn push<T: 'static>(&self, new: T) {
|
pub(crate) fn push<T: 'static>(&self, new: T) {
|
||||||
self.vals.push(InnerHook::new(Box::new(new)))
|
self.vals.borrow_mut().push(InnerHook::new(Box::new(new)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This resets the internal iterator count
|
/// This resets the internal iterator count
|
||||||
|
@ -70,7 +52,7 @@ impl HookList {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn len(&self) -> usize {
|
pub(crate) fn len(&self) -> usize {
|
||||||
self.vals.len()
|
self.vals.borrow().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -12,15 +12,15 @@
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority,
|
format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority,
|
||||||
EventTrigger, LazyNodes, NodeFactory, Properties, ScopeId, SuspendedContext, VNode, VirtualDom,
|
EventTrigger, LazyNodes, Mutations, NodeFactory, Properties, ScopeId, SuspendedContext, VNode,
|
||||||
VirtualEvent, FC,
|
VirtualDom, VirtualEvent, FC,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::component::{fc_to_builder, Fragment, Properties};
|
pub use crate::component::{fc_to_builder, Fragment, Properties};
|
||||||
pub use crate::context::Context;
|
pub use crate::context::Context;
|
||||||
pub use crate::hooks::*;
|
pub use crate::hooks::*;
|
||||||
pub use crate::innerlude::{DioxusElement, DomTree, LazyNodes, NodeFactory, FC};
|
pub use crate::innerlude::{DioxusElement, DomTree, LazyNodes, Mutations, NodeFactory, FC};
|
||||||
pub use crate::nodes::VNode;
|
pub use crate::nodes::VNode;
|
||||||
pub use crate::VirtualDom;
|
pub use crate::VirtualDom;
|
||||||
pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
|
pub use dioxus_core_macro::{format_args_f, html, rsx, Props};
|
||||||
|
@ -39,6 +39,7 @@ pub(crate) mod innerlude {
|
||||||
pub use crate::heuristics::*;
|
pub use crate::heuristics::*;
|
||||||
pub use crate::hooklist::*;
|
pub use crate::hooklist::*;
|
||||||
pub use crate::hooks::*;
|
pub use crate::hooks::*;
|
||||||
|
pub use crate::mutations::*;
|
||||||
pub use crate::nodes::*;
|
pub use crate::nodes::*;
|
||||||
pub use crate::scheduler::*;
|
pub use crate::scheduler::*;
|
||||||
pub use crate::scope::*;
|
pub use crate::scope::*;
|
||||||
|
@ -68,6 +69,7 @@ pub mod events;
|
||||||
pub mod heuristics;
|
pub mod heuristics;
|
||||||
pub mod hooklist;
|
pub mod hooklist;
|
||||||
pub mod hooks;
|
pub mod hooks;
|
||||||
|
pub mod mutations;
|
||||||
pub mod nodes;
|
pub mod nodes;
|
||||||
pub mod scheduler;
|
pub mod scheduler;
|
||||||
pub mod scope;
|
pub mod scope;
|
||||||
|
|
154
packages/core/src/mutations.rs
Normal file
154
packages/core/src/mutations.rs
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
use crate::innerlude::*;
|
||||||
|
|
||||||
|
/// The "Mutations" object holds the changes that need to be made to the DOM.
|
||||||
|
///
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Mutations<'s> {
|
||||||
|
pub edits: Vec<DomEdit<'s>>,
|
||||||
|
pub noderefs: Vec<NodeRefMutation<'s>>,
|
||||||
|
}
|
||||||
|
use DomEdit::*;
|
||||||
|
|
||||||
|
impl<'bump> Mutations<'bump> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let edits = Vec::new();
|
||||||
|
let noderefs = Vec::new();
|
||||||
|
Self { edits, noderefs }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend(&mut self, other: &mut Mutations) {}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
pub(crate) fn push_root(&mut self, root: ElementId) {
|
||||||
|
let id = root.as_u64();
|
||||||
|
self.edits.push(PushRoot { id });
|
||||||
|
}
|
||||||
|
pub(crate) fn pop(&mut self) {
|
||||||
|
self.edits.push(PopRoot {});
|
||||||
|
}
|
||||||
|
// replace the n-m node on the stack with the m nodes
|
||||||
|
// ends with the last element of the chain on the top of the stack
|
||||||
|
pub(crate) fn replace_with(&mut self, n: u32, m: u32) {
|
||||||
|
self.edits.push(ReplaceWith { n, m });
|
||||||
|
}
|
||||||
|
pub(crate) fn insert_after(&mut self, n: u32) {
|
||||||
|
self.edits.push(InsertAfter { n });
|
||||||
|
}
|
||||||
|
pub(crate) fn insert_before(&mut self, n: u32) {
|
||||||
|
self.edits.push(InsertBefore { n });
|
||||||
|
}
|
||||||
|
// Remove Nodesfrom the dom
|
||||||
|
pub(crate) fn remove(&mut self) {
|
||||||
|
self.edits.push(Remove);
|
||||||
|
}
|
||||||
|
// Create
|
||||||
|
pub(crate) fn create_text_node(&mut self, text: &'bump str, id: ElementId) {
|
||||||
|
let id = id.as_u64();
|
||||||
|
self.edits.push(CreateTextNode { text, id });
|
||||||
|
}
|
||||||
|
pub(crate) fn create_element(
|
||||||
|
&mut self,
|
||||||
|
tag: &'static str,
|
||||||
|
ns: Option<&'static str>,
|
||||||
|
id: ElementId,
|
||||||
|
) {
|
||||||
|
let id = id.as_u64();
|
||||||
|
match ns {
|
||||||
|
Some(ns) => self.edits.push(CreateElementNs { id, ns, tag }),
|
||||||
|
None => self.edits.push(CreateElement { id, tag }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
|
||||||
|
pub(crate) fn create_placeholder(&mut self, id: ElementId) {
|
||||||
|
let id = id.as_u64();
|
||||||
|
self.edits.push(CreatePlaceholder { id });
|
||||||
|
}
|
||||||
|
// events
|
||||||
|
pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
|
||||||
|
let Listener {
|
||||||
|
event,
|
||||||
|
mounted_node,
|
||||||
|
..
|
||||||
|
} = listener;
|
||||||
|
|
||||||
|
let element_id = mounted_node.get().unwrap().as_u64();
|
||||||
|
|
||||||
|
self.edits.push(NewEventListener {
|
||||||
|
scope,
|
||||||
|
event_name: event,
|
||||||
|
mounted_node_id: element_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub(crate) fn remove_event_listener(&mut self, event: &'static str) {
|
||||||
|
self.edits.push(RemoveEventListener { event });
|
||||||
|
}
|
||||||
|
// modify
|
||||||
|
pub(crate) fn set_text(&mut self, text: &'bump str) {
|
||||||
|
self.edits.push(SetText { text });
|
||||||
|
}
|
||||||
|
pub(crate) fn set_attribute(&mut self, attribute: &'bump Attribute) {
|
||||||
|
let Attribute {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
is_static,
|
||||||
|
is_volatile,
|
||||||
|
namespace,
|
||||||
|
} = attribute;
|
||||||
|
|
||||||
|
self.edits.push(SetAttribute {
|
||||||
|
field: name,
|
||||||
|
value,
|
||||||
|
ns: *namespace,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub(crate) fn set_attribute_ns(&mut self, attribute: &'bump Attribute, namespace: &'bump str) {
|
||||||
|
let Attribute {
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
is_static,
|
||||||
|
is_volatile,
|
||||||
|
..
|
||||||
|
} = attribute;
|
||||||
|
|
||||||
|
self.edits.push(SetAttribute {
|
||||||
|
field: name,
|
||||||
|
value,
|
||||||
|
ns: Some(namespace),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub(crate) fn remove_attribute(&mut self, attribute: &Attribute) {
|
||||||
|
let name = attribute.name;
|
||||||
|
self.edits.push(RemoveAttribute { name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// refs are only assigned once
|
||||||
|
pub struct NodeRefMutation<'a> {
|
||||||
|
element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
|
||||||
|
element_id: ElementId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("NodeRefMutation")
|
||||||
|
.field("element_id", &self.element_id)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NodeRefMutation<'a> {
|
||||||
|
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
|
||||||
|
self.element
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|f| f.get())
|
||||||
|
.and_then(|f| f.downcast_ref::<T>())
|
||||||
|
}
|
||||||
|
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
|
||||||
|
self.element
|
||||||
|
.as_mut()
|
||||||
|
.and_then(|f| f.get_mut())
|
||||||
|
.and_then(|f| f.downcast_mut::<T>())
|
||||||
|
}
|
||||||
|
}
|
|
@ -164,6 +164,8 @@ pub struct VComponent<'src> {
|
||||||
|
|
||||||
pub can_memoize: bool,
|
pub can_memoize: bool,
|
||||||
|
|
||||||
|
pub name: &'static str,
|
||||||
|
|
||||||
// a pointer into the bump arena (given by the 'src lifetime)
|
// a pointer into the bump arena (given by the 'src lifetime)
|
||||||
pub(crate) raw_props: *const (),
|
pub(crate) raw_props: *const (),
|
||||||
|
|
||||||
|
@ -361,6 +363,8 @@ impl<'a> NodeFactory<'a> {
|
||||||
{
|
{
|
||||||
let bump = self.bump();
|
let bump = self.bump();
|
||||||
|
|
||||||
|
let name = crate::util::type_name_of(component);
|
||||||
|
|
||||||
// We don't want the fat part of the fat pointer
|
// We don't want the fat part of the fat pointer
|
||||||
// This function does static dispatch so we don't need any VTable stuff
|
// This function does static dispatch so we don't need any VTable stuff
|
||||||
let children: &'a V = bump.alloc(children);
|
let children: &'a V = bump.alloc(children);
|
||||||
|
@ -426,6 +430,7 @@ impl<'a> NodeFactory<'a> {
|
||||||
can_memoize: P::IS_STATIC,
|
can_memoize: P::IS_STATIC,
|
||||||
ass_scope: Cell::new(None),
|
ass_scope: Cell::new(None),
|
||||||
key,
|
key,
|
||||||
|
name,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,6 +459,25 @@ impl<'a> NodeFactory<'a> {
|
||||||
pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> {
|
pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> {
|
||||||
let children = node_iter.into_vnode_list(self);
|
let children = node_iter.into_vnode_list(self);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
|
||||||
|
//
|
||||||
|
// if cfg!(debug_assertions) {
|
||||||
|
// if children.len() > 1 {
|
||||||
|
// if children.last().unwrap().key().is_none() {
|
||||||
|
// log::error!(
|
||||||
|
// r#"
|
||||||
|
// Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||||
|
// Not providing a key will lead to poor performance with lists.
|
||||||
|
// See docs.rs/dioxus for more information.
|
||||||
|
// ---
|
||||||
|
// To help you identify where this error is coming from, we've generated a backtrace.
|
||||||
|
// "#,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
VNode::Fragment(VFragment {
|
VNode::Fragment(VFragment {
|
||||||
children,
|
children,
|
||||||
key: None,
|
key: None,
|
||||||
|
@ -497,22 +521,6 @@ where
|
||||||
nodes.push(node.into_vnode(cx));
|
nodes.push(node.into_vnode(cx));
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
if nodes.len() > 1 {
|
|
||||||
if nodes.last().unwrap().key().is_none() {
|
|
||||||
log::error!(
|
|
||||||
r#"
|
|
||||||
Warning: Each child in an array or iterator should have a unique "key" prop.
|
|
||||||
Not providing a key will lead to poor performance with lists.
|
|
||||||
See docs.rs/dioxus for more information.
|
|
||||||
---
|
|
||||||
To help you identify where this error is coming from, we've generated a backtrace.
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nodes.len() == 0 {
|
if nodes.len() == 0 {
|
||||||
nodes.push(VNode::Anchor(VAnchor {
|
nodes.push(VNode::Anchor(VAnchor {
|
||||||
dom_id: empty_cell(),
|
dom_id: empty_cell(),
|
||||||
|
@ -657,9 +665,13 @@ impl Debug for VNode<'_> {
|
||||||
VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
|
VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
|
||||||
VNode::Anchor(a) => write!(s, "VAnchor"),
|
VNode::Anchor(a) => write!(s, "VAnchor"),
|
||||||
|
|
||||||
VNode::Fragment(_) => write!(s, "fragment"),
|
VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
|
||||||
VNode::Suspended { .. } => write!(s, "suspended"),
|
VNode::Suspended { .. } => write!(s, "VSuspended"),
|
||||||
VNode::Component(_) => write!(s, "component"),
|
VNode::Component(comp) => write!(
|
||||||
|
s,
|
||||||
|
"VComponent {{ fc: {:?}, children: {:?} }}",
|
||||||
|
comp.name, comp.children
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,58 +33,10 @@ use futures_util::pin_mut;
|
||||||
use futures_util::Future;
|
use futures_util::Future;
|
||||||
use futures_util::FutureExt;
|
use futures_util::FutureExt;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use indexmap::IndexSet;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::innerlude::*;
|
use crate::innerlude::*;
|
||||||
|
|
||||||
/// The "Mutations" object holds the changes that need to be made to the DOM.
|
|
||||||
///
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Mutations<'s> {
|
|
||||||
pub edits: Vec<DomEdit<'s>>,
|
|
||||||
pub noderefs: Vec<NodeRefMutation<'s>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> Mutations<'s> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let edits = Vec::new();
|
|
||||||
let noderefs = Vec::new();
|
|
||||||
Self { edits, noderefs }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extend(&mut self, other: &mut Mutations) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// refs are only assigned once
|
|
||||||
pub struct NodeRefMutation<'a> {
|
|
||||||
element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
|
|
||||||
element_id: ElementId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("NodeRefMutation")
|
|
||||||
.field("element_id", &self.element_id)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> NodeRefMutation<'a> {
|
|
||||||
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
|
|
||||||
self.element
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|f| f.get())
|
|
||||||
.and_then(|f| f.downcast_ref::<T>())
|
|
||||||
}
|
|
||||||
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
|
|
||||||
self.element
|
|
||||||
.as_mut()
|
|
||||||
.and_then(|f| f.get_mut())
|
|
||||||
.and_then(|f| f.downcast_mut::<T>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Scheduler {
|
pub struct Scheduler {
|
||||||
current_priority: EventPriority,
|
current_priority: EventPriority,
|
||||||
|
|
||||||
|
@ -432,7 +384,7 @@ pub struct Waypoint {
|
||||||
|
|
||||||
pub struct PriortySystem {
|
pub struct PriortySystem {
|
||||||
pub pending_scopes: Vec<ScopeId>,
|
pub pending_scopes: Vec<ScopeId>,
|
||||||
pub dirty_scopes: IndexSet<ScopeId>,
|
pub dirty_scopes: HashSet<ScopeId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PriortySystem {
|
impl PriortySystem {
|
||||||
|
|
|
@ -43,6 +43,9 @@ pub struct Scope {
|
||||||
pub(crate) hooks: HookList,
|
pub(crate) hooks: HookList,
|
||||||
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
||||||
|
|
||||||
|
// meta
|
||||||
|
pub(crate) function_name: &'static str,
|
||||||
|
|
||||||
// A reference to the resources shared by all the comonents
|
// A reference to the resources shared by all the comonents
|
||||||
pub(crate) vdom: SharedResources,
|
pub(crate) vdom: SharedResources,
|
||||||
}
|
}
|
||||||
|
@ -73,6 +76,8 @@ impl Scope {
|
||||||
child_nodes: ScopeChildren,
|
child_nodes: ScopeChildren,
|
||||||
|
|
||||||
vdom: SharedResources,
|
vdom: SharedResources,
|
||||||
|
|
||||||
|
function_name: &'static str,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let child_nodes = unsafe { child_nodes.extend_lifetime() };
|
let child_nodes = unsafe { child_nodes.extend_lifetime() };
|
||||||
|
|
||||||
|
@ -84,6 +89,7 @@ impl Scope {
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
function_name,
|
||||||
child_nodes,
|
child_nodes,
|
||||||
caller,
|
caller,
|
||||||
parent_idx: parent,
|
parent_idx: parent,
|
||||||
|
@ -141,7 +147,7 @@ impl Scope {
|
||||||
// the user's component succeeded. We can safely cycle to the next frame
|
// the user's component succeeded. We can safely cycle to the next frame
|
||||||
self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
|
self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
|
||||||
self.frames.cycle_frame();
|
self.frames.cycle_frame();
|
||||||
log::debug!("Cycle okay");
|
log::debug!("Successfully rendered component");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,10 @@ 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 helper type that lets scopes be ordered by their height
|
// /// A helper type that lets scopes be ordered by their height
|
||||||
// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
// pub struct HeightMarker {
|
// pub struct HeightMarker {
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub struct VirtualDom {
|
||||||
///
|
///
|
||||||
/// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
|
/// This is wrapped in an UnsafeCell because we will need to get mutable access to unique values in unique bump arenas
|
||||||
/// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
|
/// and rusts's guartnees cannot prove that this is safe. We will need to maintain the safety guarantees manually.
|
||||||
shared: SharedResources,
|
pub shared: SharedResources,
|
||||||
|
|
||||||
/// The index of the root component
|
/// The index of the root component
|
||||||
/// Should always be the first (gen=0, id=0)
|
/// Should always be the first (gen=0, id=0)
|
||||||
|
@ -111,7 +111,8 @@ impl VirtualDom {
|
||||||
|
|
||||||
let base_scope = components.insert_scope_with_key(move |myidx| {
|
let base_scope = components.insert_scope_with_key(move |myidx| {
|
||||||
let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
|
let caller = NodeFactory::create_component_caller(root, props_ptr as *const _);
|
||||||
Scope::new(caller, myidx, None, 0, ScopeChildren(&[]), link)
|
let name = type_name_of(root);
|
||||||
|
Scope::new(caller, myidx, None, 0, ScopeChildren(&[]), link, name)
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -150,35 +151,18 @@ impl VirtualDom {
|
||||||
/// The diff machine expects the RealDom's stack to be the root of the application
|
/// The diff machine expects the RealDom's stack to be the root of the application
|
||||||
///
|
///
|
||||||
/// Events like garabge collection, application of refs, etc are not handled by this method and can only be progressed
|
/// Events like garabge collection, application of refs, etc are not handled by this method and can only be progressed
|
||||||
/// through "run"
|
/// through "run". We completely avoid the task scheduler infrastructure.
|
||||||
///
|
|
||||||
pub fn rebuild<'s>(&'s mut self) -> Result<Mutations<'s>> {
|
pub fn rebuild<'s>(&'s mut self) -> Result<Mutations<'s>> {
|
||||||
let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
|
let mut fut = self.rebuild_async().boxed_local();
|
||||||
|
|
||||||
let cur_component = diff_machine
|
loop {
|
||||||
.get_scope_mut(&self.base_scope)
|
if let Some(edits) = (&mut fut).now_or_never() {
|
||||||
.expect("The base scope should never be moved");
|
break edits;
|
||||||
|
}
|
||||||
// todo!();
|
}
|
||||||
|
|
||||||
// // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
|
|
||||||
if cur_component.run_scope().is_ok() {
|
|
||||||
diff_machine.instructions.push(DiffInstruction::Create {
|
|
||||||
node: cur_component.frames.fin_head(),
|
|
||||||
and: MountType::Append,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// todo: should this be a hard error?
|
|
||||||
log::warn!(
|
|
||||||
"Component failed to run succesfully during rebuild.
|
|
||||||
This does not result in a failed rebuild, but indicates a logic failure within your app."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(diff_machine.mutations)
|
/// Rebuild the dom from the ground up
|
||||||
}
|
|
||||||
|
|
||||||
/// Rebuild the dom
|
|
||||||
pub async fn rebuild_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
|
pub async fn rebuild_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
|
||||||
let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
|
let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
|
||||||
|
|
||||||
|
@ -186,8 +170,6 @@ impl VirtualDom {
|
||||||
.get_scope_mut(&self.base_scope)
|
.get_scope_mut(&self.base_scope)
|
||||||
.expect("The base scope should never be moved");
|
.expect("The base scope should never be moved");
|
||||||
|
|
||||||
// todo!();
|
|
||||||
|
|
||||||
// // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
|
// // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
|
||||||
if cur_component.run_scope().is_ok() {
|
if cur_component.run_scope().is_ok() {
|
||||||
diff_machine.instructions.push(DiffInstruction::Create {
|
diff_machine.instructions.push(DiffInstruction::Create {
|
||||||
|
@ -205,6 +187,27 @@ impl VirtualDom {
|
||||||
|
|
||||||
Ok(diff_machine.mutations)
|
Ok(diff_machine.mutations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// diff the dom with itself
|
||||||
|
pub async fn diff_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
|
||||||
|
let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
|
||||||
|
|
||||||
|
let cur_component = diff_machine
|
||||||
|
.get_scope_mut(&self.base_scope)
|
||||||
|
.expect("The base scope should never be moved");
|
||||||
|
|
||||||
|
cur_component.run_scope().unwrap();
|
||||||
|
|
||||||
|
diff_machine.instructions.push(DiffInstruction::DiffNode {
|
||||||
|
old: cur_component.frames.wip_head(),
|
||||||
|
new: cur_component.frames.fin_head(),
|
||||||
|
});
|
||||||
|
|
||||||
|
diff_machine.work().await.unwrap();
|
||||||
|
|
||||||
|
Ok(diff_machine.mutations)
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
|
/// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
|
||||||
///
|
///
|
||||||
/// This method will not wait for any suspended nodes to complete.
|
/// This method will not wait for any suspended nodes to complete.
|
||||||
|
|
|
@ -104,9 +104,7 @@ async fn test_iterative_create_components() {
|
||||||
static Child: FC<()> = |cx| {
|
static Child: FC<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 {}
|
h1 {}
|
||||||
div {
|
div { {cx.children()} }
|
||||||
{cx.children()}
|
|
||||||
}
|
|
||||||
p {}
|
p {}
|
||||||
})
|
})
|
||||||
};
|
};
|
2
packages/core/tests/debugdiff.rs
Normal file
2
packages/core/tests/debugdiff.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/// A virtualdom wrapper used for testing purposes.
|
||||||
|
pub struct DebugDiff {}
|
46
packages/core/tests/diff_iterative.rs
Normal file
46
packages/core/tests/diff_iterative.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
//! tests to prove that the iterative implementation works
|
||||||
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
mod test_logging;
|
||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn test_iterative_create_components() {
|
||||||
|
static App: FC<()> = |cx| {
|
||||||
|
// test root fragments
|
||||||
|
cx.render(rsx! {
|
||||||
|
Child { "abc1" }
|
||||||
|
Child { "abc2" }
|
||||||
|
Child { "abc3" }
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
fn Child(cx: Context<()>) -> DomTree {
|
||||||
|
// test root fragments, anchors, and ChildNode type
|
||||||
|
cx.render(rsx! {
|
||||||
|
h1 {}
|
||||||
|
div { {cx.children()} }
|
||||||
|
Fragment {
|
||||||
|
Fragment {
|
||||||
|
Fragment {
|
||||||
|
"wozza"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{(0..0).map(|f| rsx!{ div { "walalla"}})}
|
||||||
|
p {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test_logging::set_up_logging();
|
||||||
|
|
||||||
|
let mut dom = VirtualDom::new(App);
|
||||||
|
|
||||||
|
let mutations = dom.rebuild_async().await.unwrap();
|
||||||
|
dbg!(mutations);
|
||||||
|
|
||||||
|
let mutations = dom.diff_async().await.unwrap();
|
||||||
|
dbg!(mutations);
|
||||||
|
}
|
|
@ -9,12 +9,13 @@ use bumpalo::Bump;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use dioxus::{
|
use dioxus::{
|
||||||
arena::SharedResources,
|
arena::SharedResources,
|
||||||
diff::{CreateMeta, DiffMachine},
|
diff::{CreateMeta, DiffInstruction, DiffMachine},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
DomEdit,
|
DomEdit,
|
||||||
};
|
};
|
||||||
use dioxus_core as dioxus;
|
use dioxus_core as dioxus;
|
||||||
use dioxus_html as dioxus_elements;
|
use dioxus_html as dioxus_elements;
|
||||||
|
use futures_util::FutureExt;
|
||||||
|
|
||||||
struct TestDom {
|
struct TestDom {
|
||||||
bump: Bump,
|
bump: Bump,
|
||||||
|
@ -38,30 +39,41 @@ impl TestDom {
|
||||||
lazy_nodes.into_vnode(NodeFactory::new(&self.bump))
|
lazy_nodes.into_vnode(NodeFactory::new(&self.bump))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Vec<DomEdit<'a>> {
|
fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
|
||||||
let mut edits = Vec::new();
|
// let mut edits = Vec::new();
|
||||||
let mut machine = DiffMachine::new_headless(&self.resources);
|
let mut machine = DiffMachine::new_headless(&self.resources);
|
||||||
machine.diff_node(old, new);
|
|
||||||
edits
|
machine
|
||||||
|
.instructions
|
||||||
|
.push(dioxus::diff::DiffInstruction::DiffNode { new, old });
|
||||||
|
|
||||||
|
machine.mutations
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create<'a, F1>(&'a self, left: LazyNodes<'a, F1>) -> (CreateMeta, Vec<DomEdit<'a>>)
|
fn create<'a, F1>(&'a self, left: LazyNodes<'a, F1>) -> Mutations<'a>
|
||||||
where
|
where
|
||||||
F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
||||||
{
|
{
|
||||||
let old = self.bump.alloc(self.render(left));
|
let old = self.bump.alloc(self.render(left));
|
||||||
let mut edits = Vec::new();
|
|
||||||
|
|
||||||
let mut machine = DiffMachine::new_headless(&self.resources);
|
let mut machine = DiffMachine::new_headless(&self.resources);
|
||||||
let meta = machine.create_vnode(old);
|
|
||||||
(meta, edits)
|
machine
|
||||||
|
.instructions
|
||||||
|
.push(dioxus::diff::DiffInstruction::Create {
|
||||||
|
node: old,
|
||||||
|
and: dioxus::diff::MountType::Append,
|
||||||
|
});
|
||||||
|
work_sync(&mut machine);
|
||||||
|
|
||||||
|
machine.mutations
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lazy_diff<'a, F1, F2>(
|
fn lazy_diff<'a, F1, F2>(
|
||||||
&'a self,
|
&'a self,
|
||||||
left: LazyNodes<'a, F1>,
|
left: LazyNodes<'a, F1>,
|
||||||
right: LazyNodes<'a, F2>,
|
right: LazyNodes<'a, F2>,
|
||||||
) -> (Vec<DomEdit<'a>>, Vec<DomEdit<'a>>)
|
) -> (Mutations<'a>, Mutations<'a>)
|
||||||
where
|
where
|
||||||
F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
||||||
F2: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
F2: FnOnce(NodeFactory<'a>) -> VNode<'a>,
|
||||||
|
@ -70,18 +82,37 @@ impl TestDom {
|
||||||
|
|
||||||
let new = self.bump.alloc(self.render(right));
|
let new = self.bump.alloc(self.render(right));
|
||||||
|
|
||||||
let mut create_edits = Vec::new();
|
// let mut create_edits = Vec::new();
|
||||||
|
|
||||||
let mut machine = DiffMachine::new_headless(&self.resources);
|
let mut machine = DiffMachine::new_headless(&self.resources);
|
||||||
machine.create_vnode(old);
|
machine
|
||||||
|
.instructions
|
||||||
|
.push(dioxus::diff::DiffInstruction::Create {
|
||||||
|
and: dioxus::diff::MountType::Append,
|
||||||
|
node: old,
|
||||||
|
});
|
||||||
|
work_sync(&mut machine);
|
||||||
|
let create_edits = machine.mutations;
|
||||||
|
|
||||||
let mut edits = Vec::new();
|
|
||||||
let mut machine = DiffMachine::new_headless(&self.resources);
|
let mut machine = DiffMachine::new_headless(&self.resources);
|
||||||
machine.diff_node(old, new);
|
machine
|
||||||
|
.instructions
|
||||||
|
.push(DiffInstruction::DiffNode { old, new });
|
||||||
|
work_sync(&mut machine);
|
||||||
|
let edits = machine.mutations;
|
||||||
|
|
||||||
(create_edits, edits)
|
(create_edits, edits)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn work_sync(machine: &mut DiffMachine) {
|
||||||
|
let mut fut = machine.work().boxed_local();
|
||||||
|
|
||||||
|
while let None = (&mut fut).now_or_never() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn diffing_works() {}
|
fn diffing_works() {}
|
||||||
|
|
||||||
|
@ -100,7 +131,7 @@ fn html_and_rsx_generate_the_same_output() {
|
||||||
#[test]
|
#[test]
|
||||||
fn fragments_create_properly() {
|
fn fragments_create_properly() {
|
||||||
let dom = TestDom::new();
|
let dom = TestDom::new();
|
||||||
let (meta, edits) = dom.create(rsx! {
|
let Mutations { edits, noderefs } = dom.create(rsx! {
|
||||||
div { "Hello a" }
|
div { "Hello a" }
|
||||||
div { "Hello b" }
|
div { "Hello b" }
|
||||||
div { "Hello c" }
|
div { "Hello c" }
|
||||||
|
@ -109,7 +140,7 @@ fn fragments_create_properly() {
|
||||||
assert!(&edits[3].is("CreateElement"));
|
assert!(&edits[3].is("CreateElement"));
|
||||||
assert!(&edits[6].is("CreateElement"));
|
assert!(&edits[6].is("CreateElement"));
|
||||||
|
|
||||||
assert_eq!(meta.added_to_stack, 3);
|
assert_eq!(*edits.last().unwrap(), DomEdit::AppendChildren { many: 3 });
|
||||||
dbg!(edits);
|
dbg!(edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +183,7 @@ fn empty_fragments_create_anchors_with_many_children() {
|
||||||
|
|
||||||
let edits = dom.lazy_diff(left, right);
|
let edits = dom.lazy_diff(left, right);
|
||||||
dbg!(&edits);
|
dbg!(&edits);
|
||||||
let last_edit = edits.1.last().unwrap();
|
let last_edit = edits.1.edits.last().unwrap();
|
||||||
assert!(last_edit.is("ReplaceWith"));
|
assert!(last_edit.is("ReplaceWith"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +221,7 @@ fn two_equal_fragments_are_equal() {
|
||||||
|
|
||||||
let edits = dom.lazy_diff(left, right);
|
let edits = dom.lazy_diff(left, right);
|
||||||
dbg!(&edits);
|
dbg!(&edits);
|
||||||
assert!(edits.1.is_empty());
|
assert!(edits.1.edits.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should result the creation of more nodes appended after the old last node
|
/// Should result the creation of more nodes appended after the old last node
|
||||||
|
|
35
packages/core/tests/work_sync.rs
Normal file
35
packages/core/tests/work_sync.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//! Diffing is interruptible, but uses yield_now which is loop-pollable
|
||||||
|
//!
|
||||||
|
//! This means you can actually call it synchronously if you want.
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use dioxus::{
|
||||||
|
arena::SharedResources,
|
||||||
|
diff::{CreateMeta, DiffInstruction, DiffMachine},
|
||||||
|
prelude::*,
|
||||||
|
scope::Scope,
|
||||||
|
};
|
||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_html as dioxus_elements;
|
||||||
|
use futures_util::FutureExt;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn worksync() {
|
||||||
|
static App: FC<()> = |cx| {
|
||||||
|
cx.render(rsx! {
|
||||||
|
div {"hello"}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let mut dom = VirtualDom::new(App);
|
||||||
|
|
||||||
|
let mut fut = dom.rebuild_async().boxed_local();
|
||||||
|
|
||||||
|
let mutations = loop {
|
||||||
|
let g = (&mut fut).now_or_never();
|
||||||
|
if g.is_some() {
|
||||||
|
break g.unwrap().unwrap();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dbg!(mutations);
|
||||||
|
}
|
Loading…
Reference in a new issue