feat: integrate serialization and string borrowing
This commit adds lifetimes to the diff and realdom methods so consumers may borrow the contents of the DOM for serialization or asynchronous modifications.
This commit is contained in:
1 parent
9813f23cdf
commit
f4fb5bb454
12 files changed
+480
-86
No files matched your search
@@ -21,6 +21,75 @@ enum MutStatus {
|
||||
Mut,
|
||||
}
|
||||
|
||||
// impl ScopeArenaInner {
|
||||
// pub fn new(arena: Arena<Scope>) -> Self {
|
||||
// ScopeArenaInner {
|
||||
// arena: UnsafeCell::new(arena),
|
||||
// locks: Default::default(),
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// THIS METHOD IS CURRENTLY UNSAFE
|
||||
// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
// pub fn try_get(&self, idx: ScopeIdx) -> Result<&Scope> {
|
||||
// let inner = unsafe { &*self.arena.get() };
|
||||
// let scope = inner.get(idx);
|
||||
// scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
// }
|
||||
|
||||
// /// THIS METHOD IS CURRENTLY UNSAFE
|
||||
// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
// pub fn try_get_mut(&self, idx: ScopeIdx) -> Result<&mut Scope> {
|
||||
// let inner = unsafe { &mut *self.arena.get() };
|
||||
// let scope = inner.get_mut(idx);
|
||||
// scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
// }
|
||||
|
||||
// fn inner(&self) -> &Arena<Scope> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// fn inner_mut(&mut self) -> &mut Arena<Scope> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// /// THIS METHOD IS CURRENTLY UNSAFE
|
||||
// /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
|
||||
// pub fn with<T>(&self, f: impl FnOnce(&mut Arena<Scope>) -> T) -> Result<T> {
|
||||
// let inner = unsafe { &mut *self.arena.get() };
|
||||
// Ok(f(inner))
|
||||
// // todo!()
|
||||
// }
|
||||
|
||||
// pub fn with_scope<'b, O: 'static>(
|
||||
// &'b self,
|
||||
// id: ScopeIdx,
|
||||
// f: impl FnOnce(&'b mut Scope) -> O,
|
||||
// ) -> Result<O> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// // return a bumpframe with a lifetime attached to the arena borrow
|
||||
// // this is useful for merging lifetimes
|
||||
// pub fn with_scope_vnode<'b>(
|
||||
// &self,
|
||||
// id: ScopeIdx,
|
||||
// f: impl FnOnce(&mut Scope) -> &VNode<'b>,
|
||||
// ) -> Result<&VNode<'b>> {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
// pub fn try_remove(&mut self, id: ScopeIdx) -> Result<Scope> {
|
||||
// let inner = unsafe { &mut *self.arena.get() };
|
||||
// inner
|
||||
// .remove(id)
|
||||
// .ok_or_else(|| Error::FatalInternal("Scope not found"))
|
||||
// }
|
||||
|
||||
// unsafe fn inner_unchecked<'s>() -> &'s mut Arena<Scope> {
|
||||
// todo!()
|
||||
// }
|
||||
// }
|
||||
impl ScopeArena {
|
||||
pub fn new(arena: Arena<Scope>) -> Self {
|
||||
ScopeArena(Rc::new(RefCell::new(ScopeArenaInner {
|
||||
@@ -79,7 +148,7 @@ impl ScopeArena {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn try_remove(&mut self, id: ScopeIdx) -> Result<Scope> {
|
||||
pub fn try_remove(&self, id: ScopeIdx) -> Result<Scope> {
|
||||
let inner = unsafe { &mut *self.0.borrow().arena.get() };
|
||||
inner
|
||||
.remove(id)
|
||||
|
||||
@@ -38,9 +38,9 @@ impl DebugRenderer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn step<Dom: RealDom>(&mut self, machine: &mut DiffMachine<Dom>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
// pub fn step<Dom: RealDom>(&mut self, machine: &mut DiffMachine<Dom>) -> Result<()> {
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// this does a "holy" compare - if something is missing in the rhs, it doesn't complain.
|
||||
// it only complains if something shows up that's not in the lhs, *or* if a value is different.
|
||||
|
||||
+42
-35
@@ -22,13 +22,17 @@
|
||||
//! 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::{arena::ScopeArena, innerlude::*};
|
||||
use crate::{
|
||||
arena::{ScopeArena, ScopeArenaInner},
|
||||
innerlude::*,
|
||||
};
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::Cell,
|
||||
cmp::Ordering,
|
||||
marker::PhantomData,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
@@ -42,7 +46,7 @@ use std::{
|
||||
/// will receive modifications. However, instead of using child-based methods for descending through the tree, we instead
|
||||
/// ask the RealDom to either push or pop real nodes onto the stack. This saves us the indexing cost while working on a
|
||||
/// single node
|
||||
pub trait RealDom {
|
||||
pub trait RealDom<'a> {
|
||||
// Navigation
|
||||
fn push_root(&mut self, root: RealDomNode);
|
||||
|
||||
@@ -55,27 +59,27 @@ pub trait RealDom {
|
||||
fn remove_all_children(&mut self);
|
||||
|
||||
// Create
|
||||
fn create_text_node(&mut self, text: &str) -> RealDomNode;
|
||||
fn create_element(&mut self, tag: &str) -> RealDomNode;
|
||||
fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode;
|
||||
fn create_text_node(&mut self, text: &'a str) -> RealDomNode;
|
||||
fn create_element(&mut self, tag: &'static str) -> RealDomNode;
|
||||
fn create_element_ns(&mut self, tag: &'static str, namespace: &'static str) -> RealDomNode;
|
||||
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
|
||||
fn create_placeholder(&mut self) -> RealDomNode;
|
||||
|
||||
// events
|
||||
fn new_event_listener(
|
||||
&mut self,
|
||||
event: &str,
|
||||
event: &'static str,
|
||||
scope: ScopeIdx,
|
||||
element_id: usize,
|
||||
realnode: RealDomNode,
|
||||
);
|
||||
// fn new_event_listener(&mut self, event: &str);
|
||||
fn remove_event_listener(&mut self, event: &str);
|
||||
fn remove_event_listener(&mut self, event: &'static str);
|
||||
|
||||
// modify
|
||||
fn set_text(&mut self, text: &str);
|
||||
fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool);
|
||||
fn remove_attribute(&mut self, name: &str);
|
||||
fn set_text(&mut self, text: &'a str);
|
||||
fn set_attribute(&mut self, name: &'static str, value: &'a str, is_namespaced: bool);
|
||||
fn remove_attribute(&mut self, name: &'static str);
|
||||
|
||||
// node ref
|
||||
fn raw_node_as_any_mut(&self) -> &mut dyn Any;
|
||||
@@ -93,19 +97,19 @@ pub trait RealDom {
|
||||
/// 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 were both updated via
|
||||
/// subscriptions and props changes.
|
||||
pub struct DiffMachine<'a, Dom: RealDom> {
|
||||
pub dom: &'a mut Dom,
|
||||
pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> {
|
||||
pub dom: &'real mut Dom,
|
||||
pub components: &'bump ScopeArena,
|
||||
pub cur_idx: ScopeIdx,
|
||||
pub diffed: FxHashSet<ScopeIdx>,
|
||||
pub components: ScopeArena,
|
||||
pub event_queue: EventQueue,
|
||||
pub seen_nodes: FxHashSet<ScopeIdx>,
|
||||
}
|
||||
|
||||
impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> {
|
||||
pub fn new(
|
||||
dom: &'a mut Dom,
|
||||
components: ScopeArena,
|
||||
dom: &'real mut Dom,
|
||||
components: &'bump ScopeArena,
|
||||
cur_idx: ScopeIdx,
|
||||
event_queue: EventQueue,
|
||||
) -> Self {
|
||||
@@ -128,8 +132,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
//
|
||||
// The change list stack is in the same state when this function exits.
|
||||
// In the case of Fragments, the parent node is on the stack
|
||||
pub fn diff_node(&mut self, old_node: &VNode<'a>, new_node: &VNode<'a>) {
|
||||
// pub fn diff_node(&self, old: &VNode<'a>, new: &VNode<'a>) {
|
||||
pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
|
||||
/*
|
||||
For each valid case, we "commit traversal", meaning we save this current position in the tree.
|
||||
Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
|
||||
@@ -326,7 +329,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// When this function returns, the new node is on top of the change list stack:
|
||||
//
|
||||
// [... node]
|
||||
fn create(&mut self, node: &VNode<'a>) {
|
||||
fn create(&mut self, node: &'bump VNode<'bump>) {
|
||||
// debug_assert!(self.dom.traversal_is_committed());
|
||||
match node {
|
||||
VNode::Text(text) => {
|
||||
@@ -434,7 +437,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
|
||||
// yaaaaay lifetimes out of thin air
|
||||
// really tho, we're merging the frame lifetimes together
|
||||
let inner: &'a mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
|
||||
let inner: &'bump mut _ = unsafe { &mut *self.components.0.borrow().arena.get() };
|
||||
let new_component = inner.get_mut(idx).unwrap();
|
||||
|
||||
// Actually initialize the caller's slot with the right address
|
||||
@@ -467,7 +470,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
|
||||
/// Destroy a scope and all of its descendents.
|
||||
///
|
||||
/// Calling this will run the destuctors on all hooks in the tree.
|
||||
@@ -559,8 +562,8 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// The change list stack is left unchanged.
|
||||
fn diff_attr(
|
||||
&mut self,
|
||||
old: &'a [Attribute<'a>],
|
||||
new: &'a [Attribute<'a>],
|
||||
old: &'bump [Attribute<'bump>],
|
||||
new: &'bump [Attribute<'bump>],
|
||||
is_namespaced: bool,
|
||||
) {
|
||||
// Do O(n^2) passes to add/update and remove attributes, since
|
||||
@@ -613,7 +616,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// [... parent]
|
||||
//
|
||||
// the change list stack is in the same state when this function returns.
|
||||
fn diff_children(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
||||
fn diff_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||
if new.is_empty() {
|
||||
if !old.is_empty() {
|
||||
// self.dom.commit_traversal();
|
||||
@@ -693,11 +696,11 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// [... parent]
|
||||
//
|
||||
// Upon exiting, the change list stack is in the same state.
|
||||
fn diff_keyed_children(&self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
||||
fn diff_keyed_children(&self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||
// todo!();
|
||||
if cfg!(debug_assertions) {
|
||||
let mut keys = fxhash::FxHashSet::default();
|
||||
let mut assert_unique_keys = |children: &'a [VNode<'a>]| {
|
||||
let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| {
|
||||
keys.clear();
|
||||
for child in children {
|
||||
let key = child.key();
|
||||
@@ -777,7 +780,11 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// [... parent]
|
||||
//
|
||||
// Upon exit, the change list stack is the same.
|
||||
fn diff_keyed_prefix(&self, old: &[VNode<'a>], new: &[VNode<'a>]) -> KeyedPrefixResult {
|
||||
fn diff_keyed_prefix(
|
||||
&self,
|
||||
old: &'bump [VNode<'bump>],
|
||||
new: &'bump [VNode<'bump>],
|
||||
) -> KeyedPrefixResult {
|
||||
todo!()
|
||||
// self.dom.go_down();
|
||||
// let mut shared_prefix_count = 0;
|
||||
@@ -831,8 +838,8 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// Upon exit from this function, it will be restored to that same state.
|
||||
fn diff_keyed_middle(
|
||||
&self,
|
||||
old: &[VNode<'a>],
|
||||
mut new: &[VNode<'a>],
|
||||
old: &[VNode<'bump>],
|
||||
mut new: &[VNode<'bump>],
|
||||
shared_prefix_count: usize,
|
||||
shared_suffix_count: usize,
|
||||
old_shared_suffix_start: usize,
|
||||
@@ -1059,8 +1066,8 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// When this function exits, the change list stack remains the same.
|
||||
fn diff_keyed_suffix(
|
||||
&self,
|
||||
old: &[VNode<'a>],
|
||||
new: &[VNode<'a>],
|
||||
old: &[VNode<'bump>],
|
||||
new: &[VNode<'bump>],
|
||||
new_shared_suffix_start: usize,
|
||||
) {
|
||||
todo!()
|
||||
@@ -1088,7 +1095,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// [... parent]
|
||||
//
|
||||
// the change list stack is in the same state when this function returns.
|
||||
fn diff_non_keyed_children(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
|
||||
fn diff_non_keyed_children(&mut self, old: &'bump [VNode<'bump>], new: &'bump [VNode<'bump>]) {
|
||||
// Handled these cases in `diff_children` before calling this function.
|
||||
debug_assert!(!new.is_empty());
|
||||
debug_assert!(!old.is_empty());
|
||||
@@ -1163,7 +1170,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// [... parent]
|
||||
//
|
||||
// When this function returns, the change list stack is in the same state.
|
||||
pub fn remove_all_children(&mut self, old: &[VNode<'a>]) {
|
||||
pub fn remove_all_children(&mut self, old: &'bump [VNode<'bump>]) {
|
||||
// debug_assert!(self.dom.traversal_is_committed());
|
||||
log::debug!("REMOVING CHILDREN");
|
||||
for _child in old {
|
||||
@@ -1182,7 +1189,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// [... parent]
|
||||
//
|
||||
// When this function returns, the change list stack is in the same state.
|
||||
pub fn create_and_append_children(&mut self, new: &[VNode<'a>]) {
|
||||
pub fn create_and_append_children(&mut self, new: &'bump [VNode<'bump>]) {
|
||||
// debug_assert!(self.dom.traversal_is_committed());
|
||||
for child in new {
|
||||
// self.create_and_append(node, parent)
|
||||
@@ -1200,7 +1207,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||
// After the function returns, the child is no longer on the change list stack:
|
||||
//
|
||||
// [... parent]
|
||||
pub fn remove_self_and_next_siblings(&self, old: &[VNode<'a>]) {
|
||||
pub fn remove_self_and_next_siblings(&self, old: &[VNode<'bump>]) {
|
||||
// debug_assert!(self.dom.traversal_is_committed());
|
||||
for child in old {
|
||||
if let VNode::Component(vcomp) = child {
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
//!
|
||||
//!
|
||||
|
||||
pub mod util;
|
||||
pub mod arena;
|
||||
pub mod component; // Logic for extending FC
|
||||
pub mod component;
|
||||
pub mod util; // Logic for extending FC
|
||||
|
||||
pub mod debug_renderer;
|
||||
pub mod diff;
|
||||
|
||||
@@ -8,7 +8,7 @@ impl DebugDom {
|
||||
Self { counter: 0 }
|
||||
}
|
||||
}
|
||||
impl RealDom for DebugDom {
|
||||
impl<'a> RealDom<'a> for DebugDom {
|
||||
fn push_root(&mut self, root: RealDomNode) {}
|
||||
|
||||
fn append_child(&mut self) {}
|
||||
|
||||
@@ -196,10 +196,10 @@ impl VirtualDom {
|
||||
impl VirtualDom {
|
||||
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
|
||||
/// Currently this doesn't do what we want it to do
|
||||
pub fn rebuild<'s, Dom: RealDom>(&'s mut self, realdom: &mut Dom) -> Result<()> {
|
||||
pub fn rebuild<'s, Dom: RealDom<'s>>(&'s mut self, realdom: &mut Dom) -> Result<()> {
|
||||
let mut diff_machine = DiffMachine::new(
|
||||
realdom,
|
||||
self.components.clone(),
|
||||
&self.components,
|
||||
self.base_scope,
|
||||
self.event_queue.clone(),
|
||||
);
|
||||
@@ -207,7 +207,6 @@ impl VirtualDom {
|
||||
// Schedule an update and then immediately call it on the root component
|
||||
// This is akin to a hook being called from a listener and requring a re-render
|
||||
// Instead, this is done on top-level component
|
||||
|
||||
let base = self.components.try_get(self.base_scope)?;
|
||||
|
||||
let update = &base.event_channel;
|
||||
@@ -260,21 +259,17 @@ impl VirtualDom {
|
||||
// but the guarantees provide a safe, fast, and efficient abstraction for the VirtualDOM updating framework.
|
||||
//
|
||||
// A good project would be to remove all unsafe from this crate and move the unsafety into safer abstractions.
|
||||
pub fn progress_with_event<Dom: RealDom>(
|
||||
&mut self,
|
||||
realdom: &mut Dom,
|
||||
pub fn progress_with_event<'s, Dom: RealDom<'s>>(
|
||||
&'s mut self,
|
||||
realdom: &'_ mut Dom,
|
||||
trigger: EventTrigger,
|
||||
) -> Result<()> {
|
||||
let id = trigger.component_id.clone();
|
||||
|
||||
self.components.try_get_mut(id)?.call_listener(trigger)?;
|
||||
|
||||
let mut diff_machine = DiffMachine::new(
|
||||
realdom,
|
||||
self.components.clone(),
|
||||
id,
|
||||
self.event_queue.clone(),
|
||||
);
|
||||
let mut diff_machine =
|
||||
DiffMachine::new(realdom, &self.components, id, self.event_queue.clone());
|
||||
|
||||
self.progress_completely(&mut diff_machine)?;
|
||||
|
||||
@@ -285,9 +280,9 @@ impl VirtualDom {
|
||||
/// Only ever run each component once.
|
||||
///
|
||||
/// The DiffMachine logs its progress as it goes which might be useful for certain types of renderers.
|
||||
pub(crate) fn progress_completely<'s, Dom: RealDom>(
|
||||
&'s mut self,
|
||||
diff_machine: &'_ mut DiffMachine<'s, Dom>,
|
||||
pub(crate) fn progress_completely<'a, 'bump, Dom: RealDom<'bump>>(
|
||||
&'bump self,
|
||||
diff_machine: &'_ mut DiffMachine<'a, 'bump, Dom>,
|
||||
) -> Result<()> {
|
||||
// Add this component to the list of components that need to be difed
|
||||
// #[allow(unused_assignments)]
|
||||
|
||||
Reference in new issue
Block a user