Feat: major overhaul to diffing, using a "diffing machine" now
This commit is contained in:
1 parent
4c291a0efd
commit
4dfdf91236
10 files changed
+822
-780
No files matched your search
+114
-91
@@ -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
File diff suppressed because it is too large.
Load diff
+13
-13
@@ -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::*;
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user