diff options
Diffstat (limited to '')
-rw-r--r-- | askama_derive/src/generator.rs | 163 |
1 files changed, 111 insertions, 52 deletions
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index 28640ca..0395c11 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -9,7 +9,7 @@ use quote::ToTokens; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; -use std::{cmp, hash, str}; +use std::{cmp, hash, mem, str}; use syn; @@ -41,8 +41,8 @@ struct Generator<'a> { skip_ws: bool, // If currently in a block, this will contain the name of a potential parent block super_block: Option<(&'a str, usize)>, - // buffer for literals - buf_lit: Vec<&'a str>, + // buffer for writable + buf_writable: Vec<Writable<'a>>, } impl<'a> Generator<'a> { @@ -60,7 +60,7 @@ impl<'a> Generator<'a> { next_ws: None, skip_ws: false, super_block: None, - buf_lit: vec![], + buf_writable: vec![], } } @@ -106,7 +106,7 @@ impl<'a> Generator<'a> { self.handle(ctx, &ctx.nodes, buf, AstLevel::Top); } - self.flush_ws(buf, WS(false, false)); + self.flush_ws(WS(false, false)); buf.writeln("Ok(())"); buf.writeln("}"); @@ -248,10 +248,10 @@ impl<'a> Generator<'a> { self.visit_lit(lws, val, rws); } Node::Comment(ws) => { - self.write_comment(buf, ws); + self.write_comment(ws); } Node::Expr(ws, ref val) => { - self.write_expr(buf, ws, val); + self.write_expr(ws, val); } Node::LetDecl(ws, ref var) => { self.write_let_decl(buf, ws, var); @@ -289,14 +289,14 @@ impl<'a> Generator<'a> { if level != AstLevel::Top { panic!("macro blocks only allowed at the top level"); } - self.flush_ws(buf, m.ws1); + self.flush_ws(m.ws1); self.prepare_ws(m.ws2); } Node::Import(ws, _, _) => { if level != AstLevel::Top { panic!("import blocks only allowed at the top level"); } - self.handle_ws(buf, ws); + self.handle_ws(ws); } Node::Extends(_) => { if level != AstLevel::Top { @@ -308,24 +308,29 @@ impl<'a> Generator<'a> { } } - self.write_buf_lit(buf); + if AstLevel::Top == level { + self.write_buf_writable(buf); + } } fn write_cond(&mut self, ctx: &'a Context, buf: &mut Buffer, conds: &'a [Cond], ws: WS) { for (i, &(cws, ref cond, ref nodes)) in conds.iter().enumerate() { - self.handle_ws(buf, cws); + self.handle_ws(cws); + self.write_buf_writable(buf); match *cond { Some(ref expr) => { let expr_code = self.visit_expr_root(expr); if i == 0 { buf.write("if "); } else { + self.write_buf_writable(buf); buf.dedent(); buf.write("} else if "); } buf.write(&expr_code); } None => { + self.write_buf_writable(buf); buf.dedent(); buf.write("} else"); } @@ -335,7 +340,8 @@ impl<'a> Generator<'a> { self.handle(ctx, nodes, buf, AstLevel::Nested); self.locals.pop(); } - self.handle_ws(buf, ws); + self.handle_ws(ws); + self.write_buf_writable(buf); buf.writeln("}"); } @@ -349,7 +355,8 @@ impl<'a> Generator<'a> { arms: &'a [When], ws2: WS, ) { - self.flush_ws(buf, ws1); + self.flush_ws(ws1); + self.write_buf_writable(buf); if let Some(inter) = inter { if !inter.is_empty() { self.next_ws = Some(inter); @@ -381,14 +388,15 @@ impl<'a> Generator<'a> { buf.write(")"); } buf.writeln(" => {"); - self.handle_ws(buf, ws); + self.handle_ws(ws); self.handle(ctx, body, buf, AstLevel::Nested); + self.write_buf_writable(buf); buf.writeln("}"); self.locals.pop(); } buf.writeln("}"); - self.handle_ws(buf, ws2); + self.handle_ws(ws2); } fn write_loop( @@ -401,10 +409,12 @@ impl<'a> Generator<'a> { body: &'a [Node], ws2: WS, ) { - self.handle_ws(buf, ws1); + self.handle_ws(ws1); self.locals.push(); let expr_code = self.visit_expr_root(iter); + + self.write_buf_writable(buf); buf.write("for (_loop_index, "); let targets = self.visit_target(var); for name in &targets { @@ -417,7 +427,9 @@ impl<'a> Generator<'a> { }; self.handle(ctx, body, buf, AstLevel::Nested); - self.handle_ws(buf, ws2); + self.handle_ws(ws2); + + self.write_buf_writable(buf); buf.writeln("}"); self.locals.pop(); } @@ -460,8 +472,9 @@ impl<'a> Generator<'a> { ) }; - self.flush_ws(buf, ws); // Cannot handle_ws() here: whitespace from macro definition comes first + self.flush_ws(ws); // Cannot handle_ws() here: whitespace from macro definition comes first self.locals.push(); + self.write_buf_writable(buf); buf.writeln("{"); self.prepare_ws(def.ws1); @@ -476,14 +489,16 @@ impl<'a> Generator<'a> { self.handle(own_ctx, &def.nodes, buf, AstLevel::Nested); - self.flush_ws(buf, def.ws2); + self.flush_ws(def.ws2); + self.write_buf_writable(buf); buf.writeln("}"); self.locals.pop(); self.prepare_ws(ws); } fn handle_include(&mut self, ctx: &'a Context, buf: &mut Buffer, ws: WS, path: &str) { - self.flush_ws(buf, ws); + self.flush_ws(ws); + self.write_buf_writable(buf); let path = self .input .config @@ -495,12 +510,14 @@ impl<'a> Generator<'a> { // a nested Generator here to handle the include's nodes. let mut gen = self.child(); gen.handle(ctx, &nodes, buf, AstLevel::Nested); + gen.write_buf_writable(buf); } self.prepare_ws(ws); } fn write_let_decl(&mut self, buf: &mut Buffer, ws: WS, var: &'a Target) { - self.handle_ws(buf, ws); + self.handle_ws(ws); + self.write_buf_writable(buf); buf.write("let "); match *var { Target::Name(name) => { @@ -512,7 +529,7 @@ impl<'a> Generator<'a> { } fn write_let(&mut self, buf: &mut Buffer, ws: WS, var: &'a Target, val: &Expr) { - self.handle_ws(buf, ws); + self.handle_ws(ws); let mut expr_buf = Buffer::new(0); self.visit_expr(&mut expr_buf, val); @@ -533,7 +550,7 @@ impl<'a> Generator<'a> { // is from a `super()` call, and we can get the name from `self.super_block`. fn write_block(&mut self, buf: &mut Buffer, name: Option<&'a str>, outer: WS) { // Flush preceding whitespace according to the outer WS spec - self.flush_ws(buf, outer); + self.flush_ws(outer); let prev_block = self.super_block; let cur = match (name, prev_block) { @@ -576,7 +593,7 @@ impl<'a> Generator<'a> { self.locals.push(); self.handle(ctx, nodes, buf, AstLevel::Block); self.locals.pop(); - self.flush_ws(buf, *ws2); + self.flush_ws(*ws2); // Restore original block context and set whitespace suppression for // succeeding whitespace according to the outer WS spec @@ -584,27 +601,64 @@ impl<'a> Generator<'a> { self.prepare_ws(outer); } - fn write_expr(&mut self, buf: &mut Buffer, ws: WS, s: &Expr) { - self.handle_ws(buf, ws); - let mut expr_buf = Buffer::new(0); - let wrapped = self.visit_expr(&mut expr_buf, s); - - use self::DisplayWrap::*; - use super::input::EscapeMode::*; - buf.writeln("write!(writer, \"{}\", &"); - buf.write(&match (wrapped, &self.input.escaping) { - (Wrapped, &Html) | (Wrapped, &None) | (Unwrapped, &None) => expr_buf.buf, - (Unwrapped, &Html) => format!("::askama::MarkupDisplay::from(&{})", expr_buf.buf), - }); - buf.writeln(""); - buf.writeln(")?;"); + fn write_expr(&mut self, ws: WS, s: &'a Expr<'a>) { + self.handle_ws(ws); + self.buf_writable.push(Writable::Expr(s)); } - // Write literals buffer and empty - fn write_buf_lit(&mut self, buf: &mut Buffer) { - if !self.buf_lit.is_empty() { - buf.writeln(&format!("writer.write_str({:#?})?;", self.buf_lit.join(""))); - self.buf_lit = vec![]; + // Write expression buffer and empty + fn write_buf_writable(&mut self, buf: &mut Buffer) { + if self.buf_writable.is_empty() { + return; + } else { + if self.buf_writable.iter().all(|w| match w { + Writable::Lit(_) => true, + _ => false, + }) { + let mut buf_lit = Buffer::new(0); + for s in mem::replace(&mut self.buf_writable, vec![]) { + if let Writable::Lit(s) = s { + buf_lit.write(s); + }; + } + + buf.writeln(&format!("writer.write_str({:#?})?;", &buf_lit.buf)); + } else { + let mut buf_format = Buffer::new(0); + let mut buf_expr = Buffer::new(0); + for s in mem::replace(&mut self.buf_writable, vec![]) { + match s { + Writable::Lit(s) => { + buf_format.write(&s.replace("{", "{{").replace("}", "}}")); + } + Writable::Expr(s) => { + use self::DisplayWrap::*; + use super::input::EscapeMode::*; + let mut expr_buf = Buffer::new(0); + let wrapped = self.visit_expr(&mut expr_buf, s); + + buf_format.write("{}"); + buf_expr.write("&"); + buf_expr.write(&match (wrapped, &self.input.escaping) { + (Wrapped, &Html) | (Wrapped, &None) | (Unwrapped, &None) => { + expr_buf.buf + } + (Unwrapped, &Html) => { + format!("::askama::MarkupDisplay::from(&{})", expr_buf.buf) + } + }); + buf_expr.write(", "); + } + } + } + + buf.write("write!(writer, "); + buf.write(&format!("{:#?}", &buf_format.buf)); + buf.writeln(", "); + buf.write("\t"); + buf.write(&buf_expr.buf); + buf.writeln(")?;"); + } } } @@ -617,12 +671,12 @@ impl<'a> Generator<'a> { assert!(rws.is_empty()); self.next_ws = Some(lws); } else { - self.buf_lit.push(lws); + self.buf_writable.push(Writable::Lit(lws)); } } if !val.is_empty() { - self.buf_lit.push(val); + self.buf_writable.push(Writable::Lit(val)); } if !rws.is_empty() { @@ -630,8 +684,8 @@ impl<'a> Generator<'a> { } } - fn write_comment(&mut self, buf: &mut Buffer, ws: WS) { - self.handle_ws(buf, ws); + fn write_comment(&mut self, ws: WS) { + self.handle_ws(ws); } /* Visitor methods for expression types */ @@ -922,23 +976,22 @@ impl<'a> Generator<'a> { // Combines `flush_ws()` and `prepare_ws()` to handle both trailing whitespace from the // preceding literal and leading whitespace from the succeeding literal. - fn handle_ws(&mut self, buf: &mut Buffer, ws: WS) { - self.flush_ws(buf, ws); + fn handle_ws(&mut self, ws: WS) { + self.flush_ws(ws); self.prepare_ws(ws); } // If the previous literal left some trailing whitespace in `next_ws` and the // prefix whitespace suppressor from the given argument, flush that whitespace. // In either case, `next_ws` is reset to `None` (no trailing whitespace). - fn flush_ws(&mut self, buf: &mut Buffer, ws: WS) { + fn flush_ws(&mut self, ws: WS) { if self.next_ws.is_some() && !ws.0 { let val = self.next_ws.unwrap(); if !val.is_empty() { - self.buf_lit.push(val); + self.buf_writable.push(Writable::Lit(val)); } } self.next_ws = None; - self.write_buf_lit(buf); } // Sets `skip_ws` to match the suffix whitespace suppressor from the given @@ -1061,3 +1114,9 @@ enum DisplayWrap { } impl Copy for DisplayWrap {} + +#[derive(Debug)] +enum Writable<'a> { + Lit(&'a str), + Expr(&'a Expr<'a>), +} |