on master help sign in
Introduce a `RenderContext`

This is passed to the `render` trait method. Right now it just wraps the underlying bump arena, but it will be a place that we add more functionality in the future.

commit3b3b186095e11d91dd8a2712f4d5a4d97878fca7 authorNick Fitzgerald <fitzgen@gmail.com> dateMon 25 Mar 2019, 13:51 (8 years ago) parent5dd3b12
20 files changed+345−300
README.md+2−2 benches/benches.rs+3−3 crates/js-api/src/lib.rs+19−27 examples/counter/src/lib.rs+6−7 examples/game-of-life/src/lib.rs+11−8 examples/hello-world/src/lib.rs+5−8 examples/input-form/src/lib.rs+6−7 examples/js-component/src/lib.rs+4−5 examples/moire/src/lib.rs+42−28 examples/sierpinski-triangle/src/lib.rs+29−20 examples/todomvc/src/todo.rs+11−11 examples/todomvc/src/todos.rs+33−33 src/lib.rs+9−9 src/render.rs+15−18 src/render_context.rs+38 src/vdom.rs+3−5 tests/web/events.rs+5−6 tests/web/js_api.rs+12−12 tests/web/main.rs+7−6 tests/web/render.rs+85−85
README.md+2−2
@@ -31,11 +31,11 @@ struct Hello {
3131}
3232
3333impl Render for Hello {
34− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
34+ fn render<'a, 'bump>(&'a self, cx: &mut RenderContext<'bump>) -> Node<'bump>
3535 where
3636 'a: 'bump,
3737 {
38− span(bump)
38+ span(cx.bump)
3939 .children([text("Hello, "), text(&self.who), text("!")])
4040 .finish()
4141 }
benches/benches.rs+3−3
@@ -16,15 +16,15 @@ use dodrio::{
1616/// The simplest thing we can render: `<div/>`.
1717struct Empty;
1818impl Render for Empty {
19− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
20− div(bump).finish()
19+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
20+ div(cx.bump).finish()
2121 }
2222}
2323
2424/// Render a list that is `self.0` items long, has attributes and listeners.
2525struct SimpleList(usize);
2626impl Render for SimpleList {
27− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
27+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
2828 let mut children = bumpalo::collections::Vec::with_capacity_in(self.0, bump);
2929 children.extend((0..self.0).map(|_| {
3030 li(bump)
crates/js-api/src/lib.rs+19−27
@@ -66,8 +66,7 @@ And here is a Rust rendering component that internally uses the JS rendering
6666component:
6767
6868```rust,no_run
69−use dodrio::bumpalo::Bump;
70−use dodrio::{Node, Render, Vdom};
69+use dodrio::{Node, Render, RenderContext, Vdom};
7170use dodrio_js_api::JsRender;
7271use js_sys::Object;
7372use wasm_bindgen::prelude::*;
@@ -101,15 +100,12 @@ impl GreetingViaJs {
101100/// And finally the `Render` implementation! This adds a `<p>` element and some
102101/// text around whatever the inner JS `Greeting` component renders.
103102impl Render for GreetingViaJs {
104− fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
105− where
106− 'a: 'bump,
107− {
103+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
108104 use dodrio::builder::*;
109− p(bump)
105+ p(cx.bump)
110106 .children([
111107 text("JavaScript says: "),
112− self.js.render(bump),
108+ self.js.render(cx),
113109 ])
114110 .finish()
115111 }
@@ -119,11 +115,7 @@ impl Render for GreetingViaJs {
119115 */
120116#![deny(missing_docs, missing_debug_implementations)]
121117
122−use dodrio::{
123− builder,
124− bumpalo::{self, Bump},
125− Node, Render,
126−};
118+use dodrio::{builder, bumpalo, Node, Render, RenderContext};
127119use futures::prelude::*;
128120use js_sys::{Object, Promise, Reflect};
129121use wasm_bindgen::prelude::*;
@@ -222,8 +214,8 @@ impl JsRender {
222214}
223215
224216impl Render for JsRender {
225− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
226− create(bump, self.render())
217+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
218+ create(cx, self.render())
227219 }
228220}
229221
@@ -231,9 +223,9 @@ fn has_property(obj: &Object, property: &str) -> bool {
231223 Reflect::has(obj, &property.into()).unwrap_or_default()
232224}
233225
234−fn create(bump: &Bump, val: JsValue) -> Node {
226+fn create<'bump>(cx: &mut RenderContext<'bump>, val: JsValue) -> Node<'bump> {
235227 if let Some(txt) = val.as_string() {
236− let text = bumpalo::collections::String::from_str_in(&txt, bump);
228+ let text = bumpalo::collections::String::from_str_in(&txt, cx.bump);
237229 return builder::text(text.into_bump_str());
238230 }
239231
@@ -248,12 +240,12 @@ fn create(bump: &Bump, val: JsValue) -> Node {
248240 );
249241
250242 let tag_name = elem.tag_name();
251− let tag_name = bumpalo::collections::String::from_str_in(&tag_name, bump);
243+ let tag_name = bumpalo::collections::String::from_str_in(&tag_name, cx.bump);
252244
253− builder::ElementBuilder::new(bump, tag_name.into_bump_str())
245+ builder::ElementBuilder::new(cx.bump, tag_name.into_bump_str())
254246 .listeners({
255247 let mut listeners =
256− bumpalo::collections::Vec::new_in(bump);
248+ bumpalo::collections::Vec::new_in(cx.bump);
257249 if has_property(&elem, "listeners") {
258250 let js_listeners = elem.listeners();
259251 listeners.reserve(js_listeners.length() as usize);
@@ -272,10 +264,10 @@ fn create(bump: &Bump, val: JsValue) -> Node {
272264 "listener objects returned by JS render methods must have an `callback` property"
273265 );
274266 let on = listener.on();
275− let on = bumpalo::collections::String::from_str_in(&on, bump);
267+ let on = bumpalo::collections::String::from_str_in(&on, cx.bump);
276268 let callback = listener.callback();
277269 let elem = elem.clone();
278− listeners.push(builder::on(bump, on.into_bump_str(), move |_root, vdom, event| {
270+ listeners.push(builder::on(cx.bump, on.into_bump_str(), move |_root, vdom, event| {
279271 let vdom = VdomWeak::new(vdom);
280272 let vdom: JsValue = vdom.into();
281273 if let Err(e) = callback.call2(&elem, &vdom, &event) {
@@ -287,7 +279,7 @@ fn create(bump: &Bump, val: JsValue) -> Node {
287279 listeners
288280 })
289281 .attributes({
290− let mut attributes = bumpalo::collections::Vec::new_in(bump);
282+ let mut attributes = bumpalo::collections::Vec::new_in(cx.bump);
291283 if has_property(&elem, "attributes") {
292284 let js_attributes = elem.attributes();
293285 attributes.reserve(js_attributes.length() as usize);
@@ -306,21 +298,21 @@ fn create(bump: &Bump, val: JsValue) -> Node {
306298 "attribute objects returned by JS render methods must have a `value` property"
307299 );
308300 let name = attribute.name();
309− let name = bumpalo::collections::String::from_str_in(&name, bump);
301+ let name = bumpalo::collections::String::from_str_in(&name, cx.bump);
310302 let value = attribute.value();
311− let value = bumpalo::collections::String::from_str_in(&value, bump);
303+ let value = bumpalo::collections::String::from_str_in(&value, cx.bump);
312304 attributes.push(builder::attr(name.into_bump_str(), value.into_bump_str()));
313305 });
314306 }
315307 attributes
316308 })
317309 .children({
318− let mut children = bumpalo::collections::Vec::new_in(bump);
310+ let mut children = bumpalo::collections::Vec::new_in(cx.bump);
319311 if has_property(&elem, "children") {
320312 let js_children = elem.children();
321313 children.reserve(js_children.length() as usize);
322314 js_children.for_each(&mut |child, _index, _array| {
323− children.push(create(bump, child));
315+ children.push(create(cx, child));
324316 });
325317 }
326318 children
examples/counter/src/lib.rs+6−7
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::{self, Bump};
2−use dodrio::Render;
1+use dodrio::{bumpalo, Render, RenderContext};
32use log::*;
43use wasm_bindgen::prelude::*;
54
@@ -28,15 +27,15 @@ impl Counter {
2827// The `Render` implementation for `Counter`s displays the current count and has
2928// buttons to increment and decrement the count.
3029impl Render for Counter {
31− fn render<'bump>(&self, bump: &'bump Bump) -> dodrio::Node<'bump> {
30+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> dodrio::Node<'bump> {
3231 use dodrio::builder::*;
3332
3433 // Stringify the count as a bump-allocated string.
35− let count = bumpalo::format!(in bump, "{}", self.count);
34+ let count = bumpalo::format!(in cx.bump, "{}", self.count);
3635
37− div(bump)
36+ div(cx.bump)
3837 .children([
39− button(bump)
38+ button(cx.bump)
4039 .on("click", |root, vdom, _event| {
4140 // Cast the root render component to a `Counter`, since
4241 // we know that's what it is.
@@ -52,7 +51,7 @@ impl Render for Counter {
5251 .children([text("+")])
5352 .finish(),
5453 text(count.into_bump_str()),
55− button(bump)
54+ button(cx.bump)
5655 .on("click", |root, vdom, _event| {
5756 // Same as above, but decrementing instead of incrementing.
5857 root.unwrap_mut::<Counter>().decrement();
examples/game-of-life/src/lib.rs+11−8
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::{self, Bump};
2−use dodrio::{Node, Render, Vdom};
1+use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
32use futures::prelude::*;
43use std::cell::RefCell;
54use std::rc::Rc;
@@ -129,17 +128,18 @@ impl Universe {
129128
130129/// The rendering implementation for our Game of Life.
131130impl Render for Universe {
132− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
131+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
133132 use dodrio::builder::*;
134133
135− let mut rows = bumpalo::collections::Vec::with_capacity_in(self.height as usize, bump);
134+ let mut rows = bumpalo::collections::Vec::with_capacity_in(self.height as usize, cx.bump);
136135
137136 for row in self.cells.chunks(self.width as usize) {
138− let mut cells = bumpalo::collections::Vec::with_capacity_in(self.width as usize, bump);
137+ let mut cells =
138+ bumpalo::collections::Vec::with_capacity_in(self.width as usize, cx.bump);
139139
140140 for cell in row {
141141 cells.push(
142− span(bump)
142+ span(cx.bump)
143143 .attr("class", "cell")
144144 .attr(
145145 "style",
@@ -152,10 +152,13 @@ impl Render for Universe {
152152 );
153153 }
154154
155− rows.push(div(bump).attr("class", "row").children(cells).finish());
155+ rows.push(div(cx.bump).attr("class", "row").children(cells).finish());
156156 }
157157
158− div(bump).attr("class", "universe").children(rows).finish()
158+ div(cx.bump)
159+ .attr("class", "universe")
160+ .children(rows)
161+ .finish()
159162 }
160163}
161164
examples/hello-world/src/lib.rs+5−8
@@ -1,8 +1,5 @@
1−use dodrio::{
2− builder::*,
3− bumpalo::{self, Bump},
4−};
5−use dodrio::{Node, Render, Vdom};
1+use dodrio::{builder::*, bumpalo};
2+use dodrio::{Node, Render, RenderContext, Vdom};
63use wasm_bindgen::prelude::*;
74
85/// A rendering component that displays a greeting.
@@ -14,10 +11,10 @@ struct Hello {
1411// The `Render` implementation describes how to render a `Hello` component into
1512// HTML.
1613impl Render for Hello {
17− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
18− let msg = bumpalo::format!(in bump, "Hello, {}!", self.who);
14+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
15+ let msg = bumpalo::format!(in cx.bump, "Hello, {}!", self.who);
1916 let msg = msg.into_bump_str();
20− p(bump).children([text(msg)]).finish()
17+ p(cx.bump).children([text(msg)]).finish()
2118 }
2219}
2320
examples/input-form/src/lib.rs+6−7
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::{self, Bump};
2−use dodrio::{Node, Render};
1+use dodrio::{bumpalo, Node, Render, RenderContext};
32use wasm_bindgen::prelude::*;
43use wasm_bindgen::JsCast;
54
@@ -25,16 +24,16 @@ impl SayHelloTo {
2524// The `Render` implementation has a text `<input>` and a `<div>` that shows a
2625// greeting to the `<input>`'s value.
2726impl Render for SayHelloTo {
28− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
27+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
2928 use dodrio::builder::*;
3029
31− div(bump)
30+ div(cx.bump)
3231 .children([
33− input(bump)
32+ input(cx.bump)
3433 .attr("type", "text")
3534 .attr(
3635 "value",
37− bumpalo::format!(in bump, "{}", self.who).into_bump_str(),
36+ bumpalo::format!(in cx.bump, "{}", self.who).into_bump_str(),
3837 )
3938 .on("input", |root, vdom, event| {
4039 // If the event's target is our input...
@@ -55,7 +54,7 @@ impl Render for SayHelloTo {
5554 vdom.schedule_render();
5655 })
5756 .finish(),
58− text(bumpalo::format!(in bump, "Hello, {}!", self.who).into_bump_str()),
57+ text(bumpalo::format!(in cx.bump, "Hello, {}!", self.who).into_bump_str()),
5958 ])
6059 .finish()
6160 }
examples/js-component/src/lib.rs+4−5
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::Bump;
2−use dodrio::{Node, Render, Vdom};
1+use dodrio::{Node, Render, RenderContext, Vdom};
32use dodrio_js_api::JsRender;
43use js_sys::Object;
54use wasm_bindgen::prelude::*;
@@ -33,10 +32,10 @@ impl GreetingViaJs {
3332/// Here's the `Render` implementation! This adds a `<p>` element and some text
3433/// around whatever the inner JS `Greeting` component renders.
3534impl Render for GreetingViaJs {
36− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
35+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
3736 use dodrio::builder::*;
38− p(bump)
39− .children([text("JavaScript says: "), self.js.render(bump)])
37+ p(cx.bump)
38+ .children([text("JavaScript says: "), self.js.render(cx)])
4039 .finish()
4140 }
4241}
examples/moire/src/lib.rs+42−28
@@ -1,8 +1,7 @@
11mod colors;
22
33use cfg_if::cfg_if;
4−use dodrio::bumpalo::{self, Bump};
5−use dodrio::{Node, Render, Vdom};
4+use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
65use std::cell::RefCell;
76use std::rc::Rc;
87use std::str;
@@ -107,7 +106,7 @@ impl Moire {
107106}
108107
109108impl Render for Moire {
110− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
109+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
111110 use dodrio::builder::*;
112111
113112 let elapsed = web_sys::window()
@@ -117,18 +116,18 @@ impl Render for Moire {
117116 .now();
118117 let elapsed = elapsed / 1600.0;
119118
120− main(bump)
121− .attr("style", self.moire_style(bump, elapsed))
119+ main(cx.bump)
120+ .attr("style", self.moire_style(cx, elapsed))
122121 .children([
123122 // The `<input>` that lets users control how many circles to
124123 // render.
125− input(bump)
124+ input(cx.bump)
126125 .attr("id", "circle-count")
127126 .attr("type", "range")
128127 .attr("min", "30")
129128 .attr("max", "500")
130129 .attr("value", {
131− let count = bumpalo::format!(in bump, "{}", self.count);
130+ let count = bumpalo::format!(in cx.bump, "{}", self.count);
132131 count.into_bump_str()
133132 })
134133 .on("change", |root, _vdom, event| {
@@ -141,8 +140,8 @@ impl Render for Moire {
141140 })
142141 .finish(),
143142 // And the actual concentric circles that form the Moire patterns.
144− self.orbiting_objects(bump, elapsed),
145− self.lemniscate_objects(bump, elapsed),
143+ self.orbiting_objects(cx, elapsed),
144+ self.lemniscate_objects(cx, elapsed),
146145 ])
147146 .finish()
148147 }
@@ -151,11 +150,11 @@ impl Render for Moire {
151150/// Rendering helper methods.
152151impl Moire {
153152 /// Generate the main Moire element's inline CSS styles.
154− fn moire_style<'bump>(&self, bump: &'bump Bump, elapsed: f64) -> &'bump str {
153+ fn moire_style<'bump>(&self, cx: &mut RenderContext<'bump>, elapsed: f64) -> &'bump str {
155154 let elapsed = elapsed / 3.0;
156155 let color = colors::get_interpolated_color(|(_, bg)| bg, elapsed % 1.0);
157156 let style = bumpalo::format!(
158− in bump,
157+ in cx.bump,
159158 "background-color: rgb({}, {}, {})",
160159 color.r,
161160 color.g,
@@ -165,69 +164,84 @@ impl Moire {
165164 }
166165
167166 /// Generate the orbiting circle objects.
168− fn orbiting_objects<'bump>(&self, bump: &'bump Bump, elapsed: f64) -> Node<'bump> {
167+ fn orbiting_objects<'bump>(&self, cx: &mut RenderContext<'bump>, elapsed: f64) -> Node<'bump> {
169168 const D: f64 = 200.0;
170169 let x = elapsed.sin() * D;
171170 let y = elapsed.cos() * D;
172− self.moving_object(bump, elapsed, x, y)
171+ self.moving_object(cx, elapsed, x, y)
173172 }
174173
175174 /// Generate the lemniscate circle objects that interact with the orbiting
176175 /// circle objects to form Moire patterns.
177− fn lemniscate_objects<'bump>(&self, bump: &'bump Bump, elapsed: f64) -> Node<'bump> {
176+ fn lemniscate_objects<'bump>(
177+ &self,
178+ cx: &mut RenderContext<'bump>,
179+ elapsed: f64,
180+ ) -> Node<'bump> {
178181 const A: f64 = 150.0;
179182 let x = elapsed.sin() * A;
180183 let y = elapsed.sin() * elapsed.cos() * A;
181− self.moving_object(bump, elapsed, x, y)
184+ self.moving_object(cx, elapsed, x, y)
182185 }
183186
184187 fn moving_object<'a, 'bump>(
185188 &'a self,
186− bump: &'bump Bump,
189+ cx: &mut RenderContext<'bump>,
187190 elapsed: f64,
188191 x: f64,
189192 y: f64,
190193 ) -> Node<'bump> {
191194 use dodrio::builder::*;
192− div(bump)
195+ div(cx.bump)
193196 .attr("class", "object")
194− .attr("style", self.moving_object_style(bump, x, y))
195− .children([self.circle(bump, elapsed, self.count)])
197+ .attr("style", self.moving_object_style(cx, x, y))
198+ .children([self.circle(cx, elapsed, self.count)])
196199 .finish()
197200 }
198201
199202 /// Generate inline CSS styles for a moving object.
200− fn moving_object_style<'bump>(&self, bump: &'bump Bump, x: f64, y: f64) -> &'bump str {
201− bumpalo::format!(in bump, "left: {}px; top: {}px;", x, y).into_bump_str()
203+ fn moving_object_style<'bump>(
204+ &self,
205+ cx: &mut RenderContext<'bump>,
206+ x: f64,
207+ y: f64,
208+ ) -> &'bump str {
209+ bumpalo::format!(in cx.bump, "left: {}px; top: {}px;", x, y).into_bump_str()
202210 }
203211
204212 /// Recursively generate `self.count` concentric circles.
205− fn circle<'bump>(&self, bump: &'bump Bump, elapsed: f64, n: u32) -> Node<'bump> {
213+ fn circle<'bump>(&self, cx: &mut RenderContext<'bump>, elapsed: f64, n: u32) -> Node<'bump> {
206214 use dodrio::builder::*;
207215
208216 let r = n * 16;
209217
210− let mut circle = div(bump)
218+ let mut circle = div(cx.bump)
211219 .attr("class", "circle")
212220 .attr("data-radius", {
213− let r = bumpalo::format!(in bump, "{}", r);
221+ let r = bumpalo::format!(in cx.bump, "{}", r);
214222 r.into_bump_str()
215223 })
216− .attr("style", self.circle_style(bump, elapsed, n, r));
224+ .attr("style", self.circle_style(cx, elapsed, n, r));
217225
218226 if n > 0 {
219− circle = circle.child(self.circle(bump, elapsed, n - 1));
227+ circle = circle.child(self.circle(cx, elapsed, n - 1));
220228 }
221229
222230 circle.finish()
223231 }
224232
225233 /// Generate inline CSS styles for a circle.
226− fn circle_style<'bump>(&self, bump: &'bump Bump, elapsed: f64, n: u32, r: u32) -> &'bump str {
234+ fn circle_style<'bump>(
235+ &self,
236+ cx: &mut RenderContext<'bump>,
237+ elapsed: f64,
238+ n: u32,
239+ r: u32,
240+ ) -> &'bump str {
227241 let (border, alpha) = self.circle_color(elapsed, n);
228242 let margin = r / 2 + 3;
229243 let style = bumpalo::format!(
230− in bump,
244+ in cx.bump,
231245 "border-color: rgba({}, {}, {}, {});\
232246 margin-left: -{}px;\
233247 margin-top: -{}px;\
examples/sierpinski-triangle/src/lib.rs+29−20
@@ -1,5 +1,4 @@
1−use dodrio::bumpalo::{self, Bump};
2−use dodrio::{Node, Render, Vdom};
1+use dodrio::{bumpalo, Node, Render, RenderContext, Vdom};
32use std::cell::RefCell;
43use std::rc::Rc;
54use wasm_bindgen::prelude::*;
@@ -18,11 +17,15 @@ impl Container {
1817 }
1918
2019 // Generate the container style to fluctuate triangle width.
21− fn container_transform<'bump>(&self, bump: &'bump Bump, elapsed: f64) -> &'bump str {
20+ fn container_transform<'bump>(
21+ &self,
22+ cx: &mut RenderContext<'bump>,
23+ elapsed: f64,
24+ ) -> &'bump str {
2225 let t = elapsed % 10.0;
2326 let scale = 0.45 + (if t > 5.0 { 10.0 - t } else { t }) / 40.0;
2427 let transform = bumpalo::format!(
25− in bump,
28+ in cx.bump,
2629 "transform: scaleX({}) scaleY(0.7) translateZ(0.1px)",
2730 scale
2831 );
@@ -30,11 +33,11 @@ impl Container {
3033 }
3134
3235 // Generate the dot's position on the grid.
33− fn dot_style<'bump>(&self, bump: &'bump Bump, x: f64, y: f64) -> &'bump str {
36+ fn dot_style<'bump>(&self, cx: &mut RenderContext<'bump>, x: f64, y: f64) -> &'bump str {
3437 let s = self.size * 1.3;
3538 let radius = s / 2.0;
3639 let styles = bumpalo::format!(
37− in bump,
40+ in cx.bump,
3841 r#"
3942 width: {}px;
4043 height: {}px;
@@ -49,14 +52,20 @@ impl Container {
4952 }
5053
5154 // Create a dot node.
52− fn dot<'bump>(&self, bump: &'bump Bump, x: f64, y: f64, content: u32) -> Node<'bump> {
55+ fn dot<'bump>(
56+ &self,
57+ cx: &mut RenderContext<'bump>,
58+ x: f64,
59+ y: f64,
60+ content: u32,
61+ ) -> Node<'bump> {
5362 use dodrio::builder::{div, text};
5463
55− div(bump)
64+ div(cx.bump)
5665 .attr("class", "dot")
57− .attr("style", self.dot_style(bump, x, y))
66+ .attr("style", self.dot_style(cx, x, y))
5867 .child(text(
59− bumpalo::format!(in bump, "{}", content).into_bump_str(),
68+ bumpalo::format!(in cx.bump, "{}", content).into_bump_str(),
6069 ))
6170 .finish()
6271 }
@@ -64,7 +73,7 @@ impl Container {
6473 // Create a flattened vector of dot nodes (arranged in recursive triangles)
6574 fn triangle<'bump>(
6675 &self,
67− bump: &'bump Bump,
76+ cx: &mut RenderContext<'bump>,
6877 x: f64,
6978 y: f64,
7079 s: f64,
@@ -72,23 +81,23 @@ impl Container {
7281 children: &mut bumpalo::collections::Vec<Node<'bump>>,
7382 ) {
7483 if s <= self.size {
75− children.push(self.dot(bump, x, y, content));
84+ children.push(self.dot(cx, x, y, content));
7685 return;
7786 }
7887
7988 let s = s / 2.0;
8089
8190 // Top of triangle
82− self.triangle(bump, x, y - (s / 2.0), s, content, children);
91+ self.triangle(cx, x, y - (s / 2.0), s, content, children);
8392 // Bottom-left of triangle
84− self.triangle(bump, x - s, y + (s / 2.0), s, content, children);
93+ self.triangle(cx, x - s, y + (s / 2.0), s, content, children);
8594 // Bottom-right of triangle
86− self.triangle(bump, x + s, y + (s / 2.0), s, content, children);
95+ self.triangle(cx, x + s, y + (s / 2.0), s, content, children);
8796 }
8897}
8998
9099impl Render for Container {
91− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
100+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
92101 use dodrio::builder::div;
93102
94103 let elapsed = web_sys::window()
@@ -100,12 +109,12 @@ impl Render for Container {
100109
101110 let modulus = elapsed as u32 % 10;
102111
103− let mut children = bumpalo::collections::Vec::new_in(bump);
104− self.triangle(bump, 0.0, 0.0, 1000.0, modulus, &mut children);
112+ let mut children = bumpalo::collections::Vec::new_in(cx.bump);
113+ self.triangle(cx, 0.0, 0.0, 1000.0, modulus, &mut children);
105114
106− div(bump)
115+ div(cx.bump)
107116 .attr("class", "container")
108− .attr("style", self.container_transform(bump, elapsed))
117+ .attr("style", self.container_transform(cx, elapsed))
109118 .children(children)
110119 .finish()
111120 }
examples/todomvc/src/todo.rs+11−11
@@ -1,7 +1,7 @@
11//! Type definition and `dodrio::Render` implementation for a single todo item.
22
33use crate::keys;
4−use dodrio::{bumpalo::Bump, Node, Render, RootRender, VdomWeak};
4+use dodrio::{Node, Render, RenderContext, RootRender, VdomWeak};
55use serde::{Deserialize, Serialize};
66use std::marker::PhantomData;
77use wasm_bindgen::{prelude::*, JsCast};
@@ -93,7 +93,7 @@ impl<C> Todo<C> {
9393}
9494
9595impl<C: TodoActions> Render for Todo<C> {
96− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
96+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
9797 use dodrio::{
9898 builder::*,
9999 bumpalo::{self, collections::String},
@@ -101,11 +101,11 @@ impl<C: TodoActions> Render for Todo<C> {
101101
102102 let id = self.id;
103103 let title = self.edits.as_ref().unwrap_or(&self.title);
104− let title = bumpalo::format!(in bump, "{}", title).into_bump_str();
104+ let title = bumpalo::format!(in cx.bump, "{}", title).into_bump_str();
105105
106− li(bump)
106+ li(cx.bump)
107107 .attr("class", {
108− let mut class = String::new_in(bump);
108+ let mut class = String::new_in(cx.bump);
109109 if self.completed {
110110 class.push_str("completed ");
111111 }
@@ -115,10 +115,10 @@ impl<C: TodoActions> Render for Todo<C> {
115115 class.into_bump_str()
116116 })
117117 .children([
118− div(bump)
118+ div(cx.bump)
119119 .attr("class", "view")
120120 .children([
121− input(bump)
121+ input(cx.bump)
122122 .attr("class", "toggle")
123123 .attr("type", "checkbox")
124124 .bool_attr("checked", self.completed)
@@ -126,13 +126,13 @@ impl<C: TodoActions> Render for Todo<C> {
126126 C::toggle_completed(root, vdom, id);
127127 })
128128 .finish(),
129− label(bump)
129+ label(cx.bump)
130130 .on("dblclick", move |root, vdom, _event| {
131131 C::begin_editing(root, vdom, id);
132132 })
133133 .children([text(title)])
134134 .finish(),
135− button(bump)
135+ button(cx.bump)
136136 .attr("class", "destroy")
137137 .on("click", move |root, vdom, _event| {
138138 C::delete(root, vdom, id);
@@ -140,13 +140,13 @@ impl<C: TodoActions> Render for Todo<C> {
140140 .finish(),
141141 ])
142142 .finish(),
143− input(bump)
143+ input(cx.bump)
144144 .attr("class", "edit")
145145 .attr("value", title)
146146 .attr("name", "title")
147147 .attr(
148148 "id",
149− bumpalo::format!(in bump, "todo-{}", id).into_bump_str(),
149+ bumpalo::format!(in cx.bump, "todo-{}", id).into_bump_str(),
150150 )
151151 .on("input", move |root, vdom, event| {
152152 let input = event
examples/todomvc/src/todos.rs+33−33
@@ -5,10 +5,7 @@ use crate::controller::Controller;
55use crate::todo::{Todo, TodoActions};
66use crate::visibility::Visibility;
77use crate::{keys, utils};
8−use dodrio::{
9− bumpalo::{self, Bump},
10− Node, Render, RootRender, VdomWeak,
11−};
8+use dodrio::{bumpalo, Node, Render, RenderContext, RootRender, VdomWeak};
129use serde::{Deserialize, Serialize};
1310use std::marker::PhantomData;
1411use std::mem;
@@ -136,14 +133,14 @@ impl<C> Todos<C> {
136133
137134/// Rendering helpers.
138135impl<C: TodosActions> Todos<C> {
139− fn header<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
136+ fn header<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
140137 use dodrio::builder::*;
141138
142− header(bump)
139+ header(cx.bump)
143140 .attr("class", "header")
144141 .children([
145− h1(bump).children([text("todos")]).finish(),
146− input(bump)
142+ h1(cx.bump).children([text("todos")]).finish(),
143+ input(cx.bump)
147144 .on("input", |root, vdom, event| {
148145 let input = event
149146 .target()
@@ -162,17 +159,17 @@ impl<C: TodosActions> Todos<C> {
162159 .attr("autofocus", "")
163160 .attr(
164161 "value",
165− bumpalo::format!(in bump, "{}", self.draft).into_bump_str(),
162+ bumpalo::format!(in cx.bump, "{}", self.draft).into_bump_str(),
166163 )
167164 .finish(),
168165 ])
169166 .finish()
170167 }
171168
172− fn todos_list<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
169+ fn todos_list<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
173170 use dodrio::{builder::*, bumpalo::collections::Vec};
174171
175− let mut todos = Vec::with_capacity_in(self.todos.len(), bump);
172+ let mut todos = Vec::with_capacity_in(self.todos.len(), cx.bump);
176173 todos.extend(
177174 self.todos
178175 .iter()
@@ -181,10 +178,10 @@ impl<C: TodosActions> Todos<C> {
181178 Visibility::Active => !t.is_complete(),
182179 Visibility::Completed => t.is_complete(),
183180 })
184− .map(|t| t.render(bump)),
181+ .map(|t| t.render(cx)),
185182 );
186183
187− section(bump)
184+ section(cx.bump)
188185 .attr("class", "main")
189186 .attr(
190187 "visibility",
@@ -195,7 +192,7 @@ impl<C: TodosActions> Todos<C> {
195192 },
196193 )
197194 .children([
198− input(bump)
195+ input(cx.bump)
199196 .attr("class", "toggle-all")
200197 .attr("id", "toggle-all")
201198 .attr("type", "checkbox")
@@ -205,16 +202,19 @@ impl<C: TodosActions> Todos<C> {
205202 C::toggle_all(root, vdom);
206203 })
207204 .finish(),
208− label(bump)
205+ label(cx.bump)
209206 .attr("for", "toggle-all")
210207 .children([text("Mark all as complete")])
211208 .finish(),
212− ul(bump).attr("class", "todo-list").children(todos).finish(),
209+ ul(cx.bump)
210+ .attr("class", "todo-list")
211+ .children(todos)
212+ .finish(),
213213 ])
214214 .finish()
215215 }
216216
217− fn footer<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
217+ fn footer<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
218218 use dodrio::builder::*;
219219
220220 let completed_count = self.todos.iter().filter(|t| t.is_complete()).count();
@@ -224,36 +224,36 @@ impl<C: TodosActions> Todos<C> {
224224 } else {
225225 " items left"
226226 };
227− let incomplete_count = bumpalo::format!(in bump, "{}", incomplete_count);
227+ let incomplete_count = bumpalo::format!(in cx.bump, "{}", incomplete_count);
228228
229229 let clear_completed_text = bumpalo::format!(
230− in bump,
230+ in cx.bump,
231231 "Clear completed ({})",
232232 self.todos.iter().filter(|t| t.is_complete()).count()
233233 );
234234
235− footer(bump)
235+ footer(cx.bump)
236236 .attr("class", "footer")
237237 .bool_attr("hidden", self.todos.is_empty())
238238 .children([
239− span(bump)
239+ span(cx.bump)
240240 .attr("class", "todo-count")
241241 .children([
242− strong(bump)
242+ strong(cx.bump)
243243 .children([text(incomplete_count.into_bump_str())])
244244 .finish(),
245245 text(items_left),
246246 ])
247247 .finish(),
248− ul(bump)
248+ ul(cx.bump)
249249 .attr("class", "filters")
250250 .children([
251− self.visibility_swap(bump, "#/", Visibility::All),
252− self.visibility_swap(bump, "#/active", Visibility::Active),
253− self.visibility_swap(bump, "#/completed", Visibility::Completed),
251+ self.visibility_swap(cx, "#/", Visibility::All),
252+ self.visibility_swap(cx, "#/active", Visibility::Active),
253+ self.visibility_swap(cx, "#/completed", Visibility::Completed),
254254 ])
255255 .finish(),
256− button(bump)
256+ button(cx.bump)
257257 .on("click", |root, vdom, _event| {
258258 C::delete_completed(root, vdom);
259259 })
@@ -267,17 +267,17 @@ impl<C: TodosActions> Todos<C> {
267267
268268 fn visibility_swap<'bump>(
269269 &self,
270− bump: &'bump Bump,
270+ cx: &mut RenderContext<'bump>,
271271 url: &'static str,
272272 target_vis: Visibility,
273273 ) -> Node<'bump> {
274274 use dodrio::builder::*;
275275
276− li(bump)
276+ li(cx.bump)
277277 .on("click", move |root, vdom, _event| {
278278 C::change_visibility(root, vdom, target_vis);
279279 })
280− .children([a(bump)
280+ .children([a(cx.bump)
281281 .attr("href", url)
282282 .attr(
283283 "class",
@@ -294,11 +294,11 @@ impl<C: TodosActions> Todos<C> {
294294}
295295
296296impl<C: TodosActions> Render for Todos<C> {
297− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
297+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
298298 use dodrio::builder::*;
299299
300− div(bump)
301− .children([self.header(bump), self.todos_list(bump), self.footer(bump)])
300+ div(cx.bump)
301+ .children([self.header(cx), self.todos_list(cx), self.footer(cx)])
302302 .finish()
303303 }
304304}
src/lib.rs+9−9
@@ -3,7 +3,7 @@
33//! ## Example
44//!
55//! ```no_run
6−//! use dodrio::{bumpalo::Bump, Attribute, Node, Render};
6+//! use dodrio::{bumpalo, Attribute, Node, Render, RenderContext};
77//! use wasm_bindgen::UnwrapThrowExt;
88//!
99//! /// A component that greets someone.
@@ -19,15 +19,13 @@
1919//! }
2020//!
2121//! impl<'who> Render for Hello<'who> {
22−//! fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
23−//! where
24−//! 'a: 'bump,
25−//! {
22+//! fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
2623//! use dodrio::builder::*;
2724//!
28−//! let id = bumpalo::format!(in bump, "hello-{}", self.who);
25+//! let id = bumpalo::format!(in cx.bump, "hello-{}", self.who);
26+//! let who = bumpalo::collections::String::from_str_in(self.who, cx.bump).into_bump_str();
2927//!
30−//! div(bump)
28+//! div(cx.bump)
3129//! .attr("id", id.into_bump_str())
3230//! .on("click", |root, _vdom, _event| {
3331//! let hello = root.unwrap_mut::<Hello>();
@@ -37,9 +35,9 @@
3735//! })
3836//! .children([
3937//! text("Hello, "),
40−//! strong(bump)
38+//! strong(cx.bump)
4139//! .children([
42−//! text(self.who),
40+//! text(who),
4341//! text("!"),
4442//! ])
4543//! .finish(),
@@ -70,6 +68,7 @@ pub mod change_list;
7068mod events;
7169mod node;
7270mod render;
71+mod render_context;
7372mod vdom;
7473
7574pub mod builder;
@@ -77,6 +76,7 @@ pub mod builder;
7776// Re-export items at the top level.
7877pub use self::node::{Attribute, ElementNode, Listener, ListenerCallback, Node, TextNode};
7978pub use self::render::{Render, RootRender};
79+pub use self::render_context::RenderContext;
8080pub use self::vdom::{Vdom, VdomWeak};
8181
8282cfg_if::cfg_if! {
src/render.rs+15−18
@@ -1,5 +1,4 @@
1−use crate::Node;
2−use bumpalo::Bump;
1+use crate::{Node, RenderContext};
32use std::any::Any;
43use std::rc::Rc;
54use wasm_bindgen::UnwrapThrowExt;
@@ -11,27 +10,25 @@ use wasm_bindgen::UnwrapThrowExt;
1110///
1211/// ## `Bump` Allocation
1312///
14−/// `Render` implementations can use the provided `Bump` for very fast
15−/// allocation for anything that needs to be allocated during rendering.
13+/// `Render` implementations can use the `Bump` inside the provided
14+/// `RenderContext` for very fast allocation for anything that needs to be
15+/// temporarily allocated during rendering.
1616///
1717/// ## Example
1818///
1919/// ```no_run
20−/// use dodrio::{bumpalo::Bump, Node, Render};
20+/// use dodrio::{Node, Render, RenderContext};
2121///
2222/// pub struct MyComponent;
2323///
2424/// impl Render for MyComponent {
25−/// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
26−/// where
27−/// 'a: 'bump
28−/// {
25+/// fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
2926/// use dodrio::builder::*;
3027///
31−/// p(bump)
28+/// p(cx.bump)
3229/// .children([
3330/// text("This is "),
34−/// strong(bump).children([text("my component")]).finish(),
31+/// strong(cx.bump).children([text("my component")]).finish(),
3532/// text(" rendered!"),
3633/// ])
3734/// .finish()
@@ -39,17 +36,17 @@ use wasm_bindgen::UnwrapThrowExt;
3936/// }
4037/// ```
4138pub trait Render {
42− /// Render `self` as a virtual DOM. Use the given `Bump` for temporary
43− /// allocations.
44− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump>;
39+ /// Render `self` as a virtual DOM. Use the given context's `Bump` for
40+ /// temporary allocations.
41+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump>;
4542}
4643
4744impl<'r, R> Render for &'r R
4845where
4946 R: Render,
5047{
51− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
52− (**self).render(bump)
48+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
49+ (**self).render(cx)
5350 }
5451}
5552
@@ -57,8 +54,8 @@ impl<R> Render for Rc<R>
5754where
5855 R: Render,
5956{
60− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
61− (**self).render(bump)
57+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
58+ (**self).render(cx)
6259 }
6360}
6461
src/render_context.rsnew file+38
@@ -0,0 +1,38 @@
1+use bumpalo::Bump;
2+
3+/// Common context available to all `Render` implementations.
4+///
5+/// Notably, the `RenderContext` gives access to the bump arena that the virtual
6+/// DOM should be allocated within. This is available via the `bump` field.
7+#[derive(Debug)]
8+pub struct RenderContext<'bump> {
9+ /// The underlying bump arena that virtual DOMs are rendered into.
10+ ///
11+ /// ## Example
12+ ///
13+ /// ```
14+ /// use dodrio::RenderContext;
15+ ///
16+ /// // Given a rendering context, allocate an i32 inside its bump arena.
17+ /// fn foo<'a>(cx: &mut RenderContext<'a>) -> &'a mut i32 {
18+ /// cx.bump.alloc(42)
19+ /// }
20+ /// ```
21+ pub bump: &'bump Bump,
22+
23+ // Prevent exhaustive matching on the rendering context, so we can add more
24+ // members in a semver-compatible way.
25+ _non_exhaustive: (),
26+}
27+
28+impl<'bump> RenderContext<'bump> {
29+ // Don't use this! For internal use only! Subject to change in breaking ways
30+ // without a breaking version bump!
31+ #[doc(hidden)]
32+ pub fn new(bump: &'bump Bump) -> Self {
33+ RenderContext {
34+ bump,
35+ _non_exhaustive: (),
36+ }
37+ }
38+}
src/vdom.rs+3−5
@@ -2,6 +2,7 @@ use super::change_list::ChangeList;
22use super::node::{Attribute, ElementNode, Listener, Node, TextNode};
33use super::RootRender;
44use crate::events::EventsRegistry;
5+use crate::RenderContext;
56use bumpalo::Bump;
67use futures::future::Future;
78use std::cell::Cell;
@@ -265,11 +266,8 @@ impl VdomInnerExclusive {
265266 self.dom_buffers[1].reset();
266267
267268 // Render the new current contents into the inactive bump arena.
268− let new_contents = self
269− .component
270− .as_ref()
271− .unwrap_throw()
272− .render(&self.dom_buffers[1]);
269+ let mut cx = RenderContext::new(&self.dom_buffers[1]);
270+ let new_contents = self.component.as_ref().unwrap_throw().render(&mut cx);
273271 let new_contents = extend_node_lifetime(new_contents);
274272
275273 // Diff the old contents with the new contents.
tests/web/events.rs+5−6
@@ -1,6 +1,5 @@
11use super::create_element;
2−use bumpalo::Bump;
3−use dodrio::{Node, Render, Vdom};
2+use dodrio::{Node, Render, RenderContext, Vdom};
43use futures::Future;
54use std::cell::{Cell, RefCell};
65use std::rc::Rc;
@@ -26,9 +25,9 @@ impl EventContainer {
2625}
2726
2827impl Render for EventContainer {
29− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
28+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
3029 use dodrio::builder::*;
31− div(bump)
30+ div(cx.bump)
3231 .attr("id", "target")
3332 .on(self.event, |root, _scheduler, _event| {
3433 (root.unwrap_mut::<EventContainer>().on_event)();
@@ -135,13 +134,13 @@ impl ListensOnlyOnFirstRender {
135134}
136135
137136impl Render for ListensOnlyOnFirstRender {
138− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
137+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
139138 use dodrio::builder::*;
140139
141140 let count = self.count.get();
142141 self.count.set(count + 1);
143142
144− let mut elem = div(bump).attr("id", "target");
143+ let mut elem = div(cx.bump).attr("id", "target");
145144 if count == 0 {
146145 elem = elem.on("click", |root, _scheduler, _event| {
147146 (root.unwrap_mut::<ListensOnlyOnFirstRender>().callback)();
tests/web/js_api.rs+12−12
@@ -1,5 +1,5 @@
11use super::{assert_rendered, create_element, RenderFn};
2−use dodrio::{builder::*, bumpalo::Bump, Node, Render, Vdom};
2+use dodrio::{builder::*, Node, Render, RenderContext, Vdom};
33use dodrio_js_api::JsRender;
44use futures::prelude::*;
55use futures::sync::oneshot;
@@ -37,10 +37,10 @@ impl WrapJs {
3737}
3838
3939impl Render for WrapJs {
40− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
41− div(bump)
40+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
41+ div(cx.bump)
4242 .attr("class", "wrap-js")
43− .children([self.inner.render(bump)])
43+ .children([self.inner.render(cx)])
4444 .finish()
4545 }
4646}
@@ -106,14 +106,14 @@ fn can_use_js_rendering_components() -> impl Future<Item = (), Error = JsValue>
106106 let vdom = Rc::new(Vdom::new(&container, component));
107107 assert_rendered(
108108 &container,
109− &RenderFn(|bump| {
110− div(bump)
109+ &RenderFn(|cx| {
110+ div(cx.bump)
111111 .attr("class", "wrap-js")
112− .children([span(bump)
112+ .children([span(cx.bump)
113113 .attr("class", "js-component")
114114 .children([
115115 text("Here is some plain text"),
116− b(bump)
116+ b(cx.bump)
117117 .children([text("...and here is some bold text")])
118118 .finish(),
119119 text("0"),
@@ -135,14 +135,14 @@ fn can_use_js_rendering_components() -> impl Future<Item = (), Error = JsValue>
135135 .map(move |_| {
136136 assert_rendered(
137137 &container,
138− &RenderFn(|bump| {
139− div(bump)
138+ &RenderFn(|cx| {
139+ div(cx.bump)
140140 .attr("class", "wrap-js")
141− .children([span(bump)
141+ .children([span(cx.bump)
142142 .attr("class", "js-component")
143143 .children([
144144 text("Here is some plain text"),
145− b(bump)
145+ b(cx.bump)
146146 .children([text("...and here is some bold text")])
147147 .finish(),
148148 text("1"),
tests/web/main.rs+7−6
@@ -3,7 +3,7 @@
33#![cfg(target_arch = "wasm32")]
44
55use bumpalo::Bump;
6−use dodrio::{Attribute, Node, Render, Vdom};
6+use dodrio::{Attribute, Node, Render, RenderContext, Vdom};
77use futures::prelude::*;
88use log::*;
99use std::rc::Rc;
@@ -49,7 +49,8 @@ pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
4949 init_logging();
5050
5151 let bump = &Bump::new();
52− let node = r.render(bump);
52+ let cx = &mut RenderContext::new(bump);
53+ let node = r.render(cx);
5354 let child = container
5455 .first_child()
5556 .expect("container does not have anything rendered into it?");
@@ -133,14 +134,14 @@ pub fn assert_rendered<R: Render>(container: &web_sys::Element, r: &R) {
133134/// Use the function `F` to render.
134135pub struct RenderFn<F>(F)
135136where
136− F: for<'bump> Fn(&'bump Bump) -> Node<'bump>;
137+ F: for<'bump> Fn(&mut RenderContext<'bump>) -> Node<'bump>;
137138
138139impl<F> Render for RenderFn<F>
139140where
140− F: for<'bump> Fn(&'bump Bump) -> Node<'bump>,
141+ F: for<'bump> Fn(&mut RenderContext<'bump>) -> Node<'bump>,
141142{
142− fn render<'bump>(&self, bump: &'bump Bump) -> Node<'bump> {
143− (self.0)(bump)
143+ fn render<'bump>(&self, cx: &mut RenderContext<'bump>) -> Node<'bump> {
144+ (self.0)(cx)
144145 }
145146}
146147
tests/web/render.rs+85−85
@@ -6,7 +6,7 @@ use wasm_bindgen_test::*;
66
77#[wasm_bindgen_test]
88fn render_initial_text() {
9− let hello = Rc::new(RenderFn(|_bump| text("hello")));
9+ let hello = Rc::new(RenderFn(|_cx| text("hello")));
1010
1111 let container = create_element("div");
1212 let _vdom = Vdom::new(&container, hello.clone());
@@ -15,10 +15,10 @@ fn render_initial_text() {
1515
1616#[wasm_bindgen_test]
1717fn render_initial_node() {
18− let hello = Rc::new(RenderFn(|bump| {
19− div(bump)
18+ let hello = Rc::new(RenderFn(|cx| {
19+ div(cx.bump)
2020 .attr("id", "hello-world")
21− .children([text("Hello "), span(bump).child(text("World!")).finish()])
21+ .children([text("Hello "), span(cx.bump).child(text("World!")).finish()])
2222 .finish()
2323 }));
2424
@@ -30,96 +30,96 @@ fn render_initial_node() {
3030#[wasm_bindgen_test]
3131fn container_is_emptied_upon_drop() {
3232 let container = create_element("div");
33− let vdom = Vdom::new(&container, RenderFn(|_bump| text("blah")));
33+ let vdom = Vdom::new(&container, RenderFn(|_cx| text("blah")));
3434 drop(vdom);
3535 assert!(container.first_child().is_none());
3636}
3737
3838before_after! {
3939 same_text {
40− before(_bump) {
40+ before(_cx) {
4141 text("hello")
4242 }
43− after(_bump) {
43+ after(_cx) {
4444 text("hello")
4545 }
4646 }
4747
4848 update_text {
49− before(_bump) {
49+ before(_cx) {
5050 text("before")
5151 }
52− after(_bump) {
52+ after(_cx) {
5353 text("after")
5454 }
5555 }
5656
5757 replace_text_with_elem {
58− before(_bump) {
58+ before(_cx) {
5959 text("before")
6060 }
61− after(bump) {
62− div(bump).finish()
61+ after(cx) {
62+ div(cx.bump).finish()
6363 }
6464 }
6565
6666 replace_elem_with_text {
67− before(bump) {
68− div(bump).finish()
67+ before(cx) {
68+ div(cx.bump).finish()
6969 }
70− after(_bump) {
70+ after(_cx) {
7171 text("before")
7272 }
7373 }
7474
7575 same_elem {
76− before(bump) {
77− div(bump).finish()
76+ before(cx) {
77+ div(cx.bump).finish()
7878 }
79− after(bump) {
80− div(bump).finish()
79+ after(cx) {
80+ div(cx.bump).finish()
8181 }
8282 }
8383
8484 elems_with_different_tag_names {
85− before(bump) {
86− span(bump).finish()
85+ before(cx) {
86+ span(cx.bump).finish()
8787 }
88− after(bump) {
89− div(bump).finish()
88+ after(cx) {
89+ div(cx.bump).finish()
9090 }
9191 }
9292
9393 same_tag_name_update_attribute {
94− before(bump) {
95− div(bump).attr("value", "1").finish()
94+ before(cx) {
95+ div(cx.bump).attr("value", "1").finish()
9696 }
97− after(bump) {
98− div(bump).attr("value", "2").finish()
97+ after(cx) {
98+ div(cx.bump).attr("value", "2").finish()
9999 }
100100 }
101101
102102 same_tag_name_remove_attribute {
103− before(bump) {
104− div(bump).attr("value", "1").finish()
103+ before(cx) {
104+ div(cx.bump).attr("value", "1").finish()
105105 }
106− after(bump) {
107− div(bump).finish()
106+ after(cx) {
107+ div(cx.bump).finish()
108108 }
109109 }
110110
111111 same_tag_name_add_attribute {
112− before(bump) {
113− div(bump).finish()
112+ before(cx) {
113+ div(cx.bump).finish()
114114 }
115− after(bump) {
116− div(bump).attr("value", "2").finish()
115+ after(cx) {
116+ div(cx.bump).attr("value", "2").finish()
117117 }
118118 }
119119
120120 same_tag_name_many_attributes {
121− before(bump) {
122− div(bump)
121+ before(cx) {
122+ div(cx.bump)
123123 .attr("before-1", "1")
124124 .attr("shared-1", "1")
125125 .attr("modified-1", "1")
@@ -131,8 +131,8 @@ before_after! {
131131 .attr("modified-3", "3")
132132 .finish()
133133 }
134− after(bump) {
135− div(bump)
134+ after(cx) {
135+ div(cx.bump)
136136 .attr("after-1", "1")
137137 .attr("shared-1", "1")
138138 .attr("modified-1", "100")
@@ -147,108 +147,108 @@ before_after! {
147147 }
148148
149149 same_tag_same_children {
150− before(bump) {
151− div(bump).child(text("child")).finish()
150+ before(cx) {
151+ div(cx.bump).child(text("child")).finish()
152152 }
153− after(bump) {
154− div(bump).child(text("child")).finish()
153+ after(cx) {
154+ div(cx.bump).child(text("child")).finish()
155155 }
156156 }
157157
158158 same_tag_update_child {
159− before(bump) {
160− div(bump).child(text("before")).finish()
159+ before(cx) {
160+ div(cx.bump).child(text("before")).finish()
161161 }
162− after(bump) {
163− div(bump).child(text("after")).finish()
162+ after(cx) {
163+ div(cx.bump).child(text("after")).finish()
164164 }
165165 }
166166
167167 same_tag_add_child {
168− before(bump) {
169− div(bump).finish()
168+ before(cx) {
169+ div(cx.bump).finish()
170170 }
171− after(bump) {
172− div(bump).child(text("child")).finish()
171+ after(cx) {
172+ div(cx.bump).child(text("child")).finish()
173173 }
174174 }
175175
176176 same_tag_remove_child {
177− before(bump) {
178− div(bump).child(text("child")).finish()
177+ before(cx) {
178+ div(cx.bump).child(text("child")).finish()
179179 }
180− after(bump) {
181− div(bump).finish()
180+ after(cx) {
181+ div(cx.bump).finish()
182182 }
183183 }
184184
185185 same_tag_update_many_children {
186− before(bump) {
187− div(bump)
186+ before(cx) {
187+ div(cx.bump)
188188 .children([
189− div(bump).finish(),
190− span(bump).finish(),
191− p(bump).finish(),
189+ div(cx.bump).finish(),
190+ span(cx.bump).finish(),
191+ p(cx.bump).finish(),
192192 ])
193193 .finish()
194194 }
195− after(bump) {
196− div(bump)
195+ after(cx) {
196+ div(cx.bump)
197197 .children([
198− span(bump).finish(),
199− p(bump).finish(),
200− div(bump).finish(),
198+ span(cx.bump).finish(),
199+ p(cx.bump).finish(),
200+ div(cx.bump).finish(),
201201 ])
202202 .finish()
203203 }
204204 }
205205
206206 same_tag_remove_many_children {
207− before(bump) {
208− div(bump)
207+ before(cx) {
208+ div(cx.bump)
209209 .children([
210− div(bump).finish(),
211− span(bump).finish(),
212− p(bump).finish(),
210+ div(cx.bump).finish(),
211+ span(cx.bump).finish(),
212+ p(cx.bump).finish(),
213213 ])
214214 .finish()
215215 }
216− after(bump) {
217− div(bump)
216+ after(cx) {
217+ div(cx.bump)
218218 .children([
219− div(bump).finish(),
219+ div(cx.bump).finish(),
220220 ])
221221 .finish()
222222 }
223223 }
224224
225225 same_tag_add_many_children {
226− before(bump) {
227− div(bump)
226+ before(cx) {
227+ div(cx.bump)
228228 .children([
229− div(bump).finish(),
229+ div(cx.bump).finish(),
230230 ])
231231 .finish()
232232 }
233− after(bump) {
234− div(bump)
233+ after(cx) {
234+ div(cx.bump)
235235 .children([
236− div(bump).finish(),
237− span(bump).finish(),
238− p(bump).finish(),
236+ div(cx.bump).finish(),
237+ span(cx.bump).finish(),
238+ p(cx.bump).finish(),
239239 ])
240240 .finish()
241241 }
242242 }
243243
244244 same_tag_different_namespace {
245− before(bump) {
246− div(bump)
245+ before(cx) {
246+ div(cx.bump)
247247 .namespace(Some("http://example.com"))
248248 .finish()
249249 }
250− after(bump) {
251− div(bump)
250+ after(cx) {
251+ div(cx.bump)
252252 .namespace(Some("http://example.net"))
253253 .finish()
254254 }