wip: ....sigh..... so the diffing algorithm is robust
but it's still not finished. We need to re-enable the fancier keyed diffing versions some point (soon!).
This commit is contained in:
1 parent
ff0a3d1c83
commit
68ed1c04e7
10 files changed
+168
-102
No files matched your search
@@ -17,6 +17,7 @@ pub struct Element {
|
||||
attributes: Vec<ElementAttr>,
|
||||
listeners: Vec<ElementAttr>,
|
||||
children: Vec<Node>,
|
||||
is_static: bool,
|
||||
}
|
||||
|
||||
impl ToTokens for Element {
|
||||
@@ -100,6 +101,7 @@ impl Parse for Element {
|
||||
attributes,
|
||||
children,
|
||||
listeners,
|
||||
is_static: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+41
-54
@@ -66,7 +66,7 @@ use std::any::Any;
|
||||
/// target-specific Node type as well as easily serializing the edits to be sent over a network or IPC connection.
|
||||
pub trait RealDom<'a> {
|
||||
// Navigation
|
||||
fn push_root(&mut self, root: RealDomNode);
|
||||
fn push(&mut self, root: RealDomNode);
|
||||
fn pop(&mut self);
|
||||
|
||||
// Add Nodes to the dom
|
||||
@@ -168,7 +168,7 @@ where
|
||||
}
|
||||
|
||||
if old.text != new.text {
|
||||
self.dom.push_root(old.dom_id.get());
|
||||
self.dom.push(old.dom_id.get());
|
||||
log::debug!("Text has changed {}, {}", old.text, new.text);
|
||||
self.dom.set_text(new.text);
|
||||
self.dom.pop();
|
||||
@@ -183,7 +183,7 @@ where
|
||||
//
|
||||
// In Dioxus, this is less likely to occur unless through a fragment
|
||||
if new.tag_name != old.tag_name || new.namespace != old.namespace {
|
||||
self.dom.push_root(old.dom_id.get());
|
||||
self.dom.push(old.dom_id.get());
|
||||
let meta = self.create(new_node);
|
||||
self.dom.replace_with(meta.added_to_stack);
|
||||
self.dom.pop();
|
||||
@@ -194,7 +194,7 @@ where
|
||||
new.dom_id.set(oldid);
|
||||
|
||||
// push it just in case
|
||||
self.dom.push_root(oldid);
|
||||
self.dom.push(oldid);
|
||||
self.diff_listeners(old.listeners, new.listeners);
|
||||
self.diff_attr(old.attributes, new.attributes, new.namespace);
|
||||
self.diff_children(old.children, new.children);
|
||||
@@ -210,65 +210,48 @@ where
|
||||
let scope_id = old.ass_scope.get();
|
||||
new.ass_scope.set(scope_id);
|
||||
|
||||
// new.mounted_root.set(old.mounted_root.get());
|
||||
|
||||
// make sure the component's caller function is up to date
|
||||
let scope = self.components.try_get_mut(scope_id.unwrap()).unwrap();
|
||||
// .with_scope(scope_id.unwrap(), |scope| {
|
||||
|
||||
scope.caller = new.caller.clone();
|
||||
|
||||
// ack - this doesn't work on its own!
|
||||
scope.update_children(new.children);
|
||||
|
||||
// })
|
||||
// .unwrap();
|
||||
|
||||
// React doesn't automatically memoize, but we do.
|
||||
// The cost is low enough to make it worth checking
|
||||
// Rust produces fairly performant comparison methods, sometimes SIMD accelerated
|
||||
// let should_render = match old.comparator {
|
||||
// Some(comparator) => comparator(new),
|
||||
// None => true,
|
||||
// };
|
||||
let should_render = match old.comparator {
|
||||
Some(comparator) => comparator(new),
|
||||
None => true,
|
||||
};
|
||||
|
||||
if should_render {
|
||||
scope.run_scope().unwrap();
|
||||
self.diff_node(scope.old_frame(), scope.next_frame());
|
||||
} else {
|
||||
//
|
||||
}
|
||||
|
||||
// if should_render {
|
||||
// // self.dom.commit_traversal();
|
||||
// let f = self.components.try_get_mut(scope_id.unwrap()).unwrap();
|
||||
// self.components
|
||||
// .with_scope(scope_id.unwrap(), |f| {
|
||||
// log::debug!("running scope during diff {:#?}", scope_id);
|
||||
scope.run_scope().unwrap();
|
||||
self.diff_node(scope.old_frame(), scope.next_frame());
|
||||
// log::debug!("scope completed {:#?}", scope_id);
|
||||
self.seen_nodes.insert(scope_id.unwrap());
|
||||
// })
|
||||
// .unwrap();
|
||||
|
||||
// diff_machine.change_list.load_known_root(root_id);
|
||||
// run the scope
|
||||
//
|
||||
// } else {
|
||||
// log::error!("Memoized componented");
|
||||
// // Component has memoized itself and doesn't need to be re-rendered.
|
||||
// // We still need to make sure the child's props are up-to-date.
|
||||
// // Don't commit traversal
|
||||
// }
|
||||
} else {
|
||||
// It's an entirely different component
|
||||
// this seems to be a fairy common code path that we could
|
||||
let mut old_iter = RealChildIterator::new(old_node, &self.components);
|
||||
let first = old_iter
|
||||
.next()
|
||||
.expect("Components should generate a placeholder root");
|
||||
|
||||
// A new component has shown up! We need to destroy the old node
|
||||
// remove any leftovers
|
||||
for to_remove in old_iter {
|
||||
self.dom.push(to_remove);
|
||||
self.dom.remove();
|
||||
}
|
||||
|
||||
// seems like we could combine this into a single instruction....
|
||||
self.dom.push(first);
|
||||
let meta = self.create(new_node);
|
||||
self.dom.replace_with(meta.added_to_stack);
|
||||
self.dom.pop();
|
||||
|
||||
// Wipe the old one and plant the new one
|
||||
// self.dom.commit_traversal();
|
||||
// self.dom.replace_node_with(old.dom_id, new.dom_id);
|
||||
// self.create(new_node);
|
||||
log::warn!("creating and replacing...");
|
||||
self.create(new_node);
|
||||
|
||||
// self.dom.replace_with();
|
||||
// self.create_and_repalce(new_node, old.mounted_root.get());
|
||||
|
||||
// Now we need to remove the old scope and all of its descendents
|
||||
let old_scope = old.ass_scope.get().unwrap();
|
||||
self.destroy_scopes(old_scope);
|
||||
}
|
||||
@@ -278,7 +261,7 @@ where
|
||||
// This is the case where options or direct vnodes might be used.
|
||||
// In this case, it's faster to just skip ahead to their diff
|
||||
if old.children.len() == 1 && new.children.len() == 1 {
|
||||
self.diff_node(old.children.get(0).unwrap(), new.children.get(0).unwrap());
|
||||
self.diff_node(&old.children[0], &new.children[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -300,6 +283,7 @@ where
|
||||
) => {
|
||||
// Choose the node to use as the placeholder for replacewith
|
||||
let back_node = match old_node {
|
||||
// We special case these two types to avoid allocating the small-vecs
|
||||
VNode::Element(_) | VNode::Text(_) => old_node
|
||||
.get_mounted_id(&self.components)
|
||||
.expect("Element and text always have a real node"),
|
||||
@@ -313,7 +297,7 @@ where
|
||||
|
||||
// remove any leftovers
|
||||
for to_remove in old_iter {
|
||||
self.dom.push_root(to_remove);
|
||||
self.dom.push(to_remove);
|
||||
self.dom.remove();
|
||||
}
|
||||
|
||||
@@ -322,9 +306,11 @@ where
|
||||
};
|
||||
|
||||
// replace the placeholder or first node with the nodes generated from the "new"
|
||||
self.dom.push_root(back_node);
|
||||
self.dom.push(back_node);
|
||||
let meta = self.create(new_node);
|
||||
self.dom.replace_with(meta.added_to_stack);
|
||||
|
||||
// todo use the is_static metadata to update this subtree
|
||||
}
|
||||
|
||||
// TODO
|
||||
@@ -1317,9 +1303,10 @@ enum KeyedPrefixResult {
|
||||
struct RealChildIterator<'a> {
|
||||
scopes: &'a SharedArena,
|
||||
|
||||
// Heuristcally we should never bleed into 5 completely nested fragments/components
|
||||
// Heuristcally we should never bleed into 4 completely nested fragments/components
|
||||
// Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane
|
||||
stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 5]>,
|
||||
// TODO: use const generics instead of the 4 estimation
|
||||
stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 4]>,
|
||||
}
|
||||
|
||||
impl<'a> RealChildIterator<'a> {
|
||||
|
||||
@@ -18,6 +18,7 @@ pub enum DomEdit<'bump> {
|
||||
PushRoot {
|
||||
root: u64,
|
||||
},
|
||||
PopRoot,
|
||||
AppendChildren {
|
||||
many: u32,
|
||||
},
|
||||
|
||||
@@ -62,7 +62,7 @@ impl RealDomNode {
|
||||
pub fn new(id: u64) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
pub fn empty() -> Self {
|
||||
pub const fn empty() -> Self {
|
||||
Self(u64::MIN)
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ impl DebugDom {
|
||||
}
|
||||
}
|
||||
impl<'a> RealDom<'a> for DebugDom {
|
||||
fn push_root(&mut self, root: RealDomNode) {}
|
||||
fn push(&mut self, root: RealDomNode) {}
|
||||
fn pop(&mut self) {}
|
||||
|
||||
fn append_children(&mut self, many: u32) {}
|
||||
|
||||
+53
-43
@@ -71,32 +71,46 @@ impl WebsysDom {
|
||||
}
|
||||
|
||||
impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
||||
fn push_root(&mut self, root: RealDomNode) {
|
||||
fn push(&mut self, root: RealDomNode) {
|
||||
log::debug!("Called [push_root] {:?}", root);
|
||||
let key: DefaultKey = KeyData::from_ffi(root.0).into();
|
||||
let domnode = self.nodes.get(key).expect("Failed to pop know root");
|
||||
self.stack.push(domnode.clone());
|
||||
}
|
||||
// drop the node off the stack
|
||||
fn pop(&mut self) {
|
||||
self.stack.pop();
|
||||
}
|
||||
|
||||
fn append_children(&mut self, many: u32) {
|
||||
log::debug!("Called [`append_child`]");
|
||||
let child = self.stack.pop();
|
||||
|
||||
if child.dyn_ref::<web_sys::Text>().is_some() {
|
||||
if self.last_node_was_text {
|
||||
let comment_node = self
|
||||
.document
|
||||
.create_comment("dioxus")
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
self.stack.top().append_child(&comment_node).unwrap();
|
||||
let mut root: Node = self
|
||||
.stack
|
||||
.list
|
||||
.get(self.stack.list.len() - many as usize)
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
for _ in 0..many {
|
||||
let child = self.stack.pop();
|
||||
|
||||
if child.dyn_ref::<web_sys::Text>().is_some() {
|
||||
if self.last_node_was_text {
|
||||
let comment_node = self
|
||||
.document
|
||||
.create_comment("dioxus")
|
||||
.dyn_into::<Node>()
|
||||
.unwrap();
|
||||
self.stack.top().append_child(&comment_node).unwrap();
|
||||
}
|
||||
self.last_node_was_text = true;
|
||||
} else {
|
||||
self.last_node_was_text = false;
|
||||
}
|
||||
self.last_node_was_text = true;
|
||||
} else {
|
||||
self.last_node_was_text = false;
|
||||
}
|
||||
|
||||
self.stack.top().append_child(&child).unwrap();
|
||||
root.append_child(&child).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_with(&mut self, many: u32) {
|
||||
@@ -104,35 +118,31 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
||||
let new_node = self.stack.pop();
|
||||
let old_node = self.stack.pop();
|
||||
|
||||
if old_node.has_type::<Element>() {
|
||||
old_node
|
||||
.dyn_ref::<Element>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else if old_node.has_type::<web_sys::CharacterData>() {
|
||||
old_node
|
||||
.dyn_ref::<web_sys::CharacterData>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else if old_node.has_type::<web_sys::DocumentType>() {
|
||||
old_node
|
||||
.dyn_ref::<web_sys::DocumentType>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("Cannot replace node: {:?}", old_node);
|
||||
// TODO: use different-sized replace withs
|
||||
if many == 1 {
|
||||
if old_node.has_type::<Element>() {
|
||||
old_node
|
||||
.dyn_ref::<Element>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else if old_node.has_type::<web_sys::CharacterData>() {
|
||||
old_node
|
||||
.dyn_ref::<web_sys::CharacterData>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else if old_node.has_type::<web_sys::DocumentType>() {
|
||||
old_node
|
||||
.dyn_ref::<web_sys::DocumentType>()
|
||||
.unwrap()
|
||||
.replace_with_with_node_1(&new_node)
|
||||
.unwrap();
|
||||
} else {
|
||||
panic!("Cannot replace node: {:?}", old_node);
|
||||
}
|
||||
}
|
||||
|
||||
// // poc to see if this is a valid solution
|
||||
// if let Some(id) = self.current_known {
|
||||
// // update mapping
|
||||
// self.known_roots.insert(id, new_node.clone());
|
||||
// self.current_known = None;
|
||||
// }
|
||||
|
||||
self.stack.push(new_node);
|
||||
}
|
||||
|
||||
@@ -303,7 +313,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom {
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Stack {
|
||||
list: Vec<Node>,
|
||||
pub list: Vec<Node>,
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
|
||||
@@ -31,9 +31,12 @@ impl WebviewDom<'_> {
|
||||
}
|
||||
}
|
||||
impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
|
||||
fn push_root(&mut self, root: RealDomNode) {
|
||||
fn push(&mut self, root: RealDomNode) {
|
||||
self.edits.push(PushRoot { root: root.0 });
|
||||
}
|
||||
fn pop(&mut self) {
|
||||
self.edits.push(PopRoot {});
|
||||
}
|
||||
|
||||
fn append_children(&mut self, many: u32) {
|
||||
self.edits.push(AppendChildren { many });
|
||||
|
||||
Reference in new issue
Block a user