wip: error boundary

This commit is contained in:
Jonathan Kelley committed 2022-11-22 18:38:27 -08:00
1 parent 112c954e00
commit 0e5a59f9ed
17 files changed
+343 -316

No files matched your search

+205 -171
View File
@@ -21,24 +21,6 @@ use crate::{
use fxhash::{FxHashMap, FxHashSet};
use slab::Slab;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DirtyScope {
pub height: u32,
pub id: ScopeId,
}
impl PartialOrd for DirtyScope {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.height.cmp(&other.height))
}
}
impl Ord for DirtyScope {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.height.cmp(&other.height)
}
}
impl<'b> VirtualDom {
pub fn diff_scope(&mut self, mutations: &mut Mutations<'b>, scope: ScopeId) {
let scope_state = &mut self.scopes[scope.0];
@@ -78,25 +60,26 @@ impl<'b> VirtualDom {
use RenderReturn::{Async, Sync};
match (left, right) {
// diff
(Sync(Some(l)), Sync(Some(r))) => self.diff_vnode(m, l, r),
(Sync(Ok(l)), Sync(Ok(r))) => self.diff_vnode(m, l, r),
// remove old with placeholder
(Sync(Some(l)), Sync(None)) | (Sync(Some(l)), Async(_)) => {
//
let id = self.next_element(l, &[]); // todo!
m.push(Mutation::CreatePlaceholder { id });
self.drop_template(m, l, true);
}
_ => todo!("handle diffing nonstandard nodes"),
// // remove old with placeholder
// (Sync(Ok(l)), Sync(None)) | (Sync(Ok(l)), Async(_)) => {
// //
// let id = self.next_element(l, &[]); // todo!
// m.push(Mutation::CreatePlaceholder { id });
// self.drop_template(m, l, true);
// }
// remove placeholder with nodes
(Sync(None), Sync(Some(_))) => {}
(Async(_), Sync(Some(v))) => {}
// // remove placeholder with nodes
// (Sync(None), Sync(Ok(_))) => {}
// (Async(_), Sync(Ok(v))) => {}
// nothing... just transfer the placeholders over
(Async(_), Async(_))
| (Sync(None), Sync(None))
| (Sync(None), Async(_))
| (Async(_), Sync(None)) => {}
// // nothing... just transfer the placeholders over
// (Async(_), Async(_))
// | (Sync(None), Sync(None))
// | (Sync(None), Async(_))
// | (Async(_), Sync(None)) => {}
}
}
@@ -120,15 +103,19 @@ impl<'b> VirtualDom {
.mounted_element
.set(left_attr.mounted_element.get());
if left_attr.value != right_attr.value {
if left_attr.value != right_attr.value || left_attr.volatile {
// todo: add more types of attribute values
if let AttributeValue::Text(text) = right_attr.value {
muts.push(Mutation::SetAttribute {
id: left_attr.mounted_element.get(),
name: left_attr.name,
value: text,
ns: right_attr.namespace,
});
match right_attr.value {
AttributeValue::Text(text) => {
muts.push(Mutation::SetAttribute {
id: left_attr.mounted_element.get(),
name: left_attr.name,
value: text,
ns: right_attr.namespace,
});
}
// todo: more types of attribute values
_ => (),
}
}
}
@@ -139,10 +126,9 @@ impl<'b> VirtualDom {
.zip(right_template.dynamic_nodes.iter())
{
match (left_node, right_node) {
(Component(left), Component(right)) => self.diff_vcomponent(muts, left, right),
(Text(left), Text(right)) => self.diff_vtext(muts, left, right),
(Fragment(left), Fragment(right)) => self.diff_vfragment(muts, left, right),
(Placeholder(left), Placeholder(right)) => right.set(left.get()),
(Component(left), Component(right)) => self.diff_vcomponent(muts, left, right),
_ => self.replace(muts, left_template, right_template, left_node, right_node),
};
}
@@ -168,17 +154,6 @@ impl<'b> VirtualDom {
// this codepath is through "light_diff", but we check there that the pointers are the same
assert_eq!(left.render_fn, right.render_fn);
/*
let left = rsx!{ Component {} }
let right = rsx!{ Component {} }
*/
// Make sure the new vcomponent has the right scopeid associated to it
let scope_id = left.scope.get().unwrap();
right.scope.set(Some(scope_id));
@@ -276,36 +251,63 @@ impl<'b> VirtualDom {
left: &'b VFragment<'b>,
right: &'b VFragment<'b>,
) {
// match (left.nodes, right.nodes) {
// ([], []) => rp.set(lp.get()),
// ([], _) => {
// //
// todo!()
// }
// (_, []) => {
// // if this fragment is the only child of its parent, then we can use the "RemoveAllChildren" mutation
// todo!()
// }
// _ => {
// let new_is_keyed = new[0].key.is_some();
// let old_is_keyed = old[0].key.is_some();
use VFragment::*;
match (left, right) {
(Empty(l), Empty(r)) => r.set(l.get()),
(Empty(l), NonEmpty(r)) => self.replace_placeholder_with_nodes(muts, l, r),
(NonEmpty(l), Empty(r)) => self.replace_nodes_with_placeholder(muts, l, r),
(NonEmpty(old), NonEmpty(new)) => self.diff_non_empty_fragment(new, old, muts),
}
}
// debug_assert!(
// new.iter().all(|n| n.key.is_some() == new_is_keyed),
// "all siblings must be keyed or all siblings must be non-keyed"
// );
// debug_assert!(
// old.iter().all(|o| o.key.is_some() == old_is_keyed),
// "all siblings must be keyed or all siblings must be non-keyed"
// );
fn replace_placeholder_with_nodes(
&mut self,
muts: &mut Mutations<'b>,
l: &'b std::cell::Cell<ElementId>,
r: &'b [VNode<'b>],
) {
let created = r
.iter()
.fold(0, |acc, child| acc + self.create(muts, child));
muts.push(Mutation::ReplaceWith {
id: l.get(),
m: created,
})
}
// if new_is_keyed && old_is_keyed {
// self.diff_keyed_children(muts, old, new);
// } else {
// self.diff_non_keyed_children(muts, old, new);
// }
// }
// }
fn replace_nodes_with_placeholder(
&mut self,
muts: &mut Mutations<'b>,
l: &'b [VNode<'b>],
r: &'b std::cell::Cell<ElementId>,
) {
//
// Remove the old nodes, except for one
self.remove_nodes(muts, &l[1..]);
}
fn diff_non_empty_fragment(
&mut self,
new: &'b [VNode<'b>],
old: &'b [VNode<'b>],
muts: &mut Mutations<'b>,
) {
let new_is_keyed = new[0].key.is_some();
let old_is_keyed = old[0].key.is_some();
debug_assert!(
new.iter().all(|n| n.key.is_some() == new_is_keyed),
"all siblings must be keyed or all siblings must be non-keyed"
);
debug_assert!(
old.iter().all(|o| o.key.is_some() == old_is_keyed),
"all siblings must be keyed or all siblings must be non-keyed"
);
if new_is_keyed && old_is_keyed {
// self.diff_keyed_children(muts, old, new);
} else {
self.diff_non_keyed_children(muts, old, new);
}
}
// Diff children that are not keyed.
@@ -330,8 +332,9 @@ impl<'b> VirtualDom {
match old.len().cmp(&new.len()) {
Ordering::Greater => self.remove_nodes(muts, &old[new.len()..]),
Ordering::Less => todo!(),
// Ordering::Less => self.create_and_insert_after(&new[old.len()..], old.last().unwrap()),
Ordering::Less => {
self.create_and_insert_after(muts, &new[old.len()..], old.last().unwrap())
}
Ordering::Equal => {}
}
@@ -340,95 +343,95 @@ impl<'b> VirtualDom {
}
}
// Diffing "keyed" children.
//
// With keyed children, we care about whether we delete, move, or create nodes
// versus mutate existing nodes in place. Presumably there is some sort of CSS
// transition animation that makes the virtual DOM diffing algorithm
// observable. By specifying keys for nodes, we know which virtual DOM nodes
// must reuse (or not reuse) the same physical DOM nodes.
//
// This is loosely based on Inferno's keyed patching implementation. However, we
// have to modify the algorithm since we are compiling the diff down into change
// list instructions that will be executed later, rather than applying the
// changes to the DOM directly as we compare virtual DOMs.
//
// https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
//
// The stack is empty upon entry.
fn diff_keyed_children(
&mut self,
muts: &mut Mutations<'b>,
old: &'b [VNode<'b>],
new: &'b [VNode<'b>],
) {
// if cfg!(debug_assertions) {
// let mut keys = fxhash::FxHashSet::default();
// let mut assert_unique_keys = |children: &'b [VNode<'b>]| {
// keys.clear();
// for child in children {
// let key = child.key;
// debug_assert!(
// key.is_some(),
// "if any sibling is keyed, all siblings must be keyed"
// );
// keys.insert(key);
// }
// debug_assert_eq!(
// children.len(),
// keys.len(),
// "keyed siblings must each have a unique key"
// );
// };
// assert_unique_keys(old);
// assert_unique_keys(new);
// }
// // Diffing "keyed" children.
// //
// // With keyed children, we care about whether we delete, move, or create nodes
// // versus mutate existing nodes in place. Presumably there is some sort of CSS
// // transition animation that makes the virtual DOM diffing algorithm
// // observable. By specifying keys for nodes, we know which virtual DOM nodes
// // must reuse (or not reuse) the same physical DOM nodes.
// //
// // This is loosely based on Inferno's keyed patching implementation. However, we
// // have to modify the algorithm since we are compiling the diff down into change
// // list instructions that will be executed later, rather than applying the
// // changes to the DOM directly as we compare virtual DOMs.
// //
// // https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
// //
// // The stack is empty upon entry.
// fn diff_keyed_children(
// &mut self,
// muts: &mut Mutations<'b>,
// old: &'b [VNode<'b>],
// new: &'b [VNode<'b>],
// ) {
// if cfg!(debug_assertions) {
// let mut keys = fxhash::FxHashSet::default();
// let mut assert_unique_keys = |children: &'b [VNode<'b>]| {
// keys.clear();
// for child in children {
// let key = child.key;
// debug_assert!(
// key.is_some(),
// "if any sibling is keyed, all siblings must be keyed"
// );
// keys.insert(key);
// }
// debug_assert_eq!(
// children.len(),
// keys.len(),
// "keyed siblings must each have a unique key"
// );
// };
// assert_unique_keys(old);
// assert_unique_keys(new);
// }
// // First up, we diff all the nodes with the same key at the beginning of the
// // children.
// //
// // `shared_prefix_count` is the count of how many nodes at the start of
// // `new` and `old` share the same keys.
// let (left_offset, right_offset) = match self.diff_keyed_ends(muts, old, new) {
// Some(count) => count,
// None => return,
// };
// // First up, we diff all the nodes with the same key at the beginning of the
// // children.
// //
// // `shared_prefix_count` is the count of how many nodes at the start of
// // `new` and `old` share the same keys.
// let (left_offset, right_offset) = match self.diff_keyed_ends(muts, old, new) {
// Some(count) => count,
// None => return,
// };
// // Ok, we now hopefully have a smaller range of children in the middle
// // within which to re-order nodes with the same keys, remove old nodes with
// // now-unused keys, and create new nodes with fresh keys.
// // Ok, we now hopefully have a smaller range of children in the middle
// // within which to re-order nodes with the same keys, remove old nodes with
// // now-unused keys, and create new nodes with fresh keys.
// let old_middle = &old[left_offset..(old.len() - right_offset)];
// let new_middle = &new[left_offset..(new.len() - right_offset)];
// let old_middle = &old[left_offset..(old.len() - right_offset)];
// let new_middle = &new[left_offset..(new.len() - right_offset)];
// debug_assert!(
// !((old_middle.len() == new_middle.len()) && old_middle.is_empty()),
// "keyed children must have the same number of children"
// );
// debug_assert!(
// !((old_middle.len() == new_middle.len()) && old_middle.is_empty()),
// "keyed children must have the same number of children"
// );
// if new_middle.is_empty() {
// // remove the old elements
// self.remove_nodes(muts, old_middle);
// } else if old_middle.is_empty() {
// // there were no old elements, so just create the new elements
// // we need to find the right "foothold" though - we shouldn't use the "append" at all
// if left_offset == 0 {
// // insert at the beginning of the old list
// let foothold = &old[old.len() - right_offset];
// self.create_and_insert_before(new_middle, foothold);
// } else if right_offset == 0 {
// // insert at the end the old list
// let foothold = old.last().unwrap();
// self.create_and_insert_after(new_middle, foothold);
// } else {
// // inserting in the middle
// let foothold = &old[left_offset - 1];
// self.create_and_insert_after(new_middle, foothold);
// }
// } else {
// self.diff_keyed_middle(muts, old_middle, new_middle);
// }
}
// if new_middle.is_empty() {
// // remove the old elements
// self.remove_nodes(muts, old_middle);
// } else if old_middle.is_empty() {
// // there were no old elements, so just create the new elements
// // we need to find the right "foothold" though - we shouldn't use the "append" at all
// if left_offset == 0 {
// // insert at the beginning of the old list
// let foothold = &old[old.len() - right_offset];
// self.create_and_insert_before(muts, new_middle, foothold);
// } else if right_offset == 0 {
// // insert at the end the old list
// let foothold = old.last().unwrap();
// self.create_and_insert_after(muts, new_middle, foothold);
// } else {
// // inserting in the middle
// let foothold = &old[left_offset - 1];
// self.create_and_insert_after(muts, new_middle, foothold);
// }
// } else {
// self.diff_keyed_middle(muts, old_middle, new_middle);
// }
// }
// /// Diff both ends of the children that share keys.
// ///
@@ -437,7 +440,7 @@ impl<'b> VirtualDom {
// /// If there is no offset, then this function returns None and the diffing is complete.
// fn diff_keyed_ends(
// &mut self,
// muts: &mut Renderer<'b>,
// muts: &mut Mutations<'b>,
// old: &'b [VNode<'b>],
// new: &'b [VNode<'b>],
// ) -> Option<(usize, usize)> {
@@ -496,7 +499,7 @@ impl<'b> VirtualDom {
// #[allow(clippy::too_many_lines)]
// fn diff_keyed_middle(
// &mut self,
// muts: &mut Renderer<'b>,
// muts: &mut Mutations<'b>,
// old: &'b [VNode<'b>],
// new: &'b [VNode<'b>],
// ) {
@@ -677,6 +680,37 @@ impl<'b> VirtualDom {
fn remove_nodes(&mut self, muts: &mut Mutations<'b>, nodes: &'b [VNode<'b>]) {
//
}
/// Push all the real nodes on the stack
fn push_elements_onto_stack(&mut self, node: &VNode) -> usize {
todo!()
}
pub(crate) fn create_and_insert_before(
&self,
mutations: &mut Mutations<'b>,
new: &[VNode],
after: &VNode,
) {
let id = self.get_last_real_node(after);
}
pub(crate) fn create_and_insert_after(
&self,
mutations: &mut Mutations<'b>,
new: &[VNode],
after: &VNode,
) {
let id = self.get_last_real_node(after);
}
fn get_last_real_node(&self, node: &VNode) -> ElementId {
match node.template.roots.last().unwrap() {
TemplateNode::Element { .. } => todo!(),
TemplateNode::Text(t) => todo!(),
TemplateNode::Dynamic(_) => todo!(),
TemplateNode::DynamicText(_) => todo!(),
}
}
}
fn matching_components<'a>(