diff options
| author | 2021-07-18 16:32:49 +0200 | |
|---|---|---|
| committer | 2021-11-11 15:35:45 +0100 | |
| commit | 10b2d9c615460c9dbb241b887d0e6e17e3c001ca (patch) | |
| tree | 4ec234cf416f7bcdc0bc037f787ebbd7ac4cb67e /askama_shared/src | |
| parent | a8503e0fa2d6065b1c471becf76dde68571b7984 (diff) | |
| download | askama-10b2d9c615460c9dbb241b887d0e6e17e3c001ca.tar.gz askama-10b2d9c615460c9dbb241b887d0e6e17e3c001ca.tar.bz2 askama-10b2d9c615460c9dbb241b887d0e6e17e3c001ca.zip | |
Implement for-else
This PR implements for-else statements like in Jinja. They make it easy
to print an alternative message if the loop iterator was empty. E.g.
```rs
{% for result in result %}
  <li>{{ result }}</li>
{% else %}
  <li><em>no results</em></li>
{% endfor %}
```
Diffstat (limited to '')
| -rw-r--r-- | askama_shared/src/generator.rs | 43 | ||||
| -rw-r--r-- | askama_shared/src/heritage.rs | 9 | ||||
| -rw-r--r-- | askama_shared/src/parser.rs | 49 | 
3 files changed, 72 insertions, 29 deletions
| diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 6c1b151..0c2e854 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -2,7 +2,7 @@ use super::{get_template_source, CompileError, Integrations};  use crate::filters;  use crate::heritage::{Context, Heritage};  use crate::input::{Source, TemplateInput}; -use crate::parser::{parse, Cond, CondTest, Expr, Node, Target, When, Ws}; +use crate::parser::{parse, Cond, CondTest, Expr, Loop, Node, Target, When, Ws};  use proc_macro2::Span; @@ -420,8 +420,8 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {                  Node::Match(ws1, ref expr, ref arms, ws2) => {                      self.write_match(ctx, buf, ws1, expr, arms, ws2)?;                  } -                Node::Loop(ws1, ref var, ref iter, ref body, ws2) => { -                    self.write_loop(ctx, buf, ws1, var, iter, body, ws2)?; +                Node::Loop(ref loop_block) => { +                    self.write_loop(ctx, buf, loop_block)?;                  }                  Node::BlockDef(ws1, name, _, ws2) => {                      self.write_block(buf, Some(name), Ws(ws1.0, ws2.1))?; @@ -596,21 +596,19 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {          &mut self,          ctx: &'a Context<'_>,          buf: &mut Buffer, -        ws1: Ws, -        var: &'a Target<'_>, -        iter: &Expr<'_>, -        body: &'a [Node<'_>], -        ws2: Ws, +        loop_block: &'a Loop<'_>,      ) -> Result<usize, CompileError> { -        self.handle_ws(ws1); +        self.handle_ws(loop_block.ws1);          self.locals.push(); -        let expr_code = self.visit_expr_root(iter)?; +        let expr_code = self.visit_expr_root(&loop_block.iter)?;          let flushed = self.write_buf_writable(buf)?; +        buf.writeln("{")?; +        buf.writeln("let mut _did_not_loop = true;")?;          buf.write("for ("); -        self.visit_target(buf, true, true, var); -        match iter { +        self.visit_target(buf, true, true, &loop_block.var); +        match loop_block.iter {              Expr::Range(_, _, _) => buf.writeln(&format!(                  ", _loop_item) in ::askama::helpers::TemplateLoop::new({}) {{",                  expr_code @@ -645,13 +643,24 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {              )),          }?; -        let mut size_hint = self.handle(ctx, body, buf, AstLevel::Nested)?; -        self.handle_ws(ws2); - -        size_hint += self.write_buf_writable(buf)?; +        buf.writeln("_did_not_loop = false;")?; +        let mut size_hint1 = self.handle(ctx, &loop_block.body, buf, AstLevel::Nested)?; +        self.handle_ws(loop_block.ws2); +        size_hint1 += self.write_buf_writable(buf)?; +        self.locals.pop();          buf.writeln("}")?; + +        buf.writeln("if _did_not_loop {")?; +        self.locals.push(); +        let mut size_hint2 = self.handle(ctx, &loop_block.else_block, buf, AstLevel::Nested)?; +        self.handle_ws(loop_block.ws3); +        size_hint2 += self.write_buf_writable(buf)?;          self.locals.pop(); -        Ok(flushed + (size_hint * 3)) +        buf.writeln("}")?; + +        buf.writeln("}")?; + +        Ok(flushed + ((size_hint1 * 3) + size_hint2) / 2)      }      fn write_call( diff --git a/askama_shared/src/heritage.rs b/askama_shared/src/heritage.rs index 9f90273..a0b5460 100644 --- a/askama_shared/src/heritage.rs +++ b/askama_shared/src/heritage.rs @@ -1,7 +1,7 @@  use std::collections::HashMap;  use std::path::{Path, PathBuf}; -use crate::parser::{Expr, Macro, Node}; +use crate::parser::{Expr, Loop, Macro, Node};  use crate::{CompileError, Config};  pub struct Heritage<'a> { @@ -86,8 +86,11 @@ impl<'a> Context<'a> {                              nested.push(nodes);                          }                      } -                    Node::Loop(_, _, _, nodes, _) => { -                        nested.push(nodes); +                    Node::Loop(Loop { +                        body, else_block, .. +                    }) => { +                        nested.push(body); +                        nested.push(else_block);                      }                      Node::Match(_, _, arms, _) => {                          for (_, _, arm) in arms { diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index d1bc425..90c2fe3 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -22,7 +22,7 @@ pub enum Node<'a> {      Let(Ws, Target<'a>, Expr<'a>),      Cond(Vec<Cond<'a>>, Ws),      Match(Ws, Expr<'a>, Vec<When<'a>>, Ws), -    Loop(Ws, Target<'a>, Expr<'a>, Vec<Node<'a>>, Ws), +    Loop(Loop<'a>),      Extends(Expr<'a>),      BlockDef(Ws, &'a str, Vec<Node<'a>>, Ws),      Include(Ws, &'a str), @@ -34,6 +34,17 @@ pub enum Node<'a> {  }  #[derive(Debug, PartialEq)] +pub struct Loop<'a> { +    pub ws1: Ws, +    pub var: Target<'a>, +    pub iter: Expr<'a>, +    pub body: Vec<Node<'a>>, +    pub ws2: Ws, +    pub else_block: Vec<Node<'a>>, +    pub ws3: Ws, +} + +#[derive(Debug, PartialEq)]  pub enum Expr<'a> {      BoolLit(&'a str),      NumLit(&'a str), @@ -897,12 +908,28 @@ fn block_let(i: &[u8]) -> IResult<&[u8], Node<'_>> {  fn parse_loop_content<'a>(i: &'a [u8], s: &State<'_>) -> IResult<&'a [u8], Vec<Node<'a>>> {      s.loop_depth.set(s.loop_depth.get() + 1); -    let (i, node) = parse_template(i, s)?; +    let result = parse_template(i, s);      s.loop_depth.set(s.loop_depth.get() - 1); -    Ok((i, node)) +    result  }  fn block_for<'a>(i: &'a [u8], s: &State<'_>) -> IResult<&'a [u8], Node<'a>> { +    let else_block = |i| { +        let mut p = preceded( +            ws(tag("else")), +            cut(tuple(( +                opt(tag("-")), +                delimited( +                    |i| tag_block_end(i, s), +                    |i| parse_template(i, s), +                    |i| tag_block_start(i, s), +                ), +                opt(tag("-")), +            ))), +        ); +        let (i, (pws, nodes, nws)) = p(i)?; +        Ok((i, (pws.is_some(), nodes, nws.is_some()))) +    };      let mut p = tuple((          opt(char('-')),          ws(tag("for")), @@ -918,6 +945,7 @@ fn block_for<'a>(i: &'a [u8], s: &State<'_>) -> IResult<&'a [u8], Node<'a>> {                      cut(tuple((                          |i| tag_block_start(i, s),                          opt(char('-')), +                        opt(else_block),                          ws(tag("endfor")),                          opt(char('-')),                      ))), @@ -925,16 +953,19 @@ fn block_for<'a>(i: &'a [u8], s: &State<'_>) -> IResult<&'a [u8], Node<'a>> {              ))),          ))),      )); -    let (i, (pws1, _, (var, _, (iter, nws1, _, (block, (_, pws2, _, nws2)))))) = p(i)?; +    let (i, (pws1, _, (var, _, (iter, nws1, _, (body, (_, pws2, else_block, _, nws2)))))) = p(i)?; +    let (nws3, else_block, pws3) = else_block.unwrap_or_default();      Ok((          i, -        Node::Loop( -            Ws(pws1.is_some(), nws1.is_some()), +        Node::Loop(Loop { +            ws1: Ws(pws1.is_some(), nws1.is_some()),              var,              iter, -            block, -            Ws(pws2.is_some(), nws2.is_some()), -        ), +            body, +            ws2: Ws(pws2.is_some(), nws3), +            else_block, +            ws3: Ws(pws3, nws2.is_some()), +        }),      ))  } | 
