diff options
-rw-r--r-- | askama_shared/src/generator.rs | 32 | ||||
-rw-r--r-- | askama_shared/src/lib.rs | 21 | ||||
-rw-r--r-- | askama_shared/src/parser.rs | 3 |
3 files changed, 49 insertions, 7 deletions
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index fc520d8..cae6389 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -60,8 +60,14 @@ fn build_template(ast: &syn::DeriveInput) -> Result<String, CompileError> { 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); } @@ -129,6 +135,8 @@ struct Generator<'a, S: std::hash::BuildHasher> { buf_writable: Vec<Writable<'a>>, // 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 +145,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 +157,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. @@ -1688,7 +1704,13 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { // 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.suppress_whitespace == ws.0 { let val = self.next_ws.unwrap(); if !val.is_empty() { self.buf_writable.push(Writable::Lit(val)); @@ -1701,7 +1723,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.suppress_whitespace != ws.1; } } diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index 5913eb3..2aa7114 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -118,6 +118,7 @@ struct Config<'a> { syntaxes: BTreeMap<String, Syntax<'a>>, default_syntax: &'a str, escapers: Vec<(HashSet<String>, 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<Vec<&'a str>>, default_syntax: Option<&'a str>, + suppress_whitespace: Option<bool>, } #[cfg_attr(feature = "serde", derive(Deserialize))] @@ -646,4 +651,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 2a7f49f..eeca326 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -129,6 +129,9 @@ pub(crate) enum Target<'a> { Path(Vec<&'a str>), } +/// First field is "minus 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) bool, pub(crate) bool); |