aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--askama_derive/src/generator.rs163
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>),
+}