From 06bd6ef634de9a6569f15eb5043540e545371d68 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 8 Feb 2017 14:49:27 +0100 Subject: Implement generator support for whitespace handling markers --- askama/src/generator.rs | 93 ++++++++++++++++++++++++++++----------- askama/src/parser.rs | 2 +- testing/templates/comparison.html | 54 ++++++++++++++++++++--- 3 files changed, 117 insertions(+), 32 deletions(-) diff --git a/askama/src/generator.rs b/askama/src/generator.rs index ecc1887..8441bca 100644 --- a/askama/src/generator.rs +++ b/askama/src/generator.rs @@ -1,4 +1,4 @@ -use parser::{Cond, Expr, Node, Target}; +use parser::{Cond, Expr, Node, Target, WS}; use std::str; use std::collections::HashSet; use syn; @@ -28,21 +28,25 @@ fn path_as_identifier(s: &str) -> String { res } -struct Generator { +struct Generator<'a> { buf: String, indent: u8, start: bool, locals: HashSet, + next_ws: Option<&'a str>, + skip_ws: bool, } -impl Generator { +impl<'a> Generator<'a> { - fn new() -> Generator { + fn new() -> Generator<'a> { Generator { buf: String::new(), indent: 0, start: true, locals: HashSet::new(), + next_ws: None, + skip_ws: false, } } @@ -117,20 +121,42 @@ impl Generator { } } - fn write_lit(&mut self, lws: &str, val: &str, rws: &str) { - self.write(&format!("writer.write_str({:#?}).unwrap();", lws)); + fn flush_ws(&mut self, ws: &WS) { + if self.next_ws.is_some() && !ws.0 { + let val = self.next_ws.unwrap(); + self.write(&format!("writer.write_str({:#?}).unwrap();", val)); + self.next_ws = None; + } + } + + fn prepare_ws(&mut self, ws: &WS) { + self.skip_ws = ws.1; + } + + fn handle_ws(&mut self, ws: &WS) { + self.flush_ws(ws); + self.prepare_ws(ws); + } + + fn write_lit(&mut self, lws: &str, val: &str, rws: &'a str) { + assert!(self.next_ws.is_none()); + if !self.skip_ws { + self.write(&format!("writer.write_str({:#?}).unwrap();", lws)); + } self.write(&format!("writer.write_str({:#?}).unwrap();", val)); - self.write(&format!("writer.write_str({:#?}).unwrap();", rws)); + self.next_ws = Some(rws); } - fn write_expr(&mut self, s: &Expr) { + fn write_expr(&mut self, ws: &WS, s: &Expr) { + self.handle_ws(ws); self.write("writer.write_fmt(format_args!(\"{}\", "); self.visit_expr(s); self.writeln(")).unwrap();"); } - fn write_cond(&mut self, conds: &[Cond]) { - for (i, &(_, ref cond, ref nodes)) in conds.iter().enumerate() { + fn write_cond(&mut self, conds: &'a [Cond], ws: &WS) { + for (i, &(ref cws, ref cond, ref nodes)) in conds.iter().enumerate() { + self.handle_ws(cws); match *cond { Some(ref expr) => { if i == 0 { @@ -147,11 +173,14 @@ impl Generator { self.handle(nodes); self.dedent(); } + self.handle_ws(ws); self.writeln("}"); } - fn write_loop(&mut self, var: &Target, iter: &Expr, body: &[Node]) { + fn write_loop(&mut self, ws1: &WS, var: &Target, iter: &Expr, + body: &'a [Node], ws2: &WS) { + self.handle_ws(ws1); self.write("for "); let targets = self.visit_target(var); for name in &targets { @@ -164,6 +193,7 @@ impl Generator { self.indent(); self.handle(body); + self.handle_ws(ws2); self.dedent(); self.writeln("}"); for name in &targets { @@ -171,33 +201,42 @@ impl Generator { } } - fn write_block(&mut self, name: &str) { + fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) { + self.flush_ws(ws1); self.writeln(&format!("self.render_block_{}_into(writer);", name)); + self.prepare_ws(ws2); } - fn write_block_def(&mut self, name: &str, nodes: &[Node]) { + fn write_block_def(&mut self, ws1: &WS, name: &str, nodes: &'a [Node], + ws2: &WS) { self.writeln("#[allow(unused_variables)]"); self.writeln(&format!( "fn render_block_{}_into(&self, writer: &mut std::fmt::Write) {{", name)); self.indent(); + self.prepare_ws(ws1); self.handle(nodes); + self.flush_ws(ws2); self.dedent(); self.writeln("}"); } - fn handle(&mut self, nodes: &[Node]) { + fn handle(&mut self, nodes: &'a [Node]) { for n in nodes { match *n { Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); } - Node::Expr(_, ref val) => { self.write_expr(val); }, - Node::Cond(ref conds, _) => { self.write_cond(conds); }, - Node::Loop(_, ref var, ref iter, ref body, _) => { - self.write_loop(var, iter, body); + Node::Expr(ref ws, ref val) => { self.write_expr(ws, val); }, + Node::Cond(ref conds, ref ws) => { + self.write_cond(conds, ws); + }, + Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => { + self.write_loop(ws1, var, iter, body, ws2); + }, + Node::Block(ref ws1, name, ref ws2) => { + self.write_block(ws1, name, ws2); }, - Node::Block(_, name, _) => { self.write_block(name) }, - Node::BlockDef(_, name, ref block_nodes, _) => { - self.write_block_def(name, block_nodes); + Node::BlockDef(ref ws1, name, ref block_nodes, ref ws2) => { + self.write_block_def(ws1, name, block_nodes, ws2); } Node::Extends(_) => { panic!("no extends or block definition allowed in content"); @@ -216,7 +255,7 @@ impl Generator { self.writeln(&s); } - fn template_impl(&mut self, ast: &syn::DeriveInput, nodes: &[Node]) { + fn template_impl(&mut self, ast: &syn::DeriveInput, nodes: &'a [Node]) { let anno = annotations(&ast.generics); self.writeln(&format!("impl{} askama::Template for {}{} {{", anno, ast.ident.as_ref(), anno)); @@ -225,6 +264,7 @@ impl Generator { self.writeln("fn render_into(&self, writer: &mut std::fmt::Write) {"); self.indent(); self.handle(nodes); + self.flush_ws(&WS(false, false)); self.dedent(); self.writeln("}"); @@ -232,13 +272,14 @@ impl Generator { self.writeln("}"); } - fn trait_impl(&mut self, ast: &syn::DeriveInput, base: &str, blocks: &[Node]) { + fn trait_impl(&mut self, ast: &syn::DeriveInput, base: &str, blocks: &'a [Node]) { let anno = annotations(&ast.generics); self.writeln(&format!("impl{} TraitFrom{} for {}{} {{", anno, path_as_identifier(base), ast.ident.as_ref(), anno)); self.indent(); self.handle(blocks); + self.flush_ws(&WS(false, false)); self.dedent(); self.writeln("}"); } @@ -260,7 +301,7 @@ impl Generator { } fn template_trait(&mut self, ast: &syn::DeriveInput, path: &str, - blocks: &[Node], nodes: &[Node]) { + blocks: &'a [Node], nodes: &'a [Node]) { let anno = annotations(&ast.generics); self.writeln(&format!("trait{} TraitFrom{}{} {{", anno, path_as_identifier(path), anno)); @@ -271,6 +312,7 @@ impl Generator { self.writeln("fn render_trait_into(&self, writer: &mut std::fmt::Write) {"); self.indent(); self.handle(nodes); + self.flush_ws(&WS(false, false)); self.dedent(); self.writeln("}"); @@ -304,6 +346,7 @@ pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec) -> Str } } + let empty = Vec::new(); let mut gen = Generator::new(); gen.path_based_name(ast, path); if !blocks.is_empty() { @@ -313,7 +356,7 @@ pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec) -> Str } } else { gen.template_trait(ast, path, &blocks, &content); - gen.trait_impl(ast, path, &Vec::new()); + gen.trait_impl(ast, path, &empty); } gen.trait_based_impl(ast); } else { diff --git a/askama/src/parser.rs b/askama/src/parser.rs index 87a7dab..0b57aad 100644 --- a/askama/src/parser.rs +++ b/askama/src/parser.rs @@ -13,7 +13,7 @@ pub enum Target<'a> { } #[derive(Clone, Copy)] -pub struct WS(bool, bool); +pub struct WS(pub bool, pub bool); pub enum Node<'a> { Lit(&'a str, &'a str, &'a str), diff --git a/testing/templates/comparison.html b/testing/templates/comparison.html index 87aaec3..71a4a7b 100644 --- a/testing/templates/comparison.html +++ b/testing/templates/comparison.html @@ -1,6 +1,48 @@ -{% if a == b %}t{% endif %}{% if a == c %}t{% else %}f{% endif %} -{% if a != c %}t{% endif %}{% if a != b %}t{% else %}f{% endif %} -{% if c >= b %}t{% endif %}{% if b >= c %}t{% else %}f{% endif %} -{% if c > b %}t{% endif %}{% if a > c %}t{% else %}f{% endif %} -{% if a <= b %}t{% endif %}{% if c <= b %}t{% else %}f{% endif %} -{% if a < c %}t{% endif %}{% if a < b %}t{% else %}f{% endif %} +{% if a == b -%} + t +{%- endif -%} +{% if a == c -%} + t +{%- else -%} + f +{%- endif %} +{% if a != c -%} + t +{%- endif %} +{%- if a != b -%} + t +{%- else -%} + f +{%- endif %} +{% if c >= b -%} + t +{%- endif -%} +{% if b >= c -%} + t +{%- else -%} + f +{%- endif %} +{% if c > b -%} + t +{%- endif -%} +{% if a > c -%} + t +{%- else -%} + f +{%- endif %} +{% if a <= b %} + t +{% endif -%} +{% if c <= b -%} + t +{%- else -%} + f +{%- endif %} +{% if a < c -%} + t +{%- endif %} +{%- if a < b -%} + t +{%- else -%} + f +{%- endif %} -- cgit