wip: broken, but solved

This commit is contained in:
Jonathan Kelley committed 2021-03-03 02:27:26 -05:00
1 parent 879e107634
commit cb74d70f83
21 files changed
+2398 -2341

No files matched your search

-753
View File
@@ -1,753 +0,0 @@
//! 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 (a stream of u32) 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;
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 },
NewEventListener { event_type: &'d str, idx: CbIdx },
UpdateEventListener { event_type: &'d str, idx: CbIdx },
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 },
}
/// Re-export a cover over generational ID for libraries that don't need it
/// We can go back and forth between the two via methods on GI
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct CbIdx {
pub gi_id: usize,
pub gi_gen: u64,
pub listener_idx: usize,
}
impl CbIdx {
pub fn from_gi_index(index: generational_arena::Index, listener_idx: usize) -> Self {
let (gi_id, gi_gen) = index.into_raw_parts();
Self {
gi_id,
gi_gen,
listener_idx,
}
}
}
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.
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()
}
}
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 ensure_string(&mut self, _string: &str) -> StringKey {
todo!()
// self.strings.ensure_string(string, &self.emitter)
}
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, idx: CbIdx) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::NewEventListener {
event_type: event,
idx,
});
// todo!("Event listener not wired up yet");
// log::debug!("emit: new_event_listener({:?})", listener);
// let (a, b) = listener.get_callback_parts();
// debug_assert!(a != 0);
// // let event_id = self.ensure_string(listener.event);
// self.emitter.new_event_listener(listener.event.into(), a, b);
}
pub fn update_event_listener(&mut self, event: &'a str, idx: CbIdx) {
debug_assert!(self.traversal_is_committed());
if self.forcing_new_listeners {
self.new_event_listener(event, idx);
return;
}
self.emitter.push(Edit::NewEventListener {
event_type: event,
idx,
});
// log::debug!("emit: update_event_listener({:?})", listener);
// // todo!("Event listener not wired up yet");
// let (a, b) = listener.get_callback_parts();
// debug_assert!(a != 0);
// self.emitter.push(Edit::UpdateEventListener {
// event_type: listener.event.into(),
// a,
// b,
// });
// self.emitter.update_event_listener(event_id.into(), a, b);
}
pub fn remove_event_listener(&mut self, event: &'a str) {
debug_assert!(self.traversal_is_committed());
self.emitter
.push(Edit::RemoveEventListener { event_type: event });
// debug!("emit: remove_event_listener({:?})", event);
// let _event_id = self.ensure_string(event);
// todo!("Event listener not wired up yet");
// self.emitter.remove_event_listener(event_id.into());
}
// #[inline]
// pub fn has_template(&mut self, id: CacheId) -> bool {
// self.templates.contains(&id)
// }
// 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) -> Moves {
Moves {
inner: 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
}
}
+4 -2
View File
@@ -1,6 +1,7 @@
//! This file handles the supporting infrastructure for the `Component` trait and `Properties` which makes it possible
//! for components to be used within Nodes.
//!
pub type ScopeIdx = generational_arena::Index;
/// The `Component` trait refers to any struct or funciton that can be used as a component
/// We automatically implement Component for FC<T>
@@ -44,6 +45,7 @@ mod tests {
static TestComponent: FC<()> = |ctx, props| {
//
ctx.render(html! {
<div>
</div>
@@ -52,7 +54,7 @@ mod tests {
static TestComponent2: FC<()> = |ctx, props| {
//
ctx.render(|bump: &Bump| VNode::text("blah"))
ctx.render(|ctx| VNode::text("blah"))
};
#[test]
+18 -3
View File
@@ -76,8 +76,10 @@ impl<'a> Context<'a> {
/// ctx.render(lazy_tree)
/// }
///```
pub fn render(self, lazy_nodes: impl FnOnce(&'a Bump) -> VNode<'a> + 'a) -> DomTree {
let safe_nodes = lazy_nodes(self.bump);
pub fn render(self, lazy_nodes: impl FnOnce(NodeCtx<'a>) -> VNode<'a> + 'a) -> DomTree {
let ctx = NodeCtx { bump: self.bump };
let safe_nodes = lazy_nodes(ctx);
let unsafe_nodes = unsafe { std::mem::transmute::<VNode<'a>, VNode<'static>>(safe_nodes) };
self.final_nodes.deref().borrow_mut().replace(unsafe_nodes);
DomTree {}
@@ -90,12 +92,25 @@ impl<'a> Context<'a> {
/// When the future completes, the component will be renderered
pub fn suspend(
&self,
_fut: impl Future<Output = impl FnOnce(&'a Bump) -> VNode<'a>>,
_fut: impl Future<Output = impl FnOnce(&'a NodeCtx<'a>) -> VNode<'a>>,
) -> VNode<'a> {
todo!()
}
}
// NodeCtx is used to build VNodes in the component's memory space.
// This struct adds metadata to the final DomTree about listeners, attributes, and children
pub struct NodeCtx<'a> {
bump: &'a Bump,
}
impl NodeCtx<'_> {
#[inline]
pub fn bump(&self) -> &Bump {
self.bump
}
}
pub mod hooks {
//! This module provides internal state management functionality for Dioxus components
//!
+1100 -274
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+5 -13
View File
@@ -4,29 +4,21 @@
//! 3rd party renderers are responsible for forming this virtual events from events
//!
//! The goal here is to provide a consistent event interface across all renderer types
use generational_arena::Index;
use crate::innerlude::CbIdx;
use crate::innerlude::ScopeIdx;
#[derive(Debug)]
pub struct EventTrigger {
pub component_id: Index,
pub component_id: ScopeIdx,
pub listener_id: usize,
pub event: VirtualEvent,
}
impl EventTrigger {
pub fn new(event: VirtualEvent, cb: CbIdx) -> Self {
let CbIdx {
gi_id,
gi_gen,
listener_idx,
} = cb;
let component_id = Index::from_raw_parts(gi_id, gi_gen);
pub fn new(event: VirtualEvent, scope: ScopeIdx, id: usize) -> Self {
Self {
component_id,
listener_id: listener_idx,
component_id: scope,
listener_id: id,
event,
}
}
+16 -9
View File
@@ -65,20 +65,21 @@
//! - dioxus-liveview (SSR + StringRenderer)
//!
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;
// pub mod patch; // The diffing algorithm that builds the ChangeList
pub mod dodriodiff; // The diffing algorithm that builds the ChangeList
pub mod debug_renderer;
pub mod patch; // An "edit phase" described by transitions and edit operations // Test harness for validating that lifecycles and diffs work appropriately
// pub mod diff;
// pub mod patch; // The diffing algorithm that builds the ChangeList
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 validation; // Logic for validating trees
pub mod virtual_dom; // Most fun logic starts here, manages the lifecycle and suspense
pub mod builder {
@@ -96,7 +97,12 @@ pub(crate) mod innerlude {
pub(crate) use crate::virtual_dom::VirtualDom;
pub(crate) use nodes::*;
pub use crate::changelist::CbIdx;
pub use crate::component::ScopeIdx;
pub use crate::diff::DiffMachine;
pub use crate::events::EventTrigger;
pub use crate::patch::{EditList, EditMachine};
// pub use crate::patchdx;
// pub use crate::patchtList;
// pub use nodes::iterables::IterableNodes;
/// This type alias is an internal way of abstracting over the static functions that represent components.
@@ -138,6 +144,7 @@ pub mod prelude {
// expose our bumpalo type
pub use bumpalo;
pub use bumpalo::Bump;
// Re-export the FC macro
pub use crate as dioxus;
@@ -146,8 +153,8 @@ pub mod prelude {
pub use dioxus_core_macro::format_args_f;
pub use dioxus_core_macro::{fc, html, rsx};
// pub use crate::diff::DiffMachine;
pub use crate::dodriodiff::DiffMachine;
pub use crate::component::ScopeIdx;
pub use crate::diff::DiffMachine;
pub use crate::hooks::*;
}
+1 -1
View File
@@ -333,7 +333,7 @@ where
///
/// // A button that does something when clicked!
/// let my_button = button(&b)
/// .on("click", |root, vdom, event| {
/// .on("click", |event| {
/// // ...
/// })
/// .finish();
+7 -1
View File
@@ -94,7 +94,8 @@ mod vnode {
}
mod velement {
use crate::events::VirtualEvent;
// use crate::{events::VirtualEvent, innerlude::CbIdx};
use super::*;
use std::fmt::Debug;
@@ -179,6 +180,11 @@ mod velement {
}
}
pub struct ListenerHandle {
pub event: &'static str,
pub idx: CbIdx,
}
/// An event listener.
pub struct Listener<'bump> {
/// The type of event to listen for.
+718 -64
View File
@@ -1,80 +1,734 @@
use fxhash::FxHashMap;
//! 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 crate::innerlude::{VNode, VText};
use bumpalo::Bump;
/// A Patch encodes an operation that modifies a real DOM element.
use crate::innerlude::{Listener, ScopeIdx};
use serde::{Deserialize, Serialize};
/// The `Edit` represents a single modifcation of the renderer tree.
///
/// To update the real DOM that a user sees you'll want to first diff your
/// old virtual dom and new virtual dom.
///
/// This diff operation will generate `Vec<Patch>` with zero or more patches that, when
/// applied to your real DOM, will make your real DOM look like your new virtual dom.
///
/// Each Patch has a u32 node index that helps us identify the real DOM node that it applies to.
///
/// Our old virtual dom's nodes are indexed depth first, as shown in this illustration
/// (0 being the root node, 1 being it's first child, 2 being it's first child's first child).
///
/// ```text
/// .─.
/// ( 0 )
/// `┬'
/// ┌────┴──────┐
/// │ │
/// ▼ ▼
/// .─. .─.
/// ( 1 ) ( 4 )
/// `┬' `─'
/// ┌────┴───┐ ├─────┬─────┐
/// │ │ │ │ │
/// ▼ ▼ ▼ ▼ ▼
/// .─. .─. .─. .─. .─.
/// ( 2 ) ( 3 ) ( 5 ) ( 6 ) ( 7 )
/// `─' `─' `─' `─' `─'
/// ```
///
/// The patching process is tested in a real browser in crates/virtual-dom-rs/tests/diff_patch.rs
// #[derive(serde::Serialize, serde::Deserialize)]
pub enum Patch<'a> {
/// Append a vector of child nodes to a parent node id.
AppendChildren(NodeIdx, Vec<&'a VNode<'a>>),
/// For a `node_i32`, remove all children besides the first `len`
TruncateChildren(NodeIdx, usize),
/// Replace a node with another node. This typically happens when a node's tag changes.
/// ex: <div> becomes <span>
Replace(NodeIdx, &'a VNode<'a>),
/// Add attributes that the new node has that the old node does not
AddAttributes(NodeIdx, FxHashMap<&'a str, &'a str>),
/// Remove attributes that the old node had that the new node doesn't
RemoveAttributes(NodeIdx, Vec<&'a str>),
/// Change the text of a Text node.
ChangeText(NodeIdx, &'a VText<'a>),
///
///
/// 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 },
NewEventListener { event_type: &'d str, s: ScopeIdx },
UpdateEventListener { event_type: &'d str, s: ScopeIdx },
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 },
PushKnown { node: ScopeIdx },
}
type NodeIdx = usize;
pub type EditList<'src> = Vec<Edit<'src>>;
impl<'a> Patch<'a> {
/// Every Patch is meant to be applied to a specific node within the DOM. Get the
/// index of the DOM node that this patch should apply to. DOM nodes are indexed
/// depth first with the root node in the tree having index 0.
pub fn node_idx(&self) -> usize {
match self {
Patch::AppendChildren(node_idx, _) => *node_idx,
Patch::TruncateChildren(node_idx, _) => *node_idx,
Patch::Replace(node_idx, _) => *node_idx,
Patch::AddAttributes(node_idx, _) => *node_idx,
Patch::RemoveAttributes(node_idx, _) => *node_idx,
Patch::ChangeText(node_idx, _) => *node_idx,
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.
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()
}
}
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 ensure_string(&mut self, _string: &str) -> StringKey {
todo!()
// self.strings.ensure_string(string, &self.emitter)
}
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, idx: CbIdx) {
debug_assert!(self.traversal_is_committed());
self.emitter.push(Edit::NewEventListener {
event_type: event,
s: idx,
});
// todo!("Event listener not wired up yet");
// log::debug!("emit: new_event_listener({:?})", listener);
// let (a, b) = listener.get_callback_parts();
// debug_assert!(a != 0);
// // let event_id = self.ensure_string(listener.event);
// self.emitter.new_event_listener(listener.event.into(), a, b);
}
pub fn update_event_listener(&mut self, event: &'a str, idx: CbIdx) {
debug_assert!(self.traversal_is_committed());
if self.forcing_new_listeners {
self.new_event_listener(event, idx);
return;
}
self.emitter.push(Edit::NewEventListener {
event_type: event,
s: idx,
});
// log::debug!("emit: update_event_listener({:?})", listener);
// // todo!("Event listener not wired up yet");
// let (a, b) = listener.get_callback_parts();
// debug_assert!(a != 0);
// self.emitter.push(Edit::UpdateEventListener {
// event_type: listener.event.into(),
// a,
// b,
// });
// self.emitter.update_event_listener(event_id.into(), a, b);
}
pub fn remove_event_listener(&mut self, event: &'a str) {
debug_assert!(self.traversal_is_committed());
self.emitter
.push(Edit::RemoveEventListener { event_type: event });
// debug!("emit: remove_event_listener({:?})", event);
// let _event_id = self.ensure_string(event);
// todo!("Event listener not wired up yet");
// self.emitter.remove_event_listener(event_id.into());
}
// #[inline]
// pub fn has_template(&mut self, id: CacheId) -> bool {
// self.templates.contains(&id)
// }
// 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) -> Moves {
Moves {
inner: 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);
}
}
}
pub struct PatchList<'a> {
patches: Vec<Patch<'a>>,
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StringKey(u32);
impl From<StringKey> for u32 {
#[inline]
fn from(key: StringKey) -> u32 {
key.0
}
}
+11 -9
View File
@@ -1,8 +1,9 @@
use crate::component::ScopeIdx;
use crate::context::hooks::Hook;
use crate::innerlude::*;
use crate::nodes::VNode;
use bumpalo::Bump;
use generational_arena::Index;
// use generational_arena::ScopeIdx;
use std::{
any::TypeId,
@@ -32,7 +33,7 @@ pub struct Scope {
pub hook_arena: typed_arena::Arena<Hook>,
// Map to the parent
pub parent: Option<Index>,
pub parent: Option<ScopeIdx>,
pub frames: ActiveFrame,
@@ -50,7 +51,7 @@ pub struct Scope {
impl Scope {
// create a new scope from a function
pub fn new<'a, P1, P2: 'static>(f: FC<P1>, props: P1, parent: Option<Index>) -> Self {
pub fn new<'a, P1, P2: 'static>(f: FC<P1>, props: P1, parent: Option<ScopeIdx>) -> Self {
let hook_arena = typed_arena::Arena::new();
let hooks = RefCell::new(Vec::new());
@@ -288,7 +289,7 @@ mod tests {
fn test_scope() {
let example: FC<()> = |ctx, props| {
use crate::builder::*;
ctx.render(|b| div(b).child(text("a")).finish())
ctx.render(|ctx| div(ctx.bump()).child(text("a")).finish())
};
let props = ();
@@ -318,7 +319,8 @@ mod tests {
let childprops: ExampleProps<'a> = ExampleProps { name: content };
// let childprops: ExampleProps<'a> = ExampleProps { name: content };
ctx.render(move |b: &'a Bump| {
ctx.render(move |ctx| {
let b = ctx.bump();
div(b)
.child(text(props.name))
// .child(text(props.name))
@@ -336,8 +338,8 @@ mod tests {
}
fn child_example<'b>(ctx: Context<'b>, props: &'b ExampleProps) -> DomTree {
ctx.render(move |b| {
div(b)
ctx.render(move |ctx| {
div(ctx.bump())
.child(text(props.name))
//
.finish()
@@ -346,8 +348,8 @@ mod tests {
static CHILD: FC<ExampleProps> = |ctx, props: &'_ ExampleProps| {
// todo!()
ctx.render(move |b| {
div(b)
ctx.render(move |ctx| {
div(ctx.bump())
.child(text(props.name))
//
.finish()
-40
View File
@@ -1,40 +0,0 @@
/// TODO @Jon
/// Figure out if validation should be its own crate, or embedded directly into dioxus
///
/// Should we even be bothered with validation?
///
///
///
mod validation {
use once_cell::sync::Lazy;
use std::collections::HashSet;
// Used to uniquely identify elements that contain closures so that the DomUpdater can
// look them up by their unique id.
// When the DomUpdater sees that the element no longer exists it will drop all of it's
// Rc'd Closures for those events.
// It doesn't quite make sense to keep this here, perhaps just in the html crate?
// Dioxus itself shouldn't be concerned with the attribute names
// a ftk!
static SELF_CLOSING_TAGS: Lazy<HashSet<&'static str>> = Lazy::new(|| {
[
"area", "base", "br", "col", "hr", "img", "input", "link", "meta", "param", "command",
"keygen", "source",
]
.iter()
.cloned()
.collect()
});
/// Whether or not this tag is self closing
///
/// ```ignore
/// use dioxus_core::validation::is_self_closing;
/// assert_eq!(is_self_closing("br"), true);
/// assert_eq!(is_self_closing("div"), false);
/// ```
pub fn is_self_closing(tag: &str) -> bool {
SELF_CLOSING_TAGS.contains(tag)
// SELF_CLOSING_TAGS.contains(tag) || is_self_closing_svg_tag(tag)
}
}
+12 -24
View File
@@ -1,15 +1,10 @@
// use crate::{changelist::EditList, nodes::VNode};
use crate::{
changelist::{self, EditList},
dodriodiff::DiffMachine,
};
use crate::{events::EventTrigger, innerlude::*};
use crate::innerlude::*;
use bumpalo::Bump;
use generational_arena::{Arena, Index};
use generational_arena::Arena;
use std::{
any::{self, TypeId},
any::TypeId,
borrow::BorrowMut,
cell::{RefCell, UnsafeCell},
collections::{vec_deque, VecDeque},
@@ -28,7 +23,7 @@ pub struct VirtualDom {
/// The index of the root component.
/// Will not be ready if the dom is fresh
base_scope: Index,
base_scope: ScopeIdx,
event_queue: Rc<RefCell<VecDeque<LifecycleEvent>>>,
@@ -98,11 +93,7 @@ impl VirtualDom {
component.run::<()>();
diff_machine.diff_node(
component.old_frame(),
component.new_frame(),
Some(self.base_scope),
);
diff_machine.diff_node(component.old_frame(), component.new_frame());
Ok(diff_machine.consume())
}
@@ -173,11 +164,7 @@ impl VirtualDom {
component.run::<()>();
diff_machine.diff_node(
component.old_frame(),
component.new_frame(),
Some(self.base_scope),
);
diff_machine.diff_node(component.old_frame(), component.new_frame());
// diff_machine.diff_node(
// component.old_frame(),
// component.new_frame(),
@@ -252,7 +239,7 @@ pub struct LifecycleEvent {
pub enum LifecycleType {
// Component needs to be mounted, but its scope doesn't exist yet
Mount {
to: Index,
to: ScopeIdx,
under: usize,
props: Box<dyn std::any::Any>,
},
@@ -260,17 +247,17 @@ pub enum LifecycleType {
// Parent was evalauted causing new props to generate
PropsChanged {
props: Box<dyn std::any::Any>,
component: Index,
component: ScopeIdx,
},
// Hook for the subscription API
Callback {
component: Index,
component: ScopeIdx,
},
}
impl LifecycleEvent {
fn index(&self) -> Option<Index> {
fn index(&self) -> Option<ScopeIdx> {
match &self.event_type {
LifecycleType::Mount {
to: _,
@@ -290,8 +277,9 @@ mod tests {
#[test]
fn start_dom() {
let mut dom = VirtualDom::new(|ctx, props| {
ctx.render(|bump| {
ctx.render(|ctx| {
use crate::builder::*;
let bump = ctx.bump();
div(bump).child(text("hello, world")).finish()
})
});