Merge branch 'upstream' into fix-non-str-attributes

This commit is contained in:
Evan Almloff committed 2022-12-28 20:36:57 -06:00
commit 044462876d
52 files changed
+3002 -2852

No files matched your search

+111 -50
View File
@@ -30,31 +30,61 @@ impl<'b> VirtualDom {
.try_load_node()
.expect("Call rebuild before diffing");
use RenderReturn::{Async, Sync};
use RenderReturn::{Aborted, Pending, Ready};
match (old, new) {
(Sync(Some(l)), Sync(Some(r))) => self.diff_node(l, r),
// Normal pathway
(Ready(l), Ready(r)) => self.diff_node(l, r),
// Err cases
(Sync(Some(l)), Sync(None)) => self.diff_ok_to_err(l),
(Sync(None), Sync(Some(r))) => self.diff_err_to_ok(r),
(Sync(None), Sync(None)) => { /* nothing */ }
// Unwind the mutations if need be
(Ready(l), Aborted(p)) => self.diff_ok_to_err(l, p),
// Async
(Sync(Some(_l)), Async(_)) => todo!(),
(Sync(None), Async(_)) => todo!(),
(Async(_), Sync(Some(_r))) => todo!(),
(Async(_), Sync(None)) => { /* nothing */ }
(Async(_), Async(_)) => { /* nothing */ }
// Just move over the placeholder
(Aborted(l), Aborted(r)) => r.id.set(l.id.get()),
// Becomes async, do nothing while we ait
(Ready(_nodes), Pending(_fut)) => self.diff_ok_to_async(_nodes, scope),
// Placeholder becomes something
// We should also clear the error now
(Aborted(l), Ready(r)) => self.replace_placeholder(l, [r]),
(Aborted(_), Pending(_)) => todo!("async should not resolve here"),
(Pending(_), Ready(_)) => todo!("async should not resolve here"),
(Pending(_), Aborted(_)) => todo!("async should not resolve here"),
(Pending(_), Pending(_)) => {
// All suspense should resolve before we diff it again
panic!("Should not roll from suspense to suspense.");
}
};
}
self.scope_stack.pop();
}
fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>) {}
fn diff_err_to_ok(&mut self, _l: &'b VNode<'b>) {}
fn diff_ok_to_async(&mut self, _new: &'b VNode<'b>, _scope: ScopeId) {
//
}
fn diff_ok_to_err(&mut self, _l: &'b VNode<'b>, _p: &'b VPlaceholder) {
todo!()
}
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
// If hot reloading is enabled, we need to make sure we're using the latest template
#[cfg(debug_assertions)]
{
let (path, byte_index) = right_template.template.get().name.rsplit_once(':').unwrap();
if let Some(map) = self.templates.get(path) {
let byte_index = byte_index.parse::<usize>().unwrap();
if let Some(&template) = map.get(&byte_index) {
right_template.template.set(template);
if template != left_template.template.get() {
return self.replace(left_template, [right_template]);
}
}
}
}
// If the templates are the same, we don't need to do anything, nor do we want to
if templates_are_the_same(left_template, right_template) {
return;
@@ -99,11 +129,7 @@ impl<'b> VirtualDom {
});
// Make sure the roots get transferred over while we're here
left_template
.root_ids
.iter()
.zip(right_template.root_ids.iter())
.for_each(|(left, right)| right.set(left.get()));
right_template.root_ids.transfer(&left_template.root_ids);
}
fn diff_dynamic_node(
@@ -118,7 +144,7 @@ impl<'b> VirtualDom {
(Fragment(left), Fragment(right)) => self.diff_non_empty_fragment(left, right),
(Placeholder(left), Placeholder(right)) => right.id.set(left.id.get()),
(Component(left), Component(right)) => self.diff_vcomponent(left, right, node, idx),
(Placeholder(left), Fragment(right)) => self.replace_placeholder(left, right),
(Placeholder(left), Fragment(right)) => self.replace_placeholder(left, *right),
(Fragment(left), Placeholder(right)) => self.node_to_placeholder(left, right),
_ => todo!("This is an usual custom case for dynamic nodes. We don't know how to handle it yet."),
};
@@ -191,8 +217,12 @@ impl<'b> VirtualDom {
) {
let m = self.create_component_node(right_template, right, idx);
let pre_edits = self.mutations.edits.len();
self.remove_component_node(left, true);
assert!(self.mutations.edits.len() > pre_edits);
// We want to optimize the replace case to use one less mutation if possible
// Since mutations are done in reverse, the last node removed will be the first in the stack
// Instead of *just* removing it, we can use the replace mutation
@@ -638,6 +668,7 @@ impl<'b> VirtualDom {
/// Push all the real nodes on the stack
fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize {
node.template
.get()
.roots
.iter()
.enumerate()
@@ -646,7 +677,7 @@ impl<'b> VirtualDom {
Some(node) => node,
None => {
self.mutations.push(Mutation::PushRoot {
id: node.root_ids[idx].get().unwrap(),
id: node.root_ids.get(idx).unwrap(),
});
return 1;
}
@@ -673,7 +704,8 @@ impl<'b> VirtualDom {
Component(comp) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Some(node)) => self.push_all_real_nodes(node),
RenderReturn::Ready(node) => self.push_all_real_nodes(node),
RenderReturn::Aborted(_node) => todo!(),
_ => todo!(),
}
}
@@ -701,7 +733,11 @@ impl<'b> VirtualDom {
}
/// Simply replace a placeholder with a list of nodes
fn replace_placeholder(&mut self, l: &'b VPlaceholder, r: &'b [VNode<'b>]) {
fn replace_placeholder(
&mut self,
l: &'b VPlaceholder,
r: impl IntoIterator<Item = &'b VNode<'b>>,
) {
let m = self.create_children(r);
let id = l.id.get().unwrap();
self.mutations.push(Mutation::ReplaceWith { id, m });
@@ -773,11 +809,11 @@ impl<'b> VirtualDom {
}
fn reclaim_roots(&mut self, node: &VNode, gen_muts: bool) {
for (idx, _) in node.template.roots.iter().enumerate() {
for (idx, _) in node.template.get().roots.iter().enumerate() {
if let Some(dy) = node.dynamic_root(idx) {
self.remove_dynamic_node(dy, gen_muts);
} else {
let id = node.root_ids[idx].get().unwrap();
let id = node.root_ids.get(idx).unwrap();
if gen_muts {
self.mutations.push(Mutation::Remove { id });
}
@@ -790,8 +826,19 @@ impl<'b> VirtualDom {
let mut id = None;
for (idx, attr) in node.dynamic_attrs.iter().enumerate() {
// We'll clean up the root nodes either way, so don't worry
if node.template.attr_paths[idx].len() == 1 {
continue;
let path_len = node
.template
.get()
.attr_paths
.get(idx)
.map(|path| path.len());
// if the path is 1 the attribute is in the root, so we don't need to clean it up
// if the path is 0, the attribute is a not attached at all, so we don't need to clean it up
if let Some(len) = path_len {
if (..=1).contains(&len) {
continue;
}
}
let next_id = attr.mounted_element.get();
@@ -808,12 +855,16 @@ impl<'b> VirtualDom {
fn remove_nested_dyn_nodes(&mut self, node: &VNode) {
for (idx, dyn_node) in node.dynamic_nodes.iter().enumerate() {
// Roots are cleaned up automatically above
if node.template.node_paths[idx].len() == 1 {
continue;
let path_len = node
.template
.get()
.node_paths
.get(idx)
.map(|path| path.len());
// Roots are cleaned up automatically above and nodes with a empty path are placeholders
if let Some(2..) = path_len {
self.remove_dynamic_node(dyn_node, false)
}
self.remove_dynamic_node(dyn_node, false);
}
}
@@ -847,14 +898,20 @@ impl<'b> VirtualDom {
}
fn remove_component_node(&mut self, comp: &VComponent, gen_muts: bool) {
let scope = comp.scope.take().unwrap();
let scope = comp
.scope
.take()
.expect("VComponents to always have a scope");
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Some(t)) => {
println!("Removing component node sync {:?}", gen_muts);
self.remove_node(t, gen_muts)
RenderReturn::Ready(t) => self.remove_node(t, gen_muts),
RenderReturn::Aborted(t) => {
if let Some(id) = t.id.get() {
self.try_reclaim(id);
}
return;
}
_ => todo!("cannot handle nonstandard nodes"),
_ => todo!(),
};
let props = self.scopes[scope.0].props.take();
@@ -873,14 +930,14 @@ impl<'b> VirtualDom {
fn find_first_element(&self, node: &'b VNode<'b>) -> ElementId {
match node.dynamic_root(0) {
None => node.root_ids[0].get().unwrap(),
None => node.root_ids.get(0).unwrap(),
Some(Text(t)) => t.id.get().unwrap(),
Some(Fragment(t)) => self.find_first_element(&t[0]),
Some(Placeholder(t)) => t.id.get().unwrap(),
Some(Component(comp)) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Some(t)) => self.find_first_element(t),
RenderReturn::Ready(t) => self.find_first_element(t),
_ => todo!("cannot handle nonstandard nodes"),
}
}
@@ -888,15 +945,15 @@ impl<'b> VirtualDom {
}
fn find_last_element(&self, node: &'b VNode<'b>) -> ElementId {
match node.dynamic_root(node.template.roots.len() - 1) {
None => node.root_ids.last().unwrap().get().unwrap(),
match node.dynamic_root(node.template.get().roots.len() - 1) {
None => node.root_ids.last().unwrap(),
Some(Text(t)) => t.id.get().unwrap(),
Some(Fragment(t)) => self.find_last_element(t.last().unwrap()),
Some(Placeholder(t)) => t.id.get().unwrap(),
Some(Component(comp)) => {
let scope = comp.scope.get().unwrap();
match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } {
RenderReturn::Sync(Some(t)) => self.find_last_element(t),
RenderReturn::Ready(t) => self.find_last_element(t),
_ => todo!("cannot handle nonstandard nodes"),
}
}
@@ -911,27 +968,30 @@ impl<'b> VirtualDom {
/// We use the pointer of the dynamic_node list in this case
fn templates_are_the_same<'b>(left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) -> bool {
std::ptr::eq(left_template, right_template)
|| std::ptr::eq(left_template.dynamic_nodes, right_template.dynamic_nodes)
}
fn templates_are_different(left_template: &VNode, right_template: &VNode) -> bool {
!std::ptr::eq(left_template.template.name, right_template.template.name)
&& left_template.template.name != right_template.template.name
let left_template_name = left_template.template.get().name;
let right_template_name = right_template.template.get().name;
// we want to re-create the node if the template name is different by pointer even if the value is the same so that we can detect when hot reloading changes the template
!std::ptr::eq(left_template_name, right_template_name)
}
fn matching_components<'a>(
left: &'a VNode<'a>,
right: &'a VNode<'a>,
) -> Option<Vec<(&'a VComponent<'a>, &'a VComponent<'a>)>> {
if left.template.roots.len() != right.template.roots.len() {
let left_template = left.template.get();
let right_template = right.template.get();
if left_template.roots.len() != right_template.roots.len() {
return None;
}
// run through the components, ensuring they're the same
left.template
left_template
.roots
.iter()
.zip(right.template.roots.iter())
.zip(right_template.roots.iter())
.map(|(l, r)| {
let (l, r) = match (l, r) {
(TemplateNode::Dynamic { id: l }, TemplateNode::Dynamic { id: r }) => (l, r),
@@ -956,11 +1016,12 @@ fn matching_components<'a>(
/// - for appending children we can use AppendChildren
#[allow(dead_code)]
fn is_dyn_node_only_child(node: &VNode, idx: usize) -> bool {
let path = node.template.node_paths[idx];
let template = node.template.get();
let path = template.node_paths[idx];
// use a loop to index every static node's children until the path has run out
// only break if the last path index is a dynamic node
let mut static_node = &node.template.roots[path[0] as usize];
let mut static_node = &template.roots[path[0] as usize];
for i in 1..path.len() - 1 {
match static_node {