aboutsummaryrefslogtreecommitdiffstats
path: root/askama_derive/src/generator.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--askama_derive/src/generator.rs134
1 files changed, 57 insertions, 77 deletions
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index 6f17db2..cf3a950 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -38,10 +38,7 @@ struct Generator<'a> {
// determine whether to flush prefix whitespace from the next literal.
skip_ws: bool,
// If currently in a block, this will contain the name of a potential parent block
- super_block: Option<String>,
- // If the super macro is used; this determines whether code for the parent block
- // is generated or not.
- used_super: bool,
+ super_block: Option<(&'a str, usize)>,
}
impl<'a> Generator<'a> {
@@ -59,7 +56,6 @@ impl<'a> Generator<'a> {
next_ws: None,
skip_ws: false,
super_block: None,
- used_super: false,
}
}
@@ -75,7 +71,6 @@ impl<'a> Generator<'a> {
if let Some(parent) = self.input.parent {
self.deref_to_parent(&mut buf, parent);
}
- self.trait_blocks(&mut buf);
};
self.impl_template(ctx, &mut buf);
@@ -120,64 +115,6 @@ impl<'a> Generator<'a> {
buf.writeln("}");
}
- fn trait_blocks(&mut self, buf: &mut Buffer) {
- let trait_name = format!("{}Blocks", self.input.ast.ident);
- let mut methods = vec![];
- let heritage = self.heritage
- .as_ref()
- .expect("need heritage for blocks trait");
-
- self.write_header(buf, &trait_name, None);
- for blocks in heritage.blocks.values() {
- for (gen, (ctx, def)) in blocks.iter().enumerate() {
- self.used_super = false;
- if let Node::BlockDef(ws1, name, nodes, ws2) = def {
- let fname = if gen == 0 {
- name.to_string()
- } else {
- format!("{}_g{}", name, gen)
- };
-
- buf.writeln("#[allow(unused_variables)]");
- buf.writeln(&format!(
- "fn render_block_{}_into(&self, writer: &mut ::std::fmt::Write) \
- -> ::askama::Result<()> {{",
- fname
- ));
- methods.push(fname);
- self.prepare_ws(*ws1);
-
- self.locals.push();
- self.super_block = Some(format!("{}_g{}", name, gen + 1));
- self.handle(ctx, nodes, buf, AstLevel::Block);
- self.super_block = None;
- self.locals.pop();
-
- self.flush_ws(buf, *ws2);
- buf.writeln("Ok(())");
- buf.writeln("}");
- } else {
- panic!("only block definitions allowed here");
- }
-
- if !self.used_super {
- break;
- }
- }
- }
- buf.writeln("}");
-
- buf.writeln(&format!("pub trait {} {{", trait_name));
- for name in methods {
- buf.writeln(&format!(
- "fn render_block_{}_into(&self, writer: &mut ::std::fmt::Write) \
- -> ::askama::Result<()>;",
- name
- ));
- }
- buf.writeln("}");
- }
-
// Implement `Deref<Parent>` for an inheriting context struct.
fn deref_to_parent(&mut self, buf: &mut Buffer, parent_type: &syn::Type) {
self.write_header(buf, "::std::ops::Deref", None);
@@ -333,7 +270,8 @@ impl<'a> Generator<'a> {
name
);
}
- self.write_block(buf, ws1, name, ws2);
+ let outer = WS(ws1.0, ws2.1);
+ self.write_block(buf, Some(name), outer);
}
Node::Include(ws, path) => {
self.handle_include(ctx, buf, ws, path);
@@ -483,14 +421,7 @@ impl<'a> Generator<'a> {
args: &[Expr],
) {
if name == "super" {
- self.flush_ws(buf, ws);
- let line = match self.super_block {
- Some(ref name) => format!("self.render_block_{}_into(writer)?;", name),
- None => panic!("cannot call 'super()' outside block"),
- };
- buf.writeln(&line);
- self.prepare_ws(ws);
- self.used_super = true;
+ self.write_block(buf, None, ws);
return;
}
@@ -576,10 +507,59 @@ impl<'a> Generator<'a> {
buf.writeln(&format!(" = {};", &expr_buf.buf));
}
- fn write_block(&mut self, buf: &mut Buffer, ws1: WS, name: &str, ws2: WS) {
- self.flush_ws(buf, ws1);
- buf.writeln(&format!("self.render_block_{}_into(writer)?;", name));
- self.prepare_ws(ws2);
+ // If `name` is `Some`, this is a call to a block definition, and we have to find
+ // the first block for that name from the ancestry chain. If name is `None`, this
+ // 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);
+
+ let prev_block = self.super_block;
+ let cur = match (name, prev_block) {
+ // The top-level context contains a block definition
+ (Some(cur_name), None) => (cur_name, 0),
+ // A block definition contains a block definition of the same name
+ (Some(cur_name), Some((prev_name, _))) if cur_name == prev_name => {
+ panic!("cannot define recursive blocks ({})", cur_name)
+ }
+ // A block definition contains a definition of another block
+ (Some(cur_name), Some((_, _))) => (cur_name, 0),
+ // `super()` was called inside a block
+ (None, Some((prev_name, gen))) => (prev_name, gen + 1),
+ // `super()` is called from outside a block
+ (None, None) => panic!("cannot call 'super()' outside block"),
+ };
+ self.super_block = Some(cur);
+
+ // Get the block definition from the heritage chain
+ let heritage = self.heritage
+ .as_ref()
+ .unwrap_or_else(|| panic!("no block ancestors available"));
+ let (ctx, def) = heritage.blocks[cur.0]
+ .get(cur.1)
+ .unwrap_or_else(|| match name {
+ None => panic!("no super() block found for block '{}'", cur.0),
+ Some(name) => panic!("no block found for name '{}'", name),
+ });
+
+ // Get the nodes and whitespace suppression data from the block definition
+ let (ws1, nodes, ws2) = if let Node::BlockDef(ws1, _, nodes, ws2) = def {
+ (ws1, nodes, ws2)
+ } else {
+ unreachable!()
+ };
+
+ // Handle inner whitespace suppression spec and process block nodes
+ self.prepare_ws(*ws1);
+ self.locals.push();
+ self.handle(ctx, nodes, buf, AstLevel::Block);
+ self.locals.pop();
+ self.flush_ws(buf, *ws2);
+
+ // Restore original block context and set whitespace suppression for
+ // succeeding whitespace according to the outer WS spec
+ self.super_block = prev_block;
+ self.prepare_ws(outer);
}
fn write_expr(&mut self, buf: &mut Buffer, ws: WS, s: &Expr) {