Coalesce adjacent writes

Fixes 
This commit is contained in:
Chris Wong 2015-04-10 18:44:59 +12:00
parent e3998645f6
commit c507571c5d

View file

@ -14,8 +14,9 @@ pub enum Escape {
pub struct Renderer<'cx> { pub struct Renderer<'cx> {
pub cx: &'cx ExtCtxt<'cx>, pub cx: &'cx ExtCtxt<'cx>,
stmts: Vec<P<Stmt>>,
w: Ident, w: Ident,
stmts: Vec<P<Stmt>>,
tail: String,
} }
impl<'cx> Renderer<'cx> { impl<'cx> Renderer<'cx> {
@ -23,8 +24,9 @@ impl<'cx> Renderer<'cx> {
pub fn new(cx: &'cx ExtCtxt<'cx>) -> Renderer<'cx> { pub fn new(cx: &'cx ExtCtxt<'cx>) -> Renderer<'cx> {
Renderer { Renderer {
cx: cx, cx: cx,
stmts: vec![],
w: Ident::new(token::intern("w")), w: Ident::new(token::intern("w")),
stmts: Vec::new(),
tail: String::new(),
} }
} }
@ -32,14 +34,29 @@ impl<'cx> Renderer<'cx> {
pub fn fork(&self) -> Renderer<'cx> { pub fn fork(&self) -> Renderer<'cx> {
Renderer { Renderer {
cx: self.cx, cx: self.cx,
stmts: vec![],
w: self.w, w: self.w,
stmts: Vec::new(),
tail: String::new(),
}
}
/// Flushes the tail buffer, emitting a single `.write_str()` call.
fn flush(&mut self) {
if !self.tail.is_empty() {
let expr = {
let w = self.w;
let s = &*self.tail;
quote_expr!(self.cx, $w.write_str($s))
};
let stmt = self.cx.stmt_expr(self.cx.expr_try(expr.span, expr));
self.stmts.push(stmt);
self.tail.clear();
} }
} }
/// Reifies the `Renderer` into a block of markup. /// Reifies the `Renderer` into a block of markup.
pub fn into_expr(self) -> P<Expr> { pub fn into_expr(mut self) -> P<Expr> {
let Renderer { cx, stmts, w } = self; let Renderer { cx, w, stmts, .. } = { self.flush(); self };
quote_expr!(cx, quote_expr!(cx,
::maud::rt::make_markup(|$w: &mut ::std::fmt::Write| -> Result<(), ::std::fmt::Error> { ::maud::rt::make_markup(|$w: &mut ::std::fmt::Write| -> Result<(), ::std::fmt::Error> {
$stmts $stmts
@ -48,27 +65,26 @@ impl<'cx> Renderer<'cx> {
} }
/// Reifies the `Renderer` into a raw list of statements. /// Reifies the `Renderer` into a raw list of statements.
pub fn into_stmts(self) -> Vec<P<Stmt>> { pub fn into_stmts(mut self) -> Vec<P<Stmt>> {
let Renderer { stmts, .. } = self; let Renderer { stmts, .. } = { self.flush(); self };
stmts stmts
} }
/// Appends the list of statements to the output. /// Appends the list of statements to the output.
pub fn push_stmts(&mut self, mut stmts: Vec<P<Stmt>>) { pub fn push_stmts(&mut self, mut stmts: Vec<P<Stmt>>) {
self.flush();
self.stmts.append(&mut stmts); self.stmts.append(&mut stmts);
} }
/// Pushes an expression statement, also wrapping it with `try!`. /// Pushes an statement, flushing the tail buffer in the process.
fn push_try(&mut self, expr: P<Expr>) { fn push(&mut self, stmt: P<Stmt>) {
let stmt = self.cx.stmt_expr(self.cx.expr_try(expr.span, expr)); self.flush();
self.stmts.push(stmt); self.stmts.push(stmt);
} }
/// Appends a literal pre-escaped string. /// Pushes a literal string to the tail buffer.
fn write(&mut self, s: &str) { fn push_str(&mut self, s: &str) {
let w = self.w; self.tail.push_str(s);
let expr = quote_expr!(self.cx, $w.write_str($s));
self.push_try(expr);
} }
/// Appends a literal string, with the specified escaping method. /// Appends a literal string, with the specified escaping method.
@ -78,7 +94,7 @@ impl<'cx> Renderer<'cx> {
Escape::PassThru => s, Escape::PassThru => s,
Escape::Escape => { escaped = maud::escape(s); &*escaped }, Escape::Escape => { escaped = maud::escape(s); &*escaped },
}; };
self.write(s); self.push_str(s);
} }
/// Appends the result of an expression, with the specified escaping method. /// Appends the result of an expression, with the specified escaping method.
@ -93,37 +109,38 @@ impl<'cx> Renderer<'cx> {
&mut ::maud::rt::Escaper { inner: $w }, &mut ::maud::rt::Escaper { inner: $w },
$expr)), $expr)),
}; };
self.push_try(expr); let stmt = self.cx.stmt_expr(self.cx.expr_try(expr.span, expr));
self.push(stmt);
} }
pub fn element_open_start(&mut self, name: &str) { pub fn element_open_start(&mut self, name: &str) {
self.write("<"); self.push_str("<");
self.write(name); self.push_str(name);
} }
pub fn attribute_start(&mut self, name: &str) { pub fn attribute_start(&mut self, name: &str) {
self.write(" "); self.push_str(" ");
self.write(name); self.push_str(name);
self.write("=\""); self.push_str("=\"");
} }
pub fn attribute_empty(&mut self, name: &str) { pub fn attribute_empty(&mut self, name: &str) {
self.write(" "); self.push_str(" ");
self.write(name); self.push_str(name);
} }
pub fn attribute_end(&mut self) { pub fn attribute_end(&mut self) {
self.write("\""); self.push_str("\"");
} }
pub fn element_open_end(&mut self) { pub fn element_open_end(&mut self) {
self.write(">"); self.push_str(">");
} }
pub fn element_close(&mut self, name: &str) { pub fn element_close(&mut self, name: &str) {
self.write("</"); self.push_str("</");
self.write(name); self.push_str(name);
self.write(">"); self.push_str(">");
} }
/// Emits an `if` expression. /// Emits an `if` expression.
@ -137,11 +154,11 @@ impl<'cx> Renderer<'cx> {
Some(else_body) => Some(else_body) =>
quote_stmt!(self.cx, if $if_cond { $if_body } else { $else_body }), quote_stmt!(self.cx, if $if_cond { $if_body } else { $else_body }),
}.unwrap(); }.unwrap();
self.stmts.push(stmt); self.push(stmt);
} }
pub fn emit_for(&mut self, pattern: P<Pat>, iterable: P<Expr>, body: Vec<P<Stmt>>) { pub fn emit_for(&mut self, pattern: P<Pat>, iterable: P<Expr>, body: Vec<P<Stmt>>) {
let stmt = quote_stmt!(self.cx, for $pattern in $iterable { $body }).unwrap(); let stmt = quote_stmt!(self.cx, for $pattern in $iterable { $body }).unwrap();
self.stmts.push(stmt); self.push(stmt);
} }
} }