on master help sign in
Refactor node representation and use log crate for logging
55618b1Nick Fitzgerald
Cargo.toml+2
@@ -11,6 +11,7 @@ default = []
11bumpalo = "1.0.2"
11bumpalo = "1.0.2"
12wasm-bindgen = "0.2.25"
12wasm-bindgen = "0.2.25"
13js-sys = "0.3.2"
13js-sys = "0.3.2"
14+log = "0.4.6"
14
15
15[dependencies.web-sys]
16[dependencies.web-sys]
16version = "0.3.8"
17version = "0.3.8"
@@ -24,6 +25,7 @@ features = [
24
25
25[dev-dependencies]
26[dev-dependencies]
26wasm-bindgen-test = "0.2"
27wasm-bindgen-test = "0.2"
28+console_log = "0.1.2"
27
29
28[profile.release]
30[profile.release]
29# Tell `rustc` to optimize for small code size.
31# Tell `rustc` to optimize for small code size.
src/change_list.rs+1
@@ -1,4 +1,5 @@
1use bumpalo::{Bump, BumpAllocSafe};
1use bumpalo::{Bump, BumpAllocSafe};
2+use log::*;
2use std::sync::Once;
3use std::sync::Once;
3use wasm_bindgen::prelude::*;
4use wasm_bindgen::prelude::*;
4
5
src/lib.rs+2−5
@@ -1,17 +1,14 @@
1use bumpalo::Bump;
1use bumpalo::Bump;
2
2
3−#[macro_use]
4−mod macros;
5−
6pub mod change_list;
3pub mod change_list;
7pub mod node;
4pub mod node;
8pub mod vdom;
5pub mod vdom;
9
6
10−pub use self::node::{Attribute, Node, NodeRef};
7+pub use self::node::{Attribute, ElementNode, Node, TextNode};
11pub use self::vdom::Vdom;
8pub use self::vdom::Vdom;
12
9
13pub trait Render {
10pub trait Render {
14− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> NodeRef<'bump>
11+ fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
15 where
12 where
16 'a: 'bump;
13 'a: 'bump;
17}
14}
src/macros.rsdeleted−7
@@ -1,7 +0,0 @@
1−macro_rules! debug {
2− ( $fmt:expr $(, $x:expr )* $(,)* ) => {{
3− if cfg!(debug_assertions) {
4− web_sys::console::log_1(&format!($fmt, $($x),*).into());
5− }
6− }}
7−}
src/node.rs+58−65
@@ -1,23 +1,29 @@
1−use bumpalo::BumpAllocSafe;
1+use bumpalo::{Bump, BumpAllocSafe};
2
2
3+/// A node is either a text node or an element.
3−#[derive(Clone, Debug)]
4+#[derive(Debug, Clone)]
4−pub struct Node<'a, Attributes, Children> {
5+pub enum Node<'a> {
5− data: NodeData<'a>,
6+ /// A text node.
6− attributes: Attributes,
7+ Text(TextNode<'a>),
7− children: Children,
8+
9+ /// An element potentially with attributes and children.
10+ Element(ElementNode<'a>),
8}
11}
9
12
13+/// Text nodes are just a string of text. They cannot have attributes or
14+/// children.
10−#[derive(Clone, Copy, Debug)]
15+#[derive(Debug, Clone)]
11−pub enum NodeData<'a> {
16+pub struct TextNode<'a> {
12− Element { tag_name: &'a str },
13− Text { text: &'a str },
17+ pub(crate) text: &'a str,
14}
18}
15
19
20+/// Elements have a tag name, zero or more attributes, and zero or more
21+/// children.
16−#[derive(Clone, Debug)]
22+#[derive(Debug, Clone)]
17−pub struct NodeRef<'a> {
23+pub struct ElementNode<'a> {
18− pub(crate) data: NodeData<'a>,
24+ pub(crate) tag_name: &'a str,
19 pub(crate) attributes: &'a [Attribute<'a>],
25 pub(crate) attributes: &'a [Attribute<'a>],
20− pub(crate) children: &'a [NodeRef<'a>],
26+ pub(crate) children: &'a [Node<'a>],
21}
27}
22
28
23#[derive(Clone, Debug)]
29#[derive(Clone, Debug)]
@@ -26,69 +32,56 @@ pub struct Attribute<'a> {
26 pub value: &'a str,
32 pub value: &'a str,
27}
33}
28
34
29−impl<'a, Attributes, Children> BumpAllocSafe for Node<'a, Attributes, Children>
35+impl<'a> Node<'a> {
30−where
36+ /// Is this node a text node?
31− Attributes: BumpAllocSafe,
37+ pub fn is_text(&self) -> bool {
32− Children: BumpAllocSafe,
38+ match self {
33−{
39+ Node::Text(_) => true,
34−}
40+ _ => false,
35−
36−impl<'a, Attributes, Children> From<&'a Node<'a, Attributes, Children>> for NodeRef<'a>
37−where
38− Attributes: AsRef<[Attribute<'a>]>,
39− Children: AsRef<[NodeRef<'a>]>,
40−{
41− fn from(node: &'a Node<'a, Attributes, Children>) -> NodeRef<'a> {
42− NodeRef {
43− data: node.data,
44− attributes: node.attributes.as_ref(),
45− children: node.children.as_ref(),
46 }
41 }
47 }
42 }
48−}
49
43
50−impl<'a, Attributes, Children> From<&'a mut Node<'a, Attributes, Children>> for NodeRef<'a>
44+ /// Is this node an element?
51−where
45+ pub fn is_element(&self) -> bool {
52− Attributes: AsRef<[Attribute<'a>]>,
46+ match self {
53− Children: AsRef<[NodeRef<'a>]>,
47+ Node::Element { .. } => true,
54−{
48+ _ => false,
55− fn from(node: &'a mut Node<'a, Attributes, Children>) -> NodeRef<'a> {
56− NodeRef {
57− data: node.data,
58− attributes: node.attributes.as_ref(),
59− children: node.children.as_ref(),
60 }
49 }
61 }
50 }
62}
51}
63
52
64−impl<'a, Attributes, Children> Node<'a, Attributes, Children>
53+impl<'a> BumpAllocSafe for Node<'a> {}
65−where
54+impl<'a> BumpAllocSafe for Attribute<'a> {}
66− Attributes: AsRef<[Attribute<'a>]>,
55+
67− Children: AsRef<[NodeRef<'a>]>,
56+impl<'a> Node<'a> {
68−{
57+ /// Construct a new text node with the given text.
58+ #[inline]
59+ pub fn text(text: &'a str) -> Node<'a> {
60+ Node::Text(TextNode { text })
61+ }
62+
63+ /// Construct a new element node with the given tag name and children.
64+ #[inline]
69− pub fn element(
65+ pub fn element<Attributes, Children>(
66+ bump: &'a Bump,
70 tag_name: &'a str,
67 tag_name: &'a str,
71 attributes: Attributes,
68 attributes: Attributes,
72 children: Children,
69 children: Children,
73− ) -> Node<'a, Attributes, Children> {
70+ ) -> Node<'a>
74− let data = NodeData::Element { tag_name };
71+ where
75− Node {
72+ Attributes: 'a + BumpAllocSafe + AsRef<[Attribute<'a>]>,
76− data,
73+ Children: 'a + BumpAllocSafe + AsRef<[Node<'a>]>,
77− attributes,
74+ {
75+ let children: &'a Children = bump.alloc(children);
78− children,
76+ let children: &'a [Node<'a>] = children.as_ref();
79− }
80− }
81−}
82
77
83−impl<'a> Node<'a, [Attribute<'a>; 0], [NodeRef<'a>; 0]> {
78+ let attributes: &'a Attributes = bump.alloc(attributes);
84− pub fn text(text: &'a str) -> Node<'a, [Attribute<'a>; 0], [NodeRef<'a>; 0]> {
79+ let attributes: &'a [Attribute<'a>] = attributes.as_ref();
85− let data = NodeData::Text { text };
80+
86− let attributes = [];
81+ Node::Element(ElementNode {
87− let children = [];
82+ tag_name,
88− Node {
89− data,
90 attributes,
83 attributes,
91 children,
84 children,
92− }
85+ })
93 }
86 }
94}
87}
src/vdom.rs+37−30
@@ -1,7 +1,8 @@
1use super::change_list::ChangeList;
1use super::change_list::ChangeList;
2−use super::node::{Attribute, Node, NodeData, NodeRef};
2+use super::node::{Attribute, ElementNode, Node, TextNode};
3use super::Render;
3use super::Render;
4use bumpalo::Bump;
4use bumpalo::Bump;
5+use log::*;
5use std::cmp;
6use std::cmp;
6use std::mem;
7use std::mem;
7
8
@@ -11,10 +12,10 @@ pub struct Vdom {
11 container: web_sys::Element,
12 container: web_sys::Element,
12
13
13 // Actually a reference into `self.dom_buffers[0]`.
14 // Actually a reference into `self.dom_buffers[0]`.
14− current_root: Option<NodeRef<'static>>,
15+ current_root: Option<Node<'static>>,
15}
16}
16
17
17−unsafe fn extend_node_lifetime<'a>(node: NodeRef<'a>) -> NodeRef<'static> {
18+unsafe fn extend_node_lifetime<'a>(node: Node<'a>) -> Node<'static> {
18 mem::transmute(node)
19 mem::transmute(node)
19}
20}
20
21
@@ -30,7 +31,7 @@ impl Vdom {
30 container.set_inner_html("");
31 container.set_inner_html("");
31
32
32 // Create a dummy `<div/>` in our container.
33 // Create a dummy `<div/>` in our container.
33− let current_root: NodeRef = dom_buffers[0].alloc(Node::element("div", [], [])).into();
34+ let current_root = Node::element(&dom_buffers[0], "div", [], []);
34 let current_root = Some(unsafe { extend_node_lifetime(current_root) });
35 let current_root = Some(unsafe { extend_node_lifetime(current_root) });
35 let window = web_sys::window().expect("should have acess to the Window");
36 let window = web_sys::window().expect("should have acess to the Window");
36 let document = window
37 let document = window
@@ -85,53 +86,55 @@ impl Vdom {
85 mem::swap(&mut first[0], &mut second[0]);
86 mem::swap(&mut first[0], &mut second[0]);
86 }
87 }
87
88
88− unsafe fn set_current_root(&mut self, current: NodeRef<'static>) {
89+ unsafe fn set_current_root(&mut self, current: Node<'static>) {
89 debug_assert!(self.current_root.is_none());
90 debug_assert!(self.current_root.is_none());
90 self.current_root = Some(current);
91 self.current_root = Some(current);
91 }
92 }
92
93
93− fn diff(&self, old: NodeRef, new: NodeRef) {
94+ fn diff(&self, old: Node, new: Node) {
94− debug!("---------------------------------------------------------");
95+ // debug!("---------------------------------------------------------");
95− debug!("dodrio::Vdom::diff");
96+ // debug!("dodrio::Vdom::diff");
96− debug!(" old = {:#?}", old);
97+ // debug!(" old = {:#?}", old);
97− debug!(" new = {:#?}", new);
98+ // debug!(" new = {:#?}", new);
98− match (new.data, old.data) {
99+ match (&new, old) {
99− (NodeData::Text { text: new_text }, NodeData::Text { text: old_text }) => {
100+ (&Node::Text(TextNode { text: new_text }), Node::Text(TextNode { text: old_text })) => {
100 debug!(" both are text nodes");
101 debug!(" both are text nodes");
101 if new_text != old_text {
102 if new_text != old_text {
102 debug!(" text needs updating");
103 debug!(" text needs updating");
103 self.change_list.emit_set_text(new_text);
104 self.change_list.emit_set_text(new_text);
104 }
105 }
105 }
106 }
106− (NodeData::Text { .. }, NodeData::Element { .. }) => {
107+ (&Node::Text(TextNode { .. }), Node::Element(ElementNode { .. })) => {
107 debug!(" replacing a text node with an element");
108 debug!(" replacing a text node with an element");
108 self.create(new);
109 self.create(new);
109 self.change_list.emit_replace_with();
110 self.change_list.emit_replace_with();
110 }
111 }
111− (NodeData::Element { .. }, NodeData::Text { .. }) => {
112+ (&Node::Element(ElementNode { .. }), Node::Text(TextNode { .. })) => {
112 debug!(" replacing an element with a text node");
113 debug!(" replacing an element with a text node");
113 self.create(new);
114 self.create(new);
114 self.change_list.emit_replace_with();
115 self.change_list.emit_replace_with();
115 }
116 }
116 (
117 (
117− NodeData::Element {
118+ &Node::Element(ElementNode {
118 tag_name: new_tag_name,
119 tag_name: new_tag_name,
120+ attributes: new_attributes,
121+ children: new_children,
119− },
122+ }),
120− NodeData::Element {
123+ Node::Element(ElementNode {
121 tag_name: old_tag_name,
124 tag_name: old_tag_name,
125+ attributes: old_attributes,
126+ children: old_children,
122− },
127+ }),
123 ) => {
128 ) => {
124 debug!(" updating an element");
129 debug!(" updating an element");
125−
126 if new_tag_name != old_tag_name {
130 if new_tag_name != old_tag_name {
127 debug!(" different tag names; creating new element and replacing old element");
131 debug!(" different tag names; creating new element and replacing old element");
128 self.create(new);
132 self.create(new);
129 self.change_list.emit_replace_with();
133 self.change_list.emit_replace_with();
130 return;
134 return;
131 }
135 }
132−
133− self.diff_attributes(old.attributes, new.attributes);
136+ self.diff_attributes(old_attributes, new_attributes);
134− self.diff_children(old.children, new.children);
137+ self.diff_children(old_children, new_children);
135 }
138 }
136 }
139 }
137 }
140 }
@@ -165,7 +168,7 @@ impl Vdom {
165 }
168 }
166 }
169 }
167
170
168− fn diff_children(&self, old: &[NodeRef], new: &[NodeRef]) {
171+ fn diff_children(&self, old: &[Node], new: &[Node]) {
169 debug!(" updating children shared by old and new");
172 debug!(" updating children shared by old and new");
170
173
171 let num_children_to_diff = cmp::min(new.len(), old.len());
174 let num_children_to_diff = cmp::min(new.len(), old.len());
@@ -217,18 +220,22 @@ impl Vdom {
217 }
220 }
218 }
221 }
219
222
220− fn create(&self, node: NodeRef) {
223+ fn create(&self, node: Node) {
221− debug!("dodrio::Vdom::create({:#?})", node);
224+ // debug!("dodrio::Vdom::create({:#?})", node);
222− match node.data {
225+ match node {
223− NodeData::Text { text } => {
226+ Node::Text(TextNode { text }) => {
224 self.change_list.emit_create_text_node(text);
227 self.change_list.emit_create_text_node(text);
225 }
228 }
229+ Node::Element(ElementNode {
226− NodeData::Element { tag_name } => {
230+ tag_name,
231+ attributes,
232+ children,
233+ }) => {
227 self.change_list.emit_create_element(tag_name);
234 self.change_list.emit_create_element(tag_name);
228− for attr in node.attributes {
235+ for attr in attributes {
229 self.change_list.emit_set_attribute(&attr.name, &attr.value);
236 self.change_list.emit_set_attribute(&attr.name, &attr.value);
230 }
237 }
231− for child in node.children {
238+ for child in children {
232 self.create(child.clone());
239 self.create(child.clone());
233 self.change_list.emit_append_child();
240 self.change_list.emit_append_child();
234 }
241 }