Feat: major overhaul to diffing, using a "diffing machine" now

This commit is contained in:
Jonathan Kelley committed 2021-02-14 18:03:16 -05:00
1 parent 4c291a0efd
commit 4dfdf91236
10 files changed
+822 -780

No files matched your search

+114 -91
View File
@@ -21,50 +21,50 @@
use crate::innerlude::{Listener, VirtualDom};
/// Renderers need to implement the interpreter trait
/// The `Edit` represents a single modifcation of the renderer tree.
///
///
///
///
///
trait Inrerpreter {
fn set_text(&mut self, text: &str);
fn remove_self_and_next_siblings(&mut self);
fn replace_with(&mut self);
fn set_attribute(&mut self, name: &str, value: &str);
fn remove_attribute(&mut self, name: &str);
fn push_reverse_child(&mut self, n: u32);
fn pop_push_child(&mut self, n: u32);
fn pop(&mut self);
fn append_child(&mut self);
fn create_text_node(&mut self, text: &str);
fn create_element(&mut self, tag_name: &str);
fn new_event_listener(&mut self, event_type: &str, a: u32, b: u32);
fn update_event_listener(&mut self, event_type: &str, a: u32, b: u32);
fn remove_event_listener(&mut self, event_type: &str);
fn create_element_ns(&mut self, tag_name: &str, ns: &str);
fn save_children_to_temporaries(&mut self, temp: u32, start: u32, end: u32);
// fn save_children_to_temporaries(&mut self, mut temp: u32, start: u32, end: u32);
fn push_child(&mut self, n: u32);
fn push_temporary(&mut self, temp: u32);
fn insert_before(&mut self);
fn pop_push_reverse_child(&mut self, n: u32);
fn remove_child(&mut self, n: u32);
fn set_class(&mut self, class_name: &str);
// fn save_template(&mut self, id: CacheId);
// fn push_template(&mut self, id: CacheId);
///
///
///
///
pub enum Edit<'d> {
SetText { text: &'d str },
RemoveSelfAndNextSiblings {},
ReplaceWith,
SetAttribute { name: &'d str, value: &'d str },
RemoveAttribute { name: &'d str },
PushReverseChild { n: u32 },
PopPushChild { n: u32 },
Pop,
AppendChild,
CreateTextNode { text: &'d str },
CreateElement { tag_name: &'d str },
NewEventListener { event_type: &'d str, a: u32, b: u32 },
UpdateEventListener { event_type: &'d str, a: u32, b: u32 },
RemoveEventListener { event_type: &'d str },
CreateElementNs { tag_name: &'d str, ns: &'d str },
SaveChildrenToTemporaries { temp: u32, start: u32, end: u32 },
PushChild { n: u32 },
PushTemporary { temp: u32 },
InsertBefore,
PopPushReverseChild { n: u32 },
RemoveChild { n: u32 },
SetClass { class_name: &'d str },
}
pub struct ChangeList<'src> {
pub struct EditList<'src> {
traversal: Traversal,
next_temporary: u32,
forcing_new_listeners: bool,
emitter: &'src mut dyn Inrerpreter,
domlock: &'src VirtualDom,
emitter: Vec<Edit<'src>>,
}
/// Traversal methods.
impl ChangeList<'_> {
impl EditList<'_> {
pub fn go_down(&mut self) {
self.traversal.down();
}
@@ -102,32 +102,36 @@ impl ChangeList<'_> {
}
for mv in self.traversal.commit() {
// match mv {
// MoveTo::Parent => {
// // debug!("emit: pop");
// self.emitter.pop();
// }
// MoveTo::Child(n) => {
// // debug!("emit: push_child({})", n);
// self.emitter.push_child(n);
// }
// MoveTo::ReverseChild(n) => {
// // debug!("emit: push_reverse_child({})", n);
// self.emitter.push_reverse_child(n);
// }
// MoveTo::Sibling(n) => {
// // debug!("emit: pop_push_child({})", n);
// self.emitter.pop_push_child(n);
// }
// MoveTo::ReverseSibling(n) => {
// // debug!("emit: pop_push_reverse_child({})", n);
// self.emitter.pop_push_reverse_child(n);
// }
// MoveTo::TempChild(temp) => {
// // debug!("emit: push_temporary({})", temp);
// self.emitter.push_temporary(temp);
// }
// }
match mv {
MoveTo::Parent => {
// debug!("emit: pop");
self.emitter.push(Edit::Pop {});
// self.emitter.pop();
}
MoveTo::Child(n) => {
// debug!("emit: push_child({})", n);
self.emitter.push(Edit::PushChild { n });
}
MoveTo::ReverseChild(n) => {
// debug!("emit: push_reverse_child({})", n);
self.emitter.push(Edit::PushReverseChild { n });
// self.emitter.push_reverse_child(n);
}
MoveTo::Sibling(n) => {
// debug!("emit: pop_push_child({})", n);
self.emitter.push(Edit::PopPushChild { n });
// self.emitter.pop_push_child(n);
}
MoveTo::ReverseSibling(n) => {
// debug!("emit: pop_push_reverse_child({})", n);
self.emitter.push(Edit::PopPushReverseChild { n });
}
MoveTo::TempChild(temp) => {
// debug!("emit: push_temporary({})", temp);
self.emitter.push(Edit::PushTemporary { temp });
// self.emitter.push_temporary(temp);
}
}
}
}
@@ -136,7 +140,7 @@ impl ChangeList<'_> {
}
}
impl ChangeList<'_> {
impl<'a> EditList<'a> {
pub fn next_temporary(&self) -> u32 {
self.next_temporary
}
@@ -154,27 +158,33 @@ impl ChangeList<'_> {
// temp_base, start, end
// );
self.next_temporary = temp_base + (end - start) as u32;
self.emitter
.save_children_to_temporaries(temp_base, start as u32, end as u32);
self.emitter.push(Edit::SaveChildrenToTemporaries {
temp: temp_base,
start: start as u32,
end: end as u32,
});
temp_base
}
pub fn push_temporary(&mut self, temp: u32) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: push_temporary({})", temp);
self.emitter.push_temporary(temp);
self.emitter.push(Edit::PushTemporary { temp });
// self.emitter.push_temporary(temp);
}
pub fn remove_child(&mut self, child: usize) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: remove_child({})", child);
self.emitter.remove_child(child as u32);
// self.emitter.remove_child(child as u32);
self.emitter.push(Edit::RemoveChild { n: child as u32 })
}
pub fn insert_before(&mut self) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: insert_before()");
self.emitter.insert_before();
// self.emitter.insert_before();
self.emitter.push(Edit::InsertBefore {})
}
pub fn ensure_string(&mut self, string: &str) -> StringKey {
@@ -182,45 +192,52 @@ impl ChangeList<'_> {
// self.strings.ensure_string(string, &self.emitter)
}
pub fn set_text(&mut self, text: &str) {
pub fn set_text(&mut self, text: &'a str) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: set_text({:?})", text);
self.emitter.set_text(text);
// self.emitter.set_text(text);
self.emitter.push(Edit::SetText { text });
// .set_text(text.as_ptr() as u32, text.len() as u32);
}
pub fn remove_self_and_next_siblings(&mut self) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: remove_self_and_next_siblings()");
self.emitter.remove_self_and_next_siblings();
self.emitter.push(Edit::RemoveSelfAndNextSiblings {});
// self.emitter.remove_self_and_next_siblings();
}
pub fn replace_with(&mut self) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: replace_with()");
self.emitter.replace_with();
self.emitter.push(Edit::ReplaceWith {});
// self.emitter.replace_with();
}
pub fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {
pub fn set_attribute(&mut self, name: &'a str, value: &'a str, is_namespaced: bool) {
debug_assert!(self.traversal_is_committed());
todo!()
// if name == "class" && !is_namespaced {
// let class_id = self.ensure_string(value);
// // debug!("emit: set_class({:?})", value);
// self.emitter.set_class(class_id.into());
// } else {
// let name_id = self.ensure_string(name);
// let value_id = self.ensure_string(value);
// // debug!("emit: set_attribute({:?}, {:?})", name, value);
// self.state
// .emitter
// .set_attribute(name_id.into(), value_id.into());
// }
// todo!()
if name == "class" && !is_namespaced {
// let class_id = self.ensure_string(value);
// let class_id = self.ensure_string(value);
// debug!("emit: set_class({:?})", value);
// self.emitter.set_class(class_id.into());
self.emitter.push(Edit::SetClass { class_name: value });
} else {
self.emitter.push(Edit::SetAttribute { name, value });
// let name_id = self.ensure_string(name);
// let value_id = self.ensure_string(value);
// debug!("emit: set_attribute({:?}, {:?})", name, value);
// self.state
// .emitter
// .set_attribute(name_id.into(), value_id.into());
}
}
pub fn remove_attribute(&mut self, name: &str) {
pub fn remove_attribute(&mut self, name: &'a str) {
// todo!("figure out how to get this working with ensure string");
self.emitter.remove_attribute(name);
self.emitter.push(Edit::RemoveAttribute { name });
// self.emitter.remove_attribute(name);
// debug_assert!(self.traversal_is_committed());
// // debug!("emit: remove_attribute({:?})", name);
// let name_id = self.ensure_string(name);
@@ -230,29 +247,33 @@ impl ChangeList<'_> {
pub fn append_child(&mut self) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: append_child()");
self.emitter.append_child();
self.emitter.push(Edit::AppendChild {});
// self.emitter.append_child();
}
pub fn create_text_node(&mut self, text: &str) {
pub fn create_text_node(&mut self, text: &'a str) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: create_text_node({:?})", text);
self.emitter.create_text_node(text);
// self.emitter.create_text_node(text);
self.emitter.push(Edit::CreateTextNode { text });
}
pub fn create_element(&mut self, tag_name: &str) {
pub fn create_element(&mut self, tag_name: &'a str) {
// debug_assert!(self.traversal_is_committed());
// debug!("emit: create_element({:?})", tag_name);
// let tag_name_id = self.ensure_string(tag_name);
self.emitter.create_element(tag_name);
self.emitter.push(Edit::CreateElement { tag_name });
// self.emitter.create_element(tag_name);
// self.emitter.create_element(tag_name_id.into());
}
pub fn create_element_ns(&mut self, tag_name: &str, ns: &str) {
pub fn create_element_ns(&mut self, tag_name: &'a str, ns: &'a str) {
debug_assert!(self.traversal_is_committed());
// debug!("emit: create_element_ns({:?}, {:?})", tag_name, ns);
// let tag_name_id = self.ensure_string(tag_name);
// let ns_id = self.ensure_string(ns);
self.emitter.create_element_ns(tag_name, ns);
// self.emitter.create_element_ns(tag_name, ns);
self.emitter.push(Edit::CreateElementNs { tag_name, ns });
// self.emitter
// .create_element_ns(tag_name_id.into(), ns_id.into());
}
@@ -455,7 +476,9 @@ impl Traversal {
/// that have *not* been committed yet?
#[inline]
pub fn is_committed(&self) -> bool {
self.uncommitted.is_empty()
// is_empty is not inlined?
// self.uncommitted.is_empty()
self.uncommitted.len() == 0
}
/// Commit this traversals moves and return the optimized path from the last
+598 -646
View File
File diff suppressed because it is too large. Load diff
+13 -13
View File
@@ -65,19 +65,19 @@
//! - dioxus-liveview (SSR + StringRenderer)
//!
pub mod changelist;
pub mod component;
pub mod context;
pub mod debug_renderer;
pub mod diff;
pub mod error;
pub mod events;
pub mod hooks;
pub mod nodebuilder;
pub mod nodes;
pub mod scope;
pub mod validation;
pub mod virtual_dom;
pub mod changelist; // An "edit phase" described by transitions and edit operations
pub mod component; // Logic for extending FC
pub mod context; // Logic for providing hook + context functionality to user components
pub mod debug_renderer; // Test harness for validating that lifecycles and diffs work appropriately
pub mod diff; // The diffing algorithm that builds the ChangeList
pub mod error; // Error type we expose to the renderers
pub mod events; // Manages the synthetic event API
pub mod hooks; // Built-in hooks
pub mod nodebuilder; // Logic for building VNodes with a direct syntax
pub mod nodes; // Logic for the VNodes
pub mod scope; // Logic for single components
pub mod validation; // Logic for validating trees
pub mod virtual_dom; // Most fun logic starts here, manages the lifecycle and suspense
pub mod builder {
pub use super::nodebuilder::*;
+37 -9
View File
@@ -1,11 +1,11 @@
use crate::context::hooks::Hook;
use crate::innerlude::*;
use crate::nodes::VNode;
use crate::{context::hooks::Hook, diff::diff};
use bumpalo::Bump;
use generational_arena::Index;
use std::{
any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData,
sync::atomic::AtomicUsize,
sync::atomic::AtomicUsize, todo,
};
/// Every component in Dioxus is represented by a `Scope`.
@@ -29,9 +29,13 @@ pub struct Scope {
pub frames: [Bump; 2],
// somehow build this vnode with a lifetime tied to self
pub cur_node: *mut VNode<'static>,
// This root node has "static" lifetime, but it's really not static.
// It's goverened by the oldest of the two frames and is switched every time a new render occurs
// Use this node as if it were static is unsafe, and needs to be fixed with ourborous or owning ref
// ! do not copy this reference are things WILL break !
pub root_node: *mut VNode<'static>,
pub active_frame: u8,
pub active_frame: ActiveFrame,
// IE Which listeners need to be woken up?
pub listeners: Vec<Box<dyn Fn()>>,
@@ -41,6 +45,20 @@ pub struct Scope {
pub caller: *const i32,
}
pub enum ActiveFrame {
First,
Second,
}
impl ActiveFrame {
fn next(&mut self) {
match self {
ActiveFrame::First => *self = ActiveFrame::Second,
ActiveFrame::Second => *self = ActiveFrame::First,
}
}
}
impl Scope {
// create a new scope from a function
pub(crate) fn new<T: 'static>(f: FC<T>, parent: Option<Index>) -> Self {
@@ -49,15 +67,19 @@ impl Scope {
let hook_arena = typed_arena::Arena::new();
let hooks = RefCell::new(Vec::new());
// Capture the caller
let caller = f as *const i32;
// Create the two buffers the componetn will render into
// There will always be an "old" and "new"
let frames = [Bump::new(), Bump::new()];
let listeners = Vec::new();
let active_frame = 1;
let active_frame = ActiveFrame::First;
let new = frames[0].alloc(VNode::Text(VText::new("")));
let cur_node = new as *mut _;
Self {
@@ -69,7 +91,7 @@ impl Scope {
listeners,
parent,
frames,
cur_node,
root_node: cur_node,
}
}
@@ -78,7 +100,12 @@ impl Scope {
///
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
pub(crate) fn run<'a, P: Properties + ?Sized>(&self, props: &'a P) {
let bump = &self.frames[0];
let bump = match self.active_frame {
// If the active frame is the first, then we need to bump into the second
ActiveFrame::First => &self.frames[1],
// If the active frame is the second, then we need to bump into the first
ActiveFrame::Second => &self.frames[0],
}; // n.b, there might be a better way of doing this active frame stuff - perhaps swapping
let ctx = Context {
scope: &*self,
@@ -102,13 +129,14 @@ impl Scope {
*/
let caller = unsafe { std::mem::transmute::<*const i32, FC<P>>(self.caller) };
let new_nodes = caller(ctx, props);
let old_nodes: &mut VNode<'static> = unsafe { &mut *self.cur_node };
let old_nodes: &mut VNode<'static> = unsafe { &mut *self.root_node };
// TODO: Iterate through the new nodes
// move any listeners into ourself
// perform the diff, dumping into the mutable change list
// this doesnt perform any "diff compression" where an event and a re-render
crate::diff::diff(old_nodes, &new_nodes);
// crate::diff::diff(old_nodes, &new_nodes);
todo!()
}
}
+2 -9
View File
@@ -1,4 +1,4 @@
use crate::{changelist::ChangeList, nodes::VNode};
use crate::{changelist::EditList, nodes::VNode};
use crate::{events::EventTrigger, innerlude::*};
use any::Any;
use bumpalo::Bump;
@@ -23,10 +23,6 @@ pub struct VirtualDom {
/// The index of the root component.
base_scope: Index,
///
///
///
///
event_queue: Rc<RefCell<Vec<LifecycleEvent>>>,
// Mark the root props with P, even though they're held by the root component
@@ -45,10 +41,7 @@ impl VirtualDom {
pub fn new(root: FC<()>) -> Self {
Self::new_with_props(root, ())
}
}
impl VirtualDom {
// impl<P: Properties + 'static> VirtualDom<P> {
/// Start a new VirtualDom instance with a dependent props.
/// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
///
@@ -128,7 +121,7 @@ impl VirtualDom {
///
///
/// ```
pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<ChangeList<'_>> {
pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<EditList<'_>> {
let EventTrigger {
component_id,
listener_id,