wip: staticify?

This commit is contained in:
Jonathan Kelley committed 2021-03-11 12:27:01 -05:00
1 parent 78d093a945
commit 5ad81885e4
21 files changed
+433 -346

No files matched your search

+135 -101
View File
@@ -32,13 +32,18 @@
//!
//! More info on how to improve this diffing algorithm:
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
use crate::{
innerlude::*,
};
use crate::{innerlude::*, scope::Scoped};
use bumpalo::Bump;
use fxhash::{FxHashMap, FxHashSet};
use generational_arena::Arena;
use uuid::Uuid;
use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
use std::{
cell::{RefCell, RefMut},
cmp::Ordering,
collections::VecDeque,
rc::Rc,
};
/// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
/// diffing trees of components. This means we can "re-enter" a subtree of a component by queuing a "NeedToDiff" event.
@@ -50,20 +55,29 @@ use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
/// different cursor position.
///
/// The order of these re-entrances is stored in the DiffState itself. The DiffState comes pre-loaded with a set of components
/// that were modified by the eventtrigger. This prevents doubly evaluating components if they wereboth updated via
/// that were modified by the eventtrigger. This prevents doubly evaluating components if they were both updated via
/// subscriptions and props changes.
pub struct DiffMachine<'a> {
pub change_list: EditMachine<'a>,
pub diffed: FxHashSet<ScopeIdx>,
pub need_to_diff: FxHashSet<ScopeIdx>,
pub lifecycle_events: VecDeque<LifeCycleEvent>,
}
#[derive(Debug)]
pub enum LifeCycleEvent {
Mount { caller: Caller, id: Uuid },
PropsChanged,
SameProps,
Remove,
}
impl<'a> DiffMachine<'a> {
pub fn new(bump: &'a Bump) -> Self {
pub fn new() -> Self {
// pub fn new(bump: &'a Bump) -> Self {
Self {
change_list: EditMachine::new(bump),
lifecycle_events: VecDeque::new(),
change_list: EditMachine::new(),
diffed: FxHashSet::default(),
need_to_diff: FxHashSet::default(),
}
}
@@ -111,18 +125,31 @@ impl<'a> DiffMachine<'a> {
self.diff_children(eold.children, enew.children);
}
(VNode::Component(_cold), VNode::Component(_cnew)) => {
// if cold.comp != cnew.comp {
// // queue an event to mount this new component
(VNode::Component(cold), VNode::Component(cnew)) => {
todo!("should not happen")
// if cold.caller_ref != cnew.caller_ref {
// // todo: create a stable addr
// self.lifecycle_events.push_back(LifeCycleEvent::Mount);
// return;
// }
// compare props.... somehow.....
// let comparator = &cnew.comparator.0;
// let old_props = cold.raw_props.as_ref();
// let has_changed = comparator(old_props);
todo!("Usage of component VNode not currently supported");
// if has_changed {
// self.lifecycle_events
// .push_back(LifeCycleEvent::PropsChanged);
// return;
// }
// the component is the same and hasn't changed
// migrate props over (so the addr remains stable) but don't rerun the component
}
// todo: knock out any listeners
(_, VNode::Component(_new)) => {
// self.lifecycle_events.push_back(LifeCycleEvent::Mount);
// we have no stable reference to work from
// push the lifecycle event onto the queue
// self.lifecycle_events
@@ -151,6 +178,100 @@ impl<'a> DiffMachine<'a> {
}
}
// Emit instructions to create the given virtual node.
//
// The change list stack may have any shape upon entering this function:
//
// [...]
//
// When this function returns, the new node is on top of the change list stack:
//
// [... node]
fn create(&mut self, node: &VNode<'a>) {
debug_assert!(self.change_list.traversal_is_committed());
match node {
VNode::Text(VText { text }) => {
self.change_list.create_text_node(text);
}
VNode::Element(&VElement {
key: _,
tag_name,
listeners,
attributes,
children,
namespace,
}) => {
// log::info!("Creating {:#?}", node);
if let Some(namespace) = namespace {
self.change_list.create_element_ns(tag_name, namespace);
} else {
self.change_list.create_element(tag_name);
}
listeners.iter().enumerate().for_each(|(_id, listener)| {
self.change_list
.new_event_listener(listener.event, listener.scope, listener.id)
});
for attr in attributes {
self.change_list
.set_attribute(&attr.name, &attr.value, namespace.is_some());
}
// Fast path: if there is a single text child, it is faster to
// create-and-append the text node all at once via setting the
// parent's `textContent` in a single change list instruction than
// to emit three instructions to (1) create a text node, (2) set its
// text content, and finally (3) append the text node to this
// parent.
if children.len() == 1 {
if let VNode::Text(VText { text }) = children[0] {
self.change_list.set_text(text);
return;
}
}
for child in children {
self.create(child);
self.change_list.append_child();
}
}
/*
todo: integrate re-entrace
*/
// NodeKind::Cached(ref c) => {
// cached_roots.insert(c.id);
// let (node, template) = cached_set.get(c.id);
// if let Some(template) = template {
// create_with_template(
// cached_set,
// self.change_list,
// registry,
// template,
// node,
// cached_roots,
// );
// } else {
// create(cached_set, change_list, registry, node, cached_roots);
// }
// }
VNode::Component(component) => {
self.change_list
.create_text_node("placeholder for vcomponent");
let id = uuid::Uuid::new_v4();
self.change_list.save_known_root(id);
self.lifecycle_events.push_back(LifeCycleEvent::Mount {
caller: component.caller.clone(),
id,
});
}
VNode::Suspended => {
todo!("Creation of VNode::Suspended not yet supported")
}
}
}
// Diff event listeners between `old` and `new`.
//
// The listeners' node must be on top of the change list stack:
@@ -787,93 +908,6 @@ impl<'a> DiffMachine<'a> {
// Support methods
// ======================
// Emit instructions to create the given virtual node.
//
// The change list stack may have any shape upon entering this function:
//
// [...]
//
// When this function returns, the new node is on top of the change list stack:
//
// [... node]
fn create(&mut self, node: &VNode<'a>) {
debug_assert!(self.change_list.traversal_is_committed());
match node {
VNode::Text(VText { text }) => {
self.change_list.create_text_node(text);
}
VNode::Element(&VElement {
key: _,
tag_name,
listeners,
attributes,
children,
namespace,
}) => {
// log::info!("Creating {:#?}", node);
if let Some(namespace) = namespace {
self.change_list.create_element_ns(tag_name, namespace);
} else {
self.change_list.create_element(tag_name);
}
listeners.iter().enumerate().for_each(|(_id, listener)| {
self.change_list
.new_event_listener(listener.event, listener.scope, listener.id)
});
for attr in attributes {
self.change_list
.set_attribute(&attr.name, &attr.value, namespace.is_some());
}
// Fast path: if there is a single text child, it is faster to
// create-and-append the text node all at once via setting the
// parent's `textContent` in a single change list instruction than
// to emit three instructions to (1) create a text node, (2) set its
// text content, and finally (3) append the text node to this
// parent.
if children.len() == 1 {
if let VNode::Text(VText { text }) = children[0] {
self.change_list.set_text(text);
return;
}
}
for child in children {
self.create(child);
self.change_list.append_child();
}
}
/*
todo: integrate re-entrace
*/
// NodeKind::Cached(ref c) => {
// cached_roots.insert(c.id);
// let (node, template) = cached_set.get(c.id);
// if let Some(template) = template {
// create_with_template(
// cached_set,
// self.change_list,
// registry,
// template,
// node,
// cached_roots,
// );
// } else {
// create(cached_set, change_list, registry, node, cached_roots);
// }
// }
VNode::Suspended => {
todo!("Creation of VNode::Suspended not yet supported")
}
VNode::Component(_) => {
todo!("Creation of VNode::Component not yet supported")
}
}
}
// Remove all of a node's children.
//
// The change list stack must have this shape upon entry to this function: