123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751 |
- //! Changelist
- //! ----------
- //!
- //! This module exposes the "changelist" object which allows 3rd party implementors to handle diffs to the virtual dom.
- //!
- //! # Design
- //! ---
- //! In essence, the changelist object connects a diff of two vdoms to the actual edits required to update the output renderer.
- //!
- //! This abstraction relies on the assumption that the final renderer accepts a tree of elements. For most target platforms,
- //! this is an appropriate abstraction .
- //!
- //! During the diff phase, the change list is built. Once the diff phase is over, the change list is finished and returned back
- //! to the renderer. The renderer is responsible for propogating the updates to the final display.
- //!
- //! Because the change list references data internal to the vdom, it needs to be consumed by the renderer before the vdom
- //! can continue to work. This means once a change list is generated, it should be consumed as fast as possible, otherwise the
- //! dom will be blocked from progressing. This is enforced by lifetimes on the returend changelist object.
- //!
- //!
- use bumpalo::Bump;
- use crate::innerlude::{Listener, ScopeIdx};
- use serde::{Deserialize, Serialize};
- /// The `Edit` represents a single modifcation of the renderer tree.
- /// todo@ jon: allow serde to be optional
- #[derive(Debug, Serialize, Deserialize)]
- #[serde(tag = "type")]
- 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,
- },
- 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,
- },
- // push a known node on to the stack
- TraverseToKnown {
- node: ScopeIdx,
- },
- // Add the current top of the stack to the known nodes
- MakeKnown {
- node: ScopeIdx,
- },
- // Remove the current top of the stack from the known nodes
- RemoveKnown,
- NewListener {
- event: &'d str,
- scope: ScopeIdx,
- id: usize,
- },
- UpdateListener {
- event: &'d str,
- scope: ScopeIdx,
- id: usize,
- },
- RemoveListener {
- event: &'d str,
- },
- // NewListener { event: &'d str, id: usize, s: ScopeIdx },
- }
- pub type EditList<'src> = Vec<Edit<'src>>;
- pub struct EditMachine<'src> {
- pub traversal: Traversal,
- next_temporary: u32,
- forcing_new_listeners: bool,
- pub emitter: EditList<'src>,
- }
- impl<'b> EditMachine<'b> {
- pub fn new(_bump: &'b Bump) -> Self {
- Self {
- traversal: Traversal::new(),
- next_temporary: 0,
- forcing_new_listeners: false,
- emitter: EditList::default(),
- }
- }
- }
- // ===================================
- // Traversal Methods
- // ===================================
- impl<'b> EditMachine<'b> {
- pub fn go_down(&mut self) {
- self.traversal.down();
- }
- pub fn go_down_to_child(&mut self, index: usize) {
- self.traversal.down();
- self.traversal.sibling(index);
- }
- pub fn go_down_to_reverse_child(&mut self, index: usize) {
- self.traversal.down();
- self.traversal.reverse_sibling(index);
- }
- pub fn go_up(&mut self) {
- self.traversal.up();
- }
- pub fn go_to_sibling(&mut self, index: usize) {
- self.traversal.sibling(index);
- }
- pub fn go_to_temp_sibling(&mut self, temp: u32) {
- self.traversal.up();
- self.traversal.down_to_temp(temp);
- }
- pub fn go_down_to_temp_child(&mut self, temp: u32) {
- self.traversal.down_to_temp(temp);
- }
- pub fn commit_traversal(&mut self) {
- if self.traversal.is_committed() {
- log::debug!("Traversal already committed");
- return;
- }
- for mv in self.traversal.commit() {
- match mv {
- MoveTo::Parent => {
- log::debug!("emit: pop");
- self.emitter.push(Edit::Pop {});
- // self.emitter.pop();
- }
- MoveTo::Child(n) => {
- log::debug!("emit: push_child({})", n);
- self.emitter.push(Edit::PushChild { n });
- }
- MoveTo::ReverseChild(n) => {
- log::debug!("emit: push_reverse_child({})", n);
- self.emitter.push(Edit::PushReverseChild { n });
- // self.emitter.push_reverse_child(n);
- }
- MoveTo::Sibling(n) => {
- log::debug!("emit: pop_push_child({})", n);
- self.emitter.push(Edit::PopPushChild { n });
- // self.emitter.pop_push_child(n);
- }
- MoveTo::ReverseSibling(n) => {
- log::debug!("emit: pop_push_reverse_child({})", n);
- self.emitter.push(Edit::PopPushReverseChild { n });
- }
- MoveTo::TempChild(temp) => {
- log::debug!("emit: push_temporary({})", temp);
- self.emitter.push(Edit::PushTemporary { temp });
- // self.emitter.push_temporary(temp);
- }
- }
- }
- }
- pub fn traversal_is_committed(&self) -> bool {
- self.traversal.is_committed()
- }
- }
- // ===================================
- // Stack methods
- // ===================================
- impl<'a> EditMachine<'a> {
- pub fn next_temporary(&self) -> u32 {
- self.next_temporary
- }
- pub fn set_next_temporary(&mut self, next_temporary: u32) {
- self.next_temporary = next_temporary;
- }
- pub fn save_children_to_temporaries(&mut self, start: usize, end: usize) -> u32 {
- debug_assert!(self.traversal_is_committed());
- debug_assert!(start < end);
- let temp_base = self.next_temporary;
- // debug!(
- // "emit: save_children_to_temporaries({}, {}, {})",
- // temp_base, start, end
- // );
- self.next_temporary = temp_base + (end - start) 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(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.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.push(Edit::InsertBefore {})
- }
- 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.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.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.push(Edit::ReplaceWith {});
- // self.emitter.replace_with();
- }
- 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);
- // 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: &'a str) {
- // todo!("figure out how to get this working with ensure string");
- 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);
- // self.emitter.remove_attribute(name_id.into());
- }
- pub fn append_child(&mut self) {
- debug_assert!(self.traversal_is_committed());
- // debug!("emit: append_child()");
- self.emitter.push(Edit::AppendChild {});
- // self.emitter.append_child();
- }
- 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.push(Edit::CreateTextNode { text });
- }
- 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.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: &'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.push(Edit::CreateElementNs { tag_name, ns });
- // self.emitter
- // .create_element_ns(tag_name_id.into(), ns_id.into());
- }
- pub fn push_force_new_listeners(&mut self) -> bool {
- let old = self.forcing_new_listeners;
- self.forcing_new_listeners = true;
- old
- }
- pub fn pop_force_new_listeners(&mut self, previous: bool) {
- debug_assert!(self.forcing_new_listeners);
- self.forcing_new_listeners = previous;
- }
- pub fn new_event_listener(&mut self, event: &'a str, scope: ScopeIdx, id: usize) {
- debug_assert!(self.traversal_is_committed());
- self.emitter.push(Edit::NewListener { event, scope, id });
- // log::debug!("emit: new_event_listener({:?})", listener);
- }
- pub fn update_event_listener(&mut self, event: &'a str, scope: ScopeIdx, id: usize) {
- debug_assert!(self.traversal_is_committed());
- if self.forcing_new_listeners {
- self.new_event_listener(event, scope, id);
- return;
- }
- self.emitter.push(Edit::NewListener { event, scope, id });
- }
- pub fn remove_event_listener(&mut self, event: &'a str) {
- debug_assert!(self.traversal_is_committed());
- self.emitter.push(Edit::RemoveListener { event });
- // debug!("emit: remove_event_listener({:?})", event);
- }
- // pub fn save_template(&mut self, id: CacheId) {
- // debug_assert!(self.traversal_is_committed());
- // debug_assert!(!self.has_template(id));
- // // debug!("emit: save_template({:?})", id);
- // self.templates.insert(id);
- // self.emitter.save_template(id.into());
- // }
- // pub fn push_template(&mut self, id: CacheId) {
- // debug_assert!(self.traversal_is_committed());
- // debug_assert!(self.has_template(id));
- // // debug!("emit: push_template({:?})", id);
- // self.emitter.push_template(id.into());
- // }
- }
- // Keeps track of where we are moving in a DOM tree, and shortens traversal
- // paths between mutations to their minimal number of operations.
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- pub enum MoveTo {
- /// Move from the current node up to its parent.
- Parent,
- /// Move to the current node's n^th child.
- Child(u32),
- /// Move to the current node's n^th from last child.
- ReverseChild(u32),
- /// Move to the n^th sibling. Not relative from the current
- /// location. Absolute indexed within all of the current siblings.
- Sibling(u32),
- /// Move to the n^th from last sibling. Not relative from the current
- /// location. Absolute indexed within all of the current siblings.
- ReverseSibling(u32),
- /// Move down to the given saved temporary child.
- TempChild(u32),
- }
- #[derive(Debug)]
- pub struct Traversal {
- uncommitted: Vec<MoveTo>,
- }
- impl Traversal {
- /// Construct a new `Traversal` with its internal storage backed by the
- /// given bump arena.
- pub fn new() -> Traversal {
- Traversal {
- uncommitted: Vec::with_capacity(32),
- }
- }
- /// Move the traversal up in the tree.
- pub fn up(&mut self) {
- match self.uncommitted.last() {
- Some(MoveTo::Sibling(_)) | Some(MoveTo::ReverseSibling(_)) => {
- self.uncommitted.pop();
- self.uncommitted.push(MoveTo::Parent);
- }
- Some(MoveTo::TempChild(_)) | Some(MoveTo::Child(_)) | Some(MoveTo::ReverseChild(_)) => {
- self.uncommitted.pop();
- // And we're back at the parent.
- }
- _ => {
- self.uncommitted.push(MoveTo::Parent);
- }
- }
- }
- /// Move the traversal down in the tree to the first child of the current
- /// node.
- pub fn down(&mut self) {
- if let Some(&MoveTo::Parent) = self.uncommitted.last() {
- self.uncommitted.pop();
- self.sibling(0);
- } else {
- self.uncommitted.push(MoveTo::Child(0));
- }
- }
- /// Move the traversal to the n^th sibling.
- pub fn sibling(&mut self, index: usize) {
- let index = index as u32;
- match self.uncommitted.last_mut() {
- Some(MoveTo::Sibling(ref mut n)) | Some(MoveTo::Child(ref mut n)) => {
- *n = index;
- }
- Some(MoveTo::ReverseSibling(_)) => {
- self.uncommitted.pop();
- self.uncommitted.push(MoveTo::Sibling(index));
- }
- Some(MoveTo::TempChild(_)) | Some(MoveTo::ReverseChild(_)) => {
- self.uncommitted.pop();
- self.uncommitted.push(MoveTo::Child(index))
- }
- _ => {
- self.uncommitted.push(MoveTo::Sibling(index));
- }
- }
- }
- /// Move the the n^th from last sibling.
- pub fn reverse_sibling(&mut self, index: usize) {
- let index = index as u32;
- match self.uncommitted.last_mut() {
- Some(MoveTo::ReverseSibling(ref mut n)) | Some(MoveTo::ReverseChild(ref mut n)) => {
- *n = index;
- }
- Some(MoveTo::Sibling(_)) => {
- self.uncommitted.pop();
- self.uncommitted.push(MoveTo::ReverseSibling(index));
- }
- Some(MoveTo::TempChild(_)) | Some(MoveTo::Child(_)) => {
- self.uncommitted.pop();
- self.uncommitted.push(MoveTo::ReverseChild(index))
- }
- _ => {
- self.uncommitted.push(MoveTo::ReverseSibling(index));
- }
- }
- }
- /// Go to the given saved temporary.
- pub fn down_to_temp(&mut self, temp: u32) {
- match self.uncommitted.last() {
- Some(MoveTo::Sibling(_)) | Some(MoveTo::ReverseSibling(_)) => {
- self.uncommitted.pop();
- }
- Some(MoveTo::Parent)
- | Some(MoveTo::TempChild(_))
- | Some(MoveTo::Child(_))
- | Some(MoveTo::ReverseChild(_))
- | None => {
- // Can't remove moves to parents since we rely on their stack
- // pops.
- }
- }
- self.uncommitted.push(MoveTo::TempChild(temp));
- }
- /// Are all the traversal's moves committed? That is, are there no moves
- /// that have *not* been committed yet?
- #[inline]
- pub fn is_committed(&self) -> bool {
- // 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
- /// commit.
- #[inline]
- pub fn commit(&mut self) -> std::vec::Drain<'_, MoveTo> {
- self.uncommitted.drain(..)
- }
- #[inline]
- pub fn reset(&mut self) {
- self.uncommitted.clear();
- }
- }
- // pub struct Moves<'a> {
- // inner: std::vec::Drain<'a, MoveTo>,
- // }
- // impl Iterator for Moves<'_> {
- // type Item = MoveTo;
- // #[inline]
- // fn next(&mut self) -> Option<MoveTo> {
- // self.inner.next()
- // }
- // }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn test_traversal() {
- fn t<F>(f: F) -> Box<dyn FnMut(&mut Traversal)>
- where
- F: 'static + FnMut(&mut Traversal),
- {
- Box::new(f) as _
- }
- for (mut traverse, expected_moves) in vec![
- (
- t(|t| {
- t.down();
- }),
- vec![MoveTo::Child(0)],
- ),
- (
- t(|t| {
- t.up();
- }),
- vec![MoveTo::Parent],
- ),
- (
- t(|t| {
- t.sibling(42);
- }),
- vec![MoveTo::Sibling(42)],
- ),
- (
- t(|t| {
- t.down();
- t.up();
- }),
- vec![],
- ),
- (
- t(|t| {
- t.down();
- t.sibling(2);
- t.up();
- }),
- vec![],
- ),
- (
- t(|t| {
- t.down();
- t.sibling(3);
- }),
- vec![MoveTo::Child(3)],
- ),
- (
- t(|t| {
- t.down();
- t.sibling(4);
- t.sibling(8);
- }),
- vec![MoveTo::Child(8)],
- ),
- (
- t(|t| {
- t.sibling(1);
- t.sibling(1);
- }),
- vec![MoveTo::Sibling(1)],
- ),
- (
- t(|t| {
- t.reverse_sibling(3);
- }),
- vec![MoveTo::ReverseSibling(3)],
- ),
- (
- t(|t| {
- t.down();
- t.reverse_sibling(3);
- }),
- vec![MoveTo::ReverseChild(3)],
- ),
- (
- t(|t| {
- t.down();
- t.reverse_sibling(3);
- t.up();
- }),
- vec![],
- ),
- (
- t(|t| {
- t.down();
- t.reverse_sibling(3);
- t.reverse_sibling(6);
- }),
- vec![MoveTo::ReverseChild(6)],
- ),
- (
- t(|t| {
- t.up();
- t.reverse_sibling(3);
- t.reverse_sibling(6);
- }),
- vec![MoveTo::Parent, MoveTo::ReverseSibling(6)],
- ),
- (
- t(|t| {
- t.up();
- t.sibling(3);
- t.sibling(6);
- }),
- vec![MoveTo::Parent, MoveTo::Sibling(6)],
- ),
- (
- t(|t| {
- t.sibling(3);
- t.sibling(6);
- t.up();
- }),
- vec![MoveTo::Parent],
- ),
- (
- t(|t| {
- t.reverse_sibling(3);
- t.reverse_sibling(6);
- t.up();
- }),
- vec![MoveTo::Parent],
- ),
- (
- t(|t| {
- t.down();
- t.down_to_temp(3);
- }),
- vec![MoveTo::Child(0), MoveTo::TempChild(3)],
- ),
- (
- t(|t| {
- t.down_to_temp(3);
- t.sibling(5);
- }),
- vec![MoveTo::Child(5)],
- ),
- (
- t(|t| {
- t.down_to_temp(3);
- t.reverse_sibling(5);
- }),
- vec![MoveTo::ReverseChild(5)],
- ),
- (
- t(|t| {
- t.down_to_temp(3);
- t.up();
- }),
- vec![],
- ),
- (
- t(|t| {
- t.sibling(2);
- t.up();
- t.down_to_temp(3);
- }),
- vec![MoveTo::Parent, MoveTo::TempChild(3)],
- ),
- (
- t(|t| {
- t.up();
- t.down_to_temp(3);
- }),
- vec![MoveTo::Parent, MoveTo::TempChild(3)],
- ),
- ] {
- let mut traversal = Traversal::new();
- traverse(&mut traversal);
- let actual_moves: Vec<_> = traversal.commit().collect();
- assert_eq!(actual_moves, expected_moves);
- }
- }
- }
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
- pub struct StringKey(u32);
- impl From<StringKey> for u32 {
- #[inline]
- fn from(key: StringKey) -> u32 {
- key.0
- }
- }
|