diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index fc520d855..9aec1d65f 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -1,6 +1,6 @@ use crate::heritage::{Context, Heritage}; use crate::input::{Print, Source, TemplateInput}; -use crate::parser::{parse, Cond, CondTest, Expr, Loop, Node, Target, When, Ws}; +use crate::parser::{parse, Cond, CondTest, Expr, Loop, Node, Target, When, Whitespace, Ws}; use crate::{filters, get_template_source, read_config_file, CompileError, Config}; use proc_macro2::TokenStream; @@ -28,9 +28,10 @@ pub fn derive_template(input: TokenStream) -> TokenStream { /// the parse tree and/or generated source according to the `print` key's /// value as passed to the `template()` attribute. fn build_template(ast: &syn::DeriveInput) -> Result { - let config_toml = read_config_file()?; + let template_args = TemplateArgs::new(ast)?; + let config_toml = read_config_file(&template_args.config_path)?; let config = Config::new(&config_toml)?; - let input = TemplateInput::new(ast, &config)?; + let input = TemplateInput::new(ast, &config, template_args)?; let source: String = match input.source { Source::Source(ref s) => s.clone(), Source::Path(_) => get_template_source(&input.path)?, @@ -60,14 +61,135 @@ fn build_template(ast: &syn::DeriveInput) -> Result { eprintln!("{:?}", parsed[input.path.as_path()]); } - let code = Generator::new(&input, &contexts, heritage.as_ref(), MapChain::new()) - .build(&contexts[input.path.as_path()])?; + let code = Generator::new( + &input, + &contexts, + heritage.as_ref(), + MapChain::new(), + config.suppress_whitespace, + ) + .build(&contexts[input.path.as_path()])?; if input.print == Print::Code || input.print == Print::All { eprintln!("{}", code); } Ok(code) } +#[derive(Default)] +pub(crate) struct TemplateArgs { + pub(crate) source: Option, + pub(crate) print: Print, + pub(crate) escaping: Option, + pub(crate) ext: Option, + pub(crate) syntax: Option, + pub(crate) config_path: Option, +} + +impl TemplateArgs { + fn new(ast: &'_ syn::DeriveInput) -> Result { + // Check that an attribute called `template()` exists once and that it is + // the proper type (list). + let mut template_args = None; + for attr in &ast.attrs { + let ident = match attr.path.get_ident() { + Some(ident) => ident, + None => continue, + }; + + if ident == "template" { + if template_args.is_some() { + return Err("duplicated 'template' attribute".into()); + } + + match attr.parse_meta() { + Ok(syn::Meta::List(syn::MetaList { nested, .. })) => { + template_args = Some(nested); + } + Ok(_) => return Err("'template' attribute must be a list".into()), + Err(e) => return Err(format!("unable to parse attribute: {}", e).into()), + } + } + } + let template_args = + template_args.ok_or_else(|| CompileError::from("no attribute 'template' found"))?; + + let mut args = Self::default(); + // Loop over the meta attributes and find everything that we + // understand. Return a CompileError if something is not right. + // `source` contains an enum that can represent `path` or `source`. + for item in template_args { + let pair = match item { + syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair, + _ => { + return Err(format!( + "unsupported attribute argument {:?}", + item.to_token_stream() + ) + .into()) + } + }; + let ident = match pair.path.get_ident() { + Some(ident) => ident, + None => unreachable!("not possible in syn::Meta::NameValue(…)"), + }; + + if ident == "path" { + if let syn::Lit::Str(ref s) = pair.lit { + if args.source.is_some() { + return Err("must specify 'source' or 'path', not both".into()); + } + args.source = Some(Source::Path(s.value())); + } else { + return Err("template path must be string literal".into()); + } + } else if ident == "source" { + if let syn::Lit::Str(ref s) = pair.lit { + if args.source.is_some() { + return Err("must specify 'source' or 'path', not both".into()); + } + args.source = Some(Source::Source(s.value())); + } else { + return Err("template source must be string literal".into()); + } + } else if ident == "print" { + if let syn::Lit::Str(ref s) = pair.lit { + args.print = s.value().parse()?; + } else { + return Err("print value must be string literal".into()); + } + } else if ident == "escape" { + if let syn::Lit::Str(ref s) = pair.lit { + args.escaping = Some(s.value()); + } else { + return Err("escape value must be string literal".into()); + } + } else if ident == "ext" { + if let syn::Lit::Str(ref s) = pair.lit { + args.ext = Some(s.value()); + } else { + return Err("ext value must be string literal".into()); + } + } else if ident == "syntax" { + if let syn::Lit::Str(ref s) = pair.lit { + args.syntax = Some(s.value()) + } else { + return Err("syntax value must be string literal".into()); + } + } else if ident == "config" { + if let syn::Lit::Str(ref s) = pair.lit { + args.config_path = Some(s.value()) + } else { + return Err("config value must be string literal".into()); + } + } else { + return Err(format!("unsupported attribute key {:?} found", ident).into()); + } + } + + Ok(args) + } +} + fn find_used_templates( input: &TemplateInput<'_>, map: &mut HashMap, @@ -129,6 +251,8 @@ struct Generator<'a, S: std::hash::BuildHasher> { buf_writable: Vec>, // Counter for write! hash named arguments named: usize, + // If set to `true`, the whitespace characters will be removed by default unless `+` is used. + suppress_whitespace: bool, } impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { @@ -137,6 +261,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { contexts: &'n HashMap<&'n Path, Context<'n>, S>, heritage: Option<&'n Heritage<'_>>, locals: MapChain<'n, &'n str, LocalMeta>, + suppress_whitespace: bool, ) -> Generator<'n, S> { Generator { input, @@ -148,12 +273,19 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { super_block: None, buf_writable: vec![], named: 0, + suppress_whitespace, } } fn child(&mut self) -> Generator<'_, S> { let locals = MapChain::with_parent(&self.locals); - Self::new(self.input, self.contexts, self.heritage, locals) + Self::new( + self.input, + self.contexts, + self.heritage, + locals, + self.suppress_whitespace, + ) } // Takes a Context and generates the relevant implementations. @@ -222,7 +354,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { self.handle(ctx, ctx.nodes, buf, AstLevel::Top) }?; - self.flush_ws(Ws(false, false)); + self.flush_ws(Ws(None, None)); buf.writeln("::askama::Result::Ok(())")?; buf.writeln("}")?; @@ -1684,11 +1816,25 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { self.prepare_ws(ws); } + fn should_trim_ws(&self, ws: Option) -> bool { + match ws { + Some(Whitespace::Trim) => true, + Some(Whitespace::Preserve) => false, + None => self.suppress_whitespace, + } + } + // 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, ws: Ws) { - if self.next_ws.is_some() && !ws.0 { + if self.next_ws.is_none() { + return; + } + + // If `suppress_whitespace` is enabled, we keep the whitespace characters only if there is + // a `+` character. + if !self.should_trim_ws(ws.0) { let val = self.next_ws.unwrap(); if !val.is_empty() { self.buf_writable.push(Writable::Lit(val)); @@ -1701,7 +1847,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { // argument, to determine whether to suppress leading whitespace from the // next literal. fn prepare_ws(&mut self, ws: Ws) { - self.skip_ws = ws.1; + self.skip_ws = self.should_trim_ws(ws.1); } } diff --git a/askama_shared/src/input.rs b/askama_shared/src/input.rs index c70b250f8..350fc01a7 100644 --- a/askama_shared/src/input.rs +++ b/askama_shared/src/input.rs @@ -1,10 +1,10 @@ +use crate::generator::TemplateArgs; use crate::{CompileError, Config, Syntax}; use std::path::{Path, PathBuf}; use std::str::FromStr; use mime::Mime; -use quote::ToTokens; pub(crate) struct TemplateInput<'a> { pub(crate) ast: &'a syn::DeriveInput, @@ -27,103 +27,16 @@ impl TemplateInput<'_> { pub(crate) fn new<'n>( ast: &'n syn::DeriveInput, config: &'n Config<'_>, + args: TemplateArgs, ) -> Result, CompileError> { - // Check that an attribute called `template()` exists once and that it is - // the proper type (list). - let mut template_args = None; - for attr in &ast.attrs { - let ident = match attr.path.get_ident() { - Some(ident) => ident, - None => continue, - }; - - if ident == "template" { - if template_args.is_some() { - return Err("duplicated 'template' attribute".into()); - } - - match attr.parse_meta() { - Ok(syn::Meta::List(syn::MetaList { nested, .. })) => { - template_args = Some(nested); - } - Ok(_) => return Err("'template' attribute must be a list".into()), - Err(e) => return Err(format!("unable to parse attribute: {}", e).into()), - } - } - } - let template_args = - template_args.ok_or_else(|| CompileError::from("no attribute 'template' found"))?; - - // Loop over the meta attributes and find everything that we - // understand. Return a CompileError if something is not right. - // `source` contains an enum that can represent `path` or `source`. - let mut source = None; - let mut print = Print::None; - let mut escaping = None; - let mut ext = None; - let mut syntax = None; - for item in template_args { - let pair = match item { - syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair, - _ => { - return Err(format!( - "unsupported attribute argument {:?}", - item.to_token_stream() - ) - .into()) - } - }; - let ident = match pair.path.get_ident() { - Some(ident) => ident, - None => unreachable!("not possible in syn::Meta::NameValue(…)"), - }; - - if ident == "path" { - if let syn::Lit::Str(ref s) = pair.lit { - if source.is_some() { - return Err("must specify 'source' or 'path', not both".into()); - } - source = Some(Source::Path(s.value())); - } else { - return Err("template path must be string literal".into()); - } - } else if ident == "source" { - if let syn::Lit::Str(ref s) = pair.lit { - if source.is_some() { - return Err("must specify 'source' or 'path', not both".into()); - } - source = Some(Source::Source(s.value())); - } else { - return Err("template source must be string literal".into()); - } - } else if ident == "print" { - if let syn::Lit::Str(ref s) = pair.lit { - print = s.value().parse()?; - } else { - return Err("print value must be string literal".into()); - } - } else if ident == "escape" { - if let syn::Lit::Str(ref s) = pair.lit { - escaping = Some(s.value()); - } else { - return Err("escape value must be string literal".into()); - } - } else if ident == "ext" { - if let syn::Lit::Str(ref s) = pair.lit { - ext = Some(s.value()); - } else { - return Err("ext value must be string literal".into()); - } - } else if ident == "syntax" { - if let syn::Lit::Str(ref s) = pair.lit { - syntax = Some(s.value()) - } else { - return Err("syntax value must be string literal".into()); - } - } else { - return Err(format!("unsupported attribute key {:?} found", ident).into()); - } - } + let TemplateArgs { + source, + print, + escaping, + ext, + syntax, + .. + } = args; // Validate the `source` and `ext` value together, since they are // related. In case `source` was used instead of `path`, the value @@ -261,6 +174,12 @@ impl FromStr for Print { } } +impl Default for Print { + fn default() -> Self { + Self::None + } +} + #[doc(hidden)] pub fn extension_to_mime_type(ext: &str) -> Mime { let basic_type = mime_guess::from_ext(ext).first_or_octet_stream(); diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index 5913eb31c..07331bcd1 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -118,6 +118,7 @@ struct Config<'a> { syntaxes: BTreeMap>, default_syntax: &'a str, escapers: Vec<(HashSet, String)>, + suppress_whitespace: bool, } impl Config<'_> { @@ -134,17 +135,19 @@ impl Config<'_> { RawConfig::from_toml_str(s)? }; - let (dirs, default_syntax) = match raw.general { + let (dirs, default_syntax, suppress_whitespace) = match raw.general { Some(General { dirs, default_syntax, + suppress_whitespace, }) => ( dirs.map_or(default_dirs, |v| { v.into_iter().map(|dir| root.join(dir)).collect() }), default_syntax.unwrap_or(DEFAULT_SYNTAX_NAME), + suppress_whitespace.unwrap_or(false), ), - None => (default_dirs, DEFAULT_SYNTAX_NAME), + None => (default_dirs, DEFAULT_SYNTAX_NAME, false), }; if let Some(raw_syntaxes) = raw.syntax { @@ -186,6 +189,7 @@ impl Config<'_> { syntaxes, default_syntax, escapers, + suppress_whitespace, }) } @@ -303,6 +307,7 @@ struct General<'a> { #[cfg_attr(feature = "serde", serde(borrow))] dirs: Option>, default_syntax: Option<&'a str>, + suppress_whitespace: Option, } #[cfg_attr(feature = "serde", derive(Deserialize))] @@ -322,12 +327,18 @@ struct RawEscaper<'a> { extensions: Vec<&'a str>, } -fn read_config_file() -> std::result::Result { +fn read_config_file(config_path: &Option) -> std::result::Result { let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let filename = root.join(CONFIG_FILE_NAME); + let filename = match config_path { + Some(config_path) => root.join(config_path), + None => root.join(CONFIG_FILE_NAME), + }; + if filename.exists() { fs::read_to_string(&filename) .map_err(|_| format!("unable to read {:?}", filename.to_str().unwrap()).into()) + } else if config_path.is_some() { + Err(format!("`{}` does not exist", root.display()).into()) } else { Ok("".to_string()) } @@ -646,4 +657,16 @@ mod tests { test.dyn_write_into(&mut vec).unwrap(); assert_eq!(vec, vec![b't', b'e', b's', b't']); } + + #[test] + fn test_suppress_whitespace_parsing() { + let config = Config::new( + r#" + [general] + suppress_whitespace = true + "#, + ) + .unwrap(); + assert!(config.suppress_whitespace); + } } diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index 2a7f49f64..4654e82d5 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -130,7 +130,26 @@ pub(crate) enum Target<'a> { } #[derive(Clone, Copy, Debug, PartialEq)] -pub(crate) struct Ws(pub(crate) bool, pub(crate) bool); +pub(crate) enum Whitespace { + Preserve, + Trim, +} + +impl From for Whitespace { + fn from(c: char) -> Self { + match c { + '+' => Self::Preserve, + '-' => Self::Trim, + _ => panic!("unsupported `Whitespace` conversion"), + } + } +} + +/// First field is "minus/plus sign was used on the left part of the item". +/// +/// Second field is "minus/plus sign was used on the right part of the item". +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) struct Ws(pub(crate) Option, pub(crate) Option); pub(crate) type Cond<'a> = (Ws, Option>, Vec>); @@ -657,6 +676,10 @@ expr_prec_layer!(expr_compare, expr_bor, "==", "!=", ">=", ">", "<=", "<"); expr_prec_layer!(expr_and, expr_compare, "&&"); expr_prec_layer!(expr_or, expr_and, "||"); +fn expr_handle_ws(i: &str) -> IResult<&str, Whitespace> { + alt((char('-'), char('+')))(i).map(|(s, r)| (s, Whitespace::from(r))) +} + fn expr_any(i: &str) -> IResult<&str, Expr<'_>> { let range_right = |i| pair(ws(alt((tag("..="), tag("..")))), opt(expr_or))(i); alt(( @@ -676,31 +699,31 @@ fn expr_any(i: &str) -> IResult<&str, Expr<'_>> { fn expr_node<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( |i| tag_expr_start(i, s), - cut(tuple((opt(char('-')), ws(expr_any), opt(char('-')), |i| { - tag_expr_end(i, s) - }))), + cut(tuple(( + opt(expr_handle_ws), + ws(expr_any), + opt(expr_handle_ws), + |i| tag_expr_end(i, s), + ))), )); let (i, (_, (pws, expr, nws, _))) = p(i)?; - Ok((i, Node::Expr(Ws(pws.is_some(), nws.is_some()), expr))) + Ok((i, Node::Expr(Ws(pws, nws), expr))) } fn block_call(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("call")), cut(tuple(( opt(tuple((ws(identifier), ws(tag("::"))))), ws(identifier), ws(arguments), - opt(char('-')), + opt(expr_handle_ws), ))), )); let (i, (pws, _, (scope, name, args, nws))) = p(i)?; let scope = scope.map(|(scope, _)| scope); - Ok(( - i, - Node::Call(Ws(pws.is_some(), nws.is_some()), scope, name, args), - )) + Ok((i, Node::Call(Ws(pws, nws), scope, name, args))) } fn cond_if(i: &str) -> IResult<&str, CondTest<'_>> { @@ -722,86 +745,83 @@ fn cond_if(i: &str) -> IResult<&str, CondTest<'_>> { fn cond_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Cond<'a>> { let mut p = tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("else")), cut(tuple(( opt(cond_if), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(|i| parse_template(i, s)), ))), )); let (i, (_, pws, _, (cond, nws, _, block))) = p(i)?; - Ok((i, (Ws(pws.is_some(), nws.is_some()), cond, block))) + Ok((i, (Ws(pws, nws), cond, block))) } fn block_if<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), cond_if, cut(tuple(( - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(tuple(( |i| parse_template(i, s), many0(|i| cond_block(i, s)), cut(tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endif")), - opt(char('-')), + opt(expr_handle_ws), ))), ))), ))), )); let (i, (pws1, cond, (nws1, _, (block, elifs, (_, pws2, _, nws2))))) = p(i)?; - let mut res = vec![(Ws(pws1.is_some(), nws1.is_some()), Some(cond), block)]; + let mut res = vec![(Ws(pws1, nws1), Some(cond), block)]; res.extend(elifs); - Ok((i, Node::Cond(res, Ws(pws2.is_some(), nws2.is_some())))) + Ok((i, Node::Cond(res, Ws(pws2, nws2)))) } fn match_else_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> { let mut p = tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("else")), cut(tuple(( - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(|i| parse_template(i, s)), ))), )); let (i, (_, pws, _, (nws, _, block))) = p(i)?; - Ok(( - i, - (Ws(pws.is_some(), nws.is_some()), Target::Name("_"), block), - )) + Ok((i, (Ws(pws, nws), Target::Name("_"), block))) } fn when_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, When<'a>> { let mut p = tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("when")), cut(tuple(( ws(target), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(|i| parse_template(i, s)), ))), )); let (i, (_, pws, _, (target, nws, _, block))) = p(i)?; - Ok((i, (Ws(pws.is_some(), nws.is_some()), target, block))) + Ok((i, (Ws(pws, nws), target, block))) } fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("match")), cut(tuple(( ws(expr_any), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(tuple(( ws(many0(ws(value((), |i| block_comment(i, s))))), @@ -810,9 +830,9 @@ fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { opt(|i| match_else_block(i, s)), cut(tuple(( ws(|i| tag_block_start(i, s)), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endmatch")), - opt(char('-')), + opt(expr_handle_ws), ))), ))), ))), @@ -825,25 +845,17 @@ fn block_match<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { arms.push(arm); } - Ok(( - i, - Node::Match( - Ws(pws1.is_some(), nws1.is_some()), - expr, - arms, - Ws(pws2.is_some(), nws2.is_some()), - ), - )) + Ok((i, Node::Match(Ws(pws1, nws1), expr, arms, Ws(pws2, nws2)))) } fn block_let(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(alt((tag("let"), tag("set")))), cut(tuple(( ws(target), opt(tuple((ws(char('=')), ws(expr_any)))), - opt(char('-')), + opt(expr_handle_ws), ))), )); let (i, (pws, _, (var, val, nws))) = p(i)?; @@ -851,9 +863,9 @@ fn block_let(i: &str) -> IResult<&str, Node<'_>> { Ok(( i, if let Some((_, val)) = val { - Node::Let(Ws(pws.is_some(), nws.is_some()), var, val) + Node::Let(Ws(pws, nws), var, val) } else { - Node::LetDecl(Ws(pws.is_some(), nws.is_some()), var) + Node::LetDecl(Ws(pws, nws), var) }, )) } @@ -871,20 +883,20 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = preceded( ws(tag("else")), cut(tuple(( - opt(tag("-")), + opt(expr_handle_ws), delimited( |i| tag_block_end(i, s), |i| parse_template(i, s), |i| tag_block_start(i, s), ), - opt(tag("-")), + opt(expr_handle_ws), ))), ); let (i, (pws, nodes, nws)) = p(i)?; - Ok((i, (pws.is_some(), nodes, nws.is_some()))) + Ok((i, (pws, nodes, nws))) }; let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("for")), cut(tuple(( ws(target), @@ -892,16 +904,16 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { cut(tuple(( ws(expr_any), opt(if_cond), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(tuple(( |i| parse_loop_content(i, s), cut(tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), opt(else_block), ws(tag("endfor")), - opt(char('-')), + opt(expr_handle_ws), ))), ))), ))), @@ -913,14 +925,14 @@ fn block_for<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { Ok(( i, Node::Loop(Loop { - ws1: Ws(pws1.is_some(), nws1.is_some()), + ws1: Ws(pws1, nws1), var, iter, cond, body, - ws2: Ws(pws2.is_some(), nws3), + ws2: Ws(pws2, nws3), else_block, - ws3: Ws(pws3, nws2.is_some()), + ws3: Ws(pws3, nws2), }), )) } @@ -932,9 +944,9 @@ fn block_extends(i: &str) -> IResult<&str, Node<'_>> { fn block_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut start = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("block")), - cut(tuple((ws(identifier), opt(char('-')), |i| { + cut(tuple((ws(identifier), opt(expr_handle_ws), |i| { tag_block_end(i, s) }))), )); @@ -944,67 +956,59 @@ fn block_block<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { |i| parse_template(i, s), cut(tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endblock")), - cut(tuple((opt(ws(tag(name))), opt(char('-'))))), + cut(tuple((opt(ws(tag(name))), opt(expr_handle_ws)))), ))), ))); let (i, (contents, (_, pws2, _, (_, nws2)))) = end(i)?; Ok(( i, - Node::BlockDef( - Ws(pws1.is_some(), nws1.is_some()), - name, - contents, - Ws(pws2.is_some(), nws2.is_some()), - ), + Node::BlockDef(Ws(pws1, nws1), name, contents, Ws(pws2, nws2)), )) } fn block_include(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("include")), - cut(pair(ws(str_lit), opt(char('-')))), + cut(pair(ws(str_lit), opt(expr_handle_ws))), )); let (i, (pws, _, (name, nws))) = p(i)?; - Ok((i, Node::Include(Ws(pws.is_some(), nws.is_some()), name))) + Ok((i, Node::Include(Ws(pws, nws), name))) } fn block_import(i: &str) -> IResult<&str, Node<'_>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("import")), cut(tuple(( ws(str_lit), ws(tag("as")), - cut(pair(ws(identifier), opt(char('-')))), + cut(pair(ws(identifier), opt(expr_handle_ws))), ))), )); let (i, (pws, _, (name, _, (scope, nws)))) = p(i)?; - Ok(( - i, - Node::Import(Ws(pws.is_some(), nws.is_some()), name, scope), - )) + Ok((i, Node::Import(Ws(pws, nws), name, scope))) } fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("macro")), cut(tuple(( ws(identifier), ws(parameters), - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), cut(tuple(( |i| parse_template(i, s), cut(tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endmacro")), - opt(char('-')), + opt(expr_handle_ws), ))), ))), ))), @@ -1018,10 +1022,10 @@ fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { Node::Macro( name, Macro { - ws1: Ws(pws1.is_some(), nws1.is_some()), + ws1: Ws(pws1, nws1), args: params, nodes: contents, - ws2: Ws(pws2.is_some(), nws2.is_some()), + ws2: Ws(pws2, nws2), }, ), )) @@ -1030,17 +1034,17 @@ fn block_macro<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { fn block_raw<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let endraw = tuple(( |i| tag_block_start(i, s), - opt(char('-')), + opt(expr_handle_ws), ws(tag("endraw")), - opt(char('-')), + opt(expr_handle_ws), peek(|i| tag_block_end(i, s)), )); let mut p = tuple(( - opt(char('-')), + opt(expr_handle_ws), ws(tag("raw")), cut(tuple(( - opt(char('-')), + opt(expr_handle_ws), |i| tag_block_end(i, s), consumed(skip_till(endraw)), ))), @@ -1051,27 +1055,31 @@ fn block_raw<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { Node::Lit(lws, val, rws) => (lws, val, rws), _ => unreachable!(), }; - let ws1 = Ws(pws1.is_some(), nws1.is_some()); - let ws2 = Ws(pws2.is_some(), nws2.is_some()); + let ws1 = Ws(pws1, nws1); + let ws2 = Ws(pws2, nws2); Ok((i, Node::Raw(ws1, lws, val, rws, ws2))) } fn break_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { - let mut p = tuple((opt(char('-')), ws(tag("break")), opt(char('-')))); + let mut p = tuple((opt(expr_handle_ws), ws(tag("break")), opt(expr_handle_ws))); let (j, (pws, _, nws)) = p(i)?; if s.loop_depth.get() == 0 { return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))); } - Ok((j, Node::Break(Ws(pws.is_some(), nws.is_some())))) + Ok((j, Node::Break(Ws(pws, nws)))) } fn continue_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { - let mut p = tuple((opt(char('-')), ws(tag("continue")), opt(char('-')))); + let mut p = tuple(( + opt(expr_handle_ws), + ws(tag("continue")), + opt(expr_handle_ws), + )); let (j, (pws, _, nws)) = p(i)?; if s.loop_depth.get() == 0 { return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))); } - Ok((j, Node::Continue(Ws(pws.is_some(), nws.is_some())))) + Ok((j, Node::Continue(Ws(pws, nws)))) } fn block_node<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { @@ -1120,13 +1128,20 @@ fn block_comment<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> { let mut p = tuple(( |i| tag_comment_start(i, s), cut(tuple(( - opt(char('-')), + opt(expr_handle_ws), |i| block_comment_body(i, s), |i| tag_comment_end(i, s), ))), )); let (i, (_, (pws, tail, _))) = p(i)?; - Ok((i, Node::Comment(Ws(pws.is_some(), tail.ends_with('-'))))) + let nws = if tail.ends_with('-') { + Some(Whitespace::Trim) + } else if tail.ends_with('+') { + Some(Whitespace::Preserve) + } else { + None + }; + Ok((i, Node::Comment(Ws(pws, nws)))) } fn parse_template<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Vec>> { @@ -1202,7 +1217,7 @@ pub(crate) fn parse<'a>( #[cfg(test)] mod tests { - use super::{Expr, Node, Ws}; + use super::{Expr, Node, Whitespace, Ws}; use crate::Syntax; fn check_ws_split(s: &str, res: &(&str, &str, &str)) { @@ -1239,29 +1254,23 @@ mod tests { let syntax = Syntax::default(); assert_eq!( super::parse("{{ strvar|e }}", &syntax).unwrap(), - vec![Node::Expr( - Ws(false, false), - Filter("e", vec![Var("strvar")]), - )], + vec![Node::Expr(Ws(None, None), Filter("e", vec![Var("strvar")]),)], ); assert_eq!( super::parse("{{ 2|abs }}", &syntax).unwrap(), - vec![Node::Expr( - Ws(false, false), - Filter("abs", vec![NumLit("2")]), - )], + vec![Node::Expr(Ws(None, None), Filter("abs", vec![NumLit("2")]),)], ); assert_eq!( super::parse("{{ -2|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Unary("-", NumLit("2").into())]), )], ); assert_eq!( super::parse("{{ (1 - 2)|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter( "abs", vec![Group( @@ -1277,11 +1286,11 @@ mod tests { let syntax = Syntax::default(); assert_eq!( super::parse("{{ 2 }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::NumLit("2"),)], + vec![Node::Expr(Ws(None, None), Expr::NumLit("2"),)], ); assert_eq!( super::parse("{{ 2.5 }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::NumLit("2.5"),)], + vec![Node::Expr(Ws(None, None), Expr::NumLit("2.5"),)], ); } @@ -1291,16 +1300,16 @@ mod tests { assert_eq!( super::parse("{{ foo }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Var("foo"))], + vec![Node::Expr(Ws(None, None), Expr::Var("foo"))], ); assert_eq!( super::parse("{{ foo_bar }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Var("foo_bar"))], + vec![Node::Expr(Ws(None, None), Expr::Var("foo_bar"))], ); assert_eq!( super::parse("{{ none }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Var("none"))], + vec![Node::Expr(Ws(None, None), Expr::Var("none"))], ); } @@ -1310,16 +1319,16 @@ mod tests { assert_eq!( super::parse("{{ FOO }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Path(vec!["FOO"]))], + vec![Node::Expr(Ws(None, None), Expr::Path(vec!["FOO"]))], ); assert_eq!( super::parse("{{ FOO_BAR }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Path(vec!["FOO_BAR"]))], + vec![Node::Expr(Ws(None, None), Expr::Path(vec!["FOO_BAR"]))], ); assert_eq!( super::parse("{{ NONE }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Path(vec!["NONE"]))], + vec![Node::Expr(Ws(None, None), Expr::Path(vec!["NONE"]))], ); } @@ -1329,12 +1338,12 @@ mod tests { assert_eq!( super::parse("{{ None }}", &s).unwrap(), - vec![Node::Expr(Ws(false, false), Expr::Path(vec!["None"]))], + vec![Node::Expr(Ws(None, None), Expr::Path(vec!["None"]))], ); assert_eq!( super::parse("{{ Some(123) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["Some"])), vec![Expr::NumLit("123")] @@ -1345,14 +1354,14 @@ mod tests { assert_eq!( super::parse("{{ Ok(123) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call(Box::new(Expr::Path(vec!["Ok"])), vec![Expr::NumLit("123")]), )], ); assert_eq!( super::parse("{{ Err(123) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call(Box::new(Expr::Path(vec!["Err"])), vec![Expr::NumLit("123")]), )], ); @@ -1363,7 +1372,7 @@ mod tests { assert_eq!( super::parse("{{ function(\"123\", 3) }}", &Syntax::default()).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Var("function")), vec![Expr::StrLit("123"), Expr::NumLit("3")] @@ -1379,14 +1388,14 @@ mod tests { assert_eq!( super::parse("{{ Option::None }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Path(vec!["Option", "None"]) )], ); assert_eq!( super::parse("{{ Option::Some(123) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["Option", "Some"])), vec![Expr::NumLit("123")], @@ -1397,7 +1406,7 @@ mod tests { assert_eq!( super::parse("{{ self::function(\"123\", 3) }}", &s).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["self", "function"])), vec![Expr::StrLit("123"), Expr::NumLit("3")], @@ -1412,7 +1421,7 @@ mod tests { assert_eq!( super::parse("{{ std::string::String::new() }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["std", "string", "String", "new"])), vec![] @@ -1422,7 +1431,7 @@ mod tests { assert_eq!( super::parse("{{ ::std::string::String::new() }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Expr::Call( Box::new(Expr::Path(vec!["", "std", "string", "String", "new"])), vec![] @@ -1449,7 +1458,7 @@ mod tests { assert_eq!( super::parse("{{ a + b == c }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "==", BinOp("+", Var("a").into(), Var("b").into()).into(), @@ -1460,7 +1469,7 @@ mod tests { assert_eq!( super::parse("{{ a + b * c - d / e }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "-", BinOp( @@ -1476,7 +1485,7 @@ mod tests { assert_eq!( super::parse("{{ a * (b + c) / -d }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "/", BinOp( @@ -1492,7 +1501,7 @@ mod tests { assert_eq!( super::parse("{{ a || b && c || d && e }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "||", BinOp( @@ -1514,7 +1523,7 @@ mod tests { assert_eq!( super::parse("{{ a + b + c }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "+", BinOp("+", Var("a").into(), Var("b").into()).into(), @@ -1525,7 +1534,7 @@ mod tests { assert_eq!( super::parse("{{ a * b * c }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "*", BinOp("*", Var("a").into(), Var("b").into()).into(), @@ -1536,7 +1545,7 @@ mod tests { assert_eq!( super::parse("{{ a && b && c }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "&&", BinOp("&&", Var("a").into(), Var("b").into()).into(), @@ -1547,7 +1556,7 @@ mod tests { assert_eq!( super::parse("{{ a + b - c + d }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "+", BinOp( @@ -1563,7 +1572,7 @@ mod tests { assert_eq!( super::parse("{{ a == b != c > d > e == f }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "==", BinOp( @@ -1595,7 +1604,7 @@ mod tests { assert_eq!( super::parse("{{ a[b](c) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Call( Box::new(Index(Box::new(Var("a")), Box::new(Var("b")))), vec![Var("c")], @@ -1605,7 +1614,7 @@ mod tests { assert_eq!( super::parse("{{ (a + b)(c) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Call( Box::new(Group(Box::new(BinOp( "+", @@ -1619,7 +1628,7 @@ mod tests { assert_eq!( super::parse("{{ a + b(c) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "+", Box::new(Var("a")), @@ -1630,7 +1639,7 @@ mod tests { assert_eq!( super::parse("{{ (-a)(b) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Call( Box::new(Group(Box::new(Unary("-", Box::new(Var("a")))))), vec![Var("b")], @@ -1640,7 +1649,7 @@ mod tests { assert_eq!( super::parse("{{ -a(b) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Unary("-", Box::new(Call(Box::new(Var("a")), vec![Var("b")])),), )], ); @@ -1652,32 +1661,70 @@ mod tests { assert_eq!( super::parse("{##}", s).unwrap(), - vec![Node::Comment(Ws(false, false))], + vec![Node::Comment(Ws(None, None))], ); assert_eq!( super::parse("{#- #}", s).unwrap(), - vec![Node::Comment(Ws(true, false))], + vec![Node::Comment(Ws(Some(Whitespace::Trim), None))], ); assert_eq!( super::parse("{# -#}", s).unwrap(), - vec![Node::Comment(Ws(false, true))], + vec![Node::Comment(Ws(None, Some(Whitespace::Trim)))], ); assert_eq!( super::parse("{#--#}", s).unwrap(), - vec![Node::Comment(Ws(true, true))], + vec![Node::Comment(Ws( + Some(Whitespace::Trim), + Some(Whitespace::Trim) + ))], ); - assert_eq!( super::parse("{#- foo\n bar -#}", s).unwrap(), - vec![Node::Comment(Ws(true, true))], + vec![Node::Comment(Ws( + Some(Whitespace::Trim), + Some(Whitespace::Trim) + ))], ); assert_eq!( super::parse("{#- foo\n {#- bar\n -#} baz -#}", s).unwrap(), - vec![Node::Comment(Ws(true, true))], + vec![Node::Comment(Ws( + Some(Whitespace::Trim), + Some(Whitespace::Trim) + ))], + ); + assert_eq!( + super::parse("{#+ #}", s).unwrap(), + vec![Node::Comment(Ws(Some(Whitespace::Preserve), None))], + ); + assert_eq!( + super::parse("{# +#}", s).unwrap(), + vec![Node::Comment(Ws(None, Some(Whitespace::Preserve)))], + ); + assert_eq!( + super::parse("{#++#}", s).unwrap(), + vec![Node::Comment(Ws( + Some(Whitespace::Preserve), + Some(Whitespace::Preserve) + ))], + ); + assert_eq!( + super::parse("{#+ foo\n bar +#}", s).unwrap(), + vec![Node::Comment(Ws( + Some(Whitespace::Preserve), + Some(Whitespace::Preserve) + ))], ); + assert_eq!( + super::parse("{#+ foo\n {#+ bar\n +#} baz -+#}", s).unwrap(), + vec![Node::Comment(Ws( + Some(Whitespace::Preserve), + Some(Whitespace::Preserve) + ))], + ); + assert_eq!( super::parse("{# foo {# bar #} {# {# baz #} qux #} #}", s).unwrap(), - vec![Node::Comment(Ws(false, false))], + vec![Node::Comment(Ws(None, None))], ); } @@ -1687,74 +1734,74 @@ mod tests { let syntax = Syntax::default(); assert_eq!( super::parse("{{ () }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![]),)], ); assert_eq!( super::parse("{{ (1) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Group(Box::new(NumLit("1"))),)], + vec![Node::Expr(Ws(None, None), Group(Box::new(NumLit("1"))),)], ); assert_eq!( super::parse("{{ (1,) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( super::parse("{{ (1, ) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( super::parse("{{ (1 ,) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( super::parse("{{ (1 , ) }}", &syntax).unwrap(), - vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)], + vec![Node::Expr(Ws(None, None), Tuple(vec![NumLit("1")]),)], ); assert_eq!( super::parse("{{ (1, 2) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2")]), )], ); assert_eq!( super::parse("{{ (1, 2,) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2")]), )], ); assert_eq!( super::parse("{{ (1, 2, 3) }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Tuple(vec![NumLit("1"), NumLit("2"), NumLit("3")]), )], ); assert_eq!( super::parse("{{ ()|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Tuple(vec![])]), )], ); assert_eq!( super::parse("{{ () | abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp("|", Box::new(Tuple(vec![])), Box::new(Var("abs"))), )], ); assert_eq!( super::parse("{{ (1)|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Group(Box::new(NumLit("1")))]), )], ); assert_eq!( super::parse("{{ (1) | abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "|", Box::new(Group(Box::new(NumLit("1")))), @@ -1765,14 +1812,14 @@ mod tests { assert_eq!( super::parse("{{ (1,)|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Tuple(vec![NumLit("1")])]), )], ); assert_eq!( super::parse("{{ (1,) | abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "|", Box::new(Tuple(vec![NumLit("1")])), @@ -1783,14 +1830,14 @@ mod tests { assert_eq!( super::parse("{{ (1, 2)|abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), Filter("abs", vec![Tuple(vec![NumLit("1"), NumLit("2")])]), )], ); assert_eq!( super::parse("{{ (1, 2) | abs }}", &syntax).unwrap(), vec![Node::Expr( - Ws(false, false), + Ws(None, None), BinOp( "|", Box::new(Tuple(vec![NumLit("1"), NumLit("2")])), diff --git a/book/src/configuration.md b/book/src/configuration.md index 7c687241c..05c18a07c 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -11,8 +11,35 @@ This example file demonstrates the default configuration: [general] # Directories to search for templates, relative to the crate root. dirs = ["templates"] +# Unless you add a `-` in a block, whitespace won't be trimmed. +suppress_whitespace = false ``` +In the default configuration, you can use the `-` operator to indicate that +whitespace should be suppressed before or after a block. For example: + +```jinja +
+ + +{%- if something %} +Hello +(% endif %} +``` + +In the template above, only the whitespace between `
` and `{%-` will be +suppressed. If you enable `suppress_whitespace`, whitespace characters before +and after each block will be suppressed by default. To preserve the whitespace +characters, you can use the `+` operator: + +```jinja +{% if something +%} +Hello +(%+ endif %} +``` + +In this example, `Hello` will be surrounded with newline characters. + Here is an example that defines two custom syntaxes: ```toml @@ -64,4 +91,4 @@ first escaper configured and ending with the default escapers for HTML (extensions `html`, `htm`, `xml`, `j2`, `jinja`, `jinja2`) and plain text (no escaping; `md`, `yml`, `none`, `txt`, and the empty string). Note that this means you can also define other escapers that match different extensions -to the same escaper. \ No newline at end of file +to the same escaper. diff --git a/book/src/creating_templates.md b/book/src/creating_templates.md index 406a8255d..e087ede5c 100644 --- a/book/src/creating_templates.md +++ b/book/src/creating_templates.md @@ -84,10 +84,17 @@ recognized: struct HelloTemplate<'a> { ... } ``` * `syntax` (as `syntax = "foo"`): set the syntax name for a parser defined - in the configuration file. The default syntax , "default", is the one + in the configuration file. The default syntax , "default", is the one provided by Askama. ```rust #[derive(Template)] #[template(path = "hello.html", syntax = "foo")] struct HelloTemplate<'a> { ... } - ``` \ No newline at end of file + ``` +* `config` (as `config = "config_file_path"`): set the path for the config file + to be used. The path is interpreted as relative to your crate root. + ```rust + #[derive(Template)] + #[template(path = "hello.html", config = "config.toml")] + struct HelloTemplate<'a> { ... } + ``` diff --git a/testing/test_trim.toml b/testing/test_trim.toml new file mode 100644 index 000000000..57e88f882 --- /dev/null +++ b/testing/test_trim.toml @@ -0,0 +1,2 @@ +[general] +suppress_whitespace = true diff --git a/testing/tests/gen_ws_tests.py b/testing/tests/gen_ws_tests.py index 5d2a332f0..bf59a097d 100644 --- a/testing/tests/gen_ws_tests.py +++ b/testing/tests/gen_ws_tests.py @@ -10,7 +10,10 @@ IF, ELSE_IF, ELSE, END_IF = 0, 1, 2, 3 NL = "\\n" -dash = lambda ws: [" ", "-"][ws] + + +def handle_ws(ws, trim=True): + return [" ", "-" if trim else "+"][ws] def trim(s, ws): @@ -40,8 +43,9 @@ def pairwise(iterable): return zip(a, b) -def write_cond(conds, active_branch): +def write_cond(conds, active_branch, sign, invert): n = len(conds) - 1 + trim = sign == "-" lits = [] for i in range(1, n + 2 + 1): @@ -54,7 +58,7 @@ def write_cond(conds, active_branch): kind = cond_kind(i, n) b = str(i == active_branch).lower() cond = [f"if {b}", f"else if {b}", "else", "endif"][kind] - cond = f"{{%{dash(pws)} {cond} {dash(nws)}%}}" + cond = f"{{%{handle_ws(pws, trim)} {cond} {handle_ws(nws, trim)}%}}" conds[i] = cond it = map("".join, lits) @@ -64,8 +68,15 @@ def write_cond(conds, active_branch): expected = f"{lits[0][0]}{lits[0][1]}" for i, (cond, (before, after)) in enumerate(zip(conds, pairwise(lits))): kind = cond_kind(i, n) - pws = cond.startswith("{%-") - nws = cond.endswith("-%}") + pws = cond.startswith(f"{{%{sign}") + nws = cond.endswith(f"{sign}%}}") + + if sign == "+": + pws = not pws + nws = not nws + elif invert: + pws = True + nws = True cond = i == active_branch prev_cond = i == (active_branch + 1) @@ -87,13 +98,13 @@ def write_match(contents, arms, match_ws): code = before pws, nws = match_ws[0] - code += f"{{%{dash(pws)} match {expr} {dash(nws)}%}}" + code += f"{{%{handle_ws(pws)} match {expr} {handle_ws(nws)}%}}" for (arm, expr), (pws, nws) in zip(arms, match_ws[1:-1]): - code += f"{{%{dash(pws)} when {arm} {dash(nws)}%}}{expr}" + code += f"{{%{handle_ws(pws)} when {arm} {handle_ws(nws)}%}}{expr}" pws, nws = match_ws[-1] - code += f"{{%{dash(pws)} endmatch {dash(nws)}%}}" + code += f"{{%{handle_ws(pws)} endmatch {handle_ws(nws)}%}}" code += after return code @@ -109,22 +120,25 @@ def write_match_result(active_arm, contents, arms, match_ws): return expected -def write_cond_tests(f): +def write_cond_tests(f, name_extra, config): f.write(""" -macro_rules! test_template { - ($source:literal, $rendered:expr) => {{ +macro_rules! test_template{name_extra} {{ + ($source:literal, $rendered:expr) => {{{{ #[derive(Template)] - #[template(source = $source, ext = "txt")] + #[template(source = $source, ext = "txt"{config})] struct CondWs; assert_eq!(CondWs.render().unwrap(), $rendered); - }}; -} + }}}}; +}} #[rustfmt::skip] #[test] -fn test_cond_ws() { -""") +fn test_cond_ws{name_extra}() {{ +""".format(name_extra = name_extra, config = config)) + + invert = len(name_extra) > 0 + sign = "+" if invert else "-" for branches in range(1, BRANCHES + 1): for x in product([False, True], repeat=(branches+1)*2): @@ -133,8 +147,13 @@ def write_cond_tests(f): conds = list(zip(x[::2], x[1::2])) for i in range(branches): - code, expected = write_cond(conds, i) - f.write(f' test_template!("{code}", "{expected}");\n') + code, expected = write_cond(conds, i, sign, invert) + f.write(f' test_template{name_extra}!("{code}", "{expected}");\n') + if invert and ("{%+" in code or "+%}" in code): + # We also check that the minus sign is trimming with suppress_whitespace + # option enabled. + code, expected = write_cond(conds, i, "-", invert) + f.write(f' test_template{name_extra}!("{code}", "{expected}");\n') if branches != BRANCHES: f.write("\n") @@ -182,5 +201,6 @@ def write_match_tests(f): with open("ws.rs", "w") as f: f.write("// This file is auto generated by gen_ws_tests.py\n\n") f.write("use askama::Template;\n") - write_cond_tests(f) + write_cond_tests(f, "", "") + write_cond_tests(f, "_inverted", ', config = "test_trim.toml"') write_match_tests(f) diff --git a/testing/tests/ws.rs b/testing/tests/ws.rs index 3b4eb162c..77af1ecd1 100644 --- a/testing/tests/ws.rs +++ b/testing/tests/ws.rs @@ -162,6 +162,307 @@ fn test_cond_ws() { test_template!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); } +macro_rules! test_template_inverted { + ($source:literal, $rendered:expr) => {{ + #[derive(Template)] + #[template(source = $source, ext = "txt", config = "test_trim.toml")] + struct CondWs; + + assert_eq!(CondWs.render().unwrap(), $rendered); + }}; +} + +#[rustfmt::skip] +#[test] +fn test_cond_ws_inverted() { + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% endif +%}\n\n\n3\r\n\r\n\r\n", "\n12\n\n\n3"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% endif -%}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ endif %}\n\n\n3\r\n\r\n\r\n", "\n12\r\n\r\n3"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- endif %}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ endif +%}\n\n\n3\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n3"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- endif -%}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n1\n\n23"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% endif +%}\n\n\n3\r\n\r\n\r\n", "\n1\n\n2\n\n\n3"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% endif -%}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ endif %}\n\n\n3\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n3"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- endif %}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ endif +%}\n\n\n3\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n3"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- endif -%}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n1\r\n23"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% endif +%}\n\n\n3\r\n\r\n\r\n", "\n1\r\n2\n\n\n3"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% endif -%}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ endif %}\n\n\n3\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n3"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- endif %}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ endif +%}\n\n\n3\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n3"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- endif -%}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n1\r\n\n\n23"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% endif %}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% endif +%}\n\n\n3\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n3"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% endif -%}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ endif %}\n\n\n3\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n3"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- endif %}\n\n\n3\r\n\r\n\r\n", "\n123"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ endif +%}\n\n\n3\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n3"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- endif -%}\n\n\n3\r\n\r\n\r\n", "\n123"); + + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n34"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n34"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n12\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n24"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n24"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n24"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n34"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n24"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n13\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n34"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{% if true +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{% if false +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\n\n\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{% if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n24"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n34"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n24"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n24"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n34"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n24"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n34"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n34"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false %}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false %}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n24"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n34"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n24"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n24"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n34"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n24"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{% else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{% else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n34"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{%+ else %}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else %}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n34"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{% endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{% endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif %}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); + test_template_inverted!("\n1\r\n{%+ if true +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n2\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if true -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n124"); + test_template_inverted!("\n1\r\n{%+ if false +%}\n\n2\r\n\r\n{%+ else +%}\n\n\n3\r\n\r\n\r\n{%+ endif +%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n1\r\n\n\n\n3\r\n\r\n\r\n\n\n\n\n4"); + test_template_inverted!("\n1\r\n{%- if false -%}\n\n2\r\n\r\n{%- else -%}\n\n\n3\r\n\r\n\r\n{%- endif -%}\n\n\n\n4\r\n\r\n\r\n\r\n", "\n134"); +} + #[rustfmt::skip] macro_rules! test_match { ($source:literal, $some_rendered:expr, $none_rendered:expr) => {{