Repository: djc/askama
Pull request #546 (2021-10-12 17:32:05)

Allow whitespace trimming in {{raw}} blocks 
This PR is based on #545 and #541.

Kijewski: Huh, do you know why the CI isn't checking the PR?
djc: Probably just a glitch, try pushing it again?

Patch:
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index e121be5..cb395d4 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -457,9 +457,9 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
                     self.flush_ws(m.ws1);
                     self.prepare_ws(m.ws2);
                 }
-                Node::Raw(ws1, contents, ws2) => {
+                Node::Raw(ws1, lws, val, rws, ws2) => {
                     self.handle_ws(ws1);
-                    self.buf_writable.push(Writable::Lit(contents));
+                    self.visit_lit(lws, val, rws);
                     self.handle_ws(ws2);
                 }
                 Node::Import(ws, _, _) => {
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs
index 44902d0..535c7f2 100644
--- a/askama_shared/src/parser.rs
+++ b/askama_shared/src/parser.rs
@@ -4,7 +4,7 @@ use std::str;
 use nom::branch::alt;
 use nom::bytes::complete::{escaped, is_not, tag, take_till, take_until};
 use nom::character::complete::{anychar, char, digit1};
-use nom::combinator::{complete, cut, eof, map, not, opt, recognize, value};
+use nom::combinator::{complete, consumed, cut, eof, map, not, opt, peek, recognize, value};
 use nom::error::{Error, ErrorKind};
 use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1};
 use nom::sequence::{delimited, pair, preceded, terminated, tuple};
@@ -28,7 +28,7 @@ pub enum Node<'a> {
     Include(Ws, &'a str),
     Import(Ws, &'a str, &'a str),
     Macro(&'a str, Macro<'a>),
-    Raw(Ws, &'a str, Ws),
+    Raw(Ws, &'a str, &'a str, &'a str, Ws),
     Break(Ws),
     Continue(Ws),
 }
@@ -1048,29 +1048,32 @@ 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('-')),
+        ws(tag("endraw")),
+        opt(char('-')),
+        peek(|i| tag_block_end(i, s)),
+    ));
+
     let mut p = tuple((
         opt(char('-')),
         ws(tag("raw")),
         cut(tuple((
             opt(char('-')),
             |i| tag_block_end(i, s),
-            take_until("{% endraw %}"),
-            |i| tag_block_start(i, s),
-            opt(char('-')),
-            ws(tag("endraw")),
-            opt(char('-')),
+            consumed(skip_till(endraw)),
         ))),
     ));
 
-    let (i, (pws1, _, (nws1, _, contents, _, pws2, _, nws2))) = p(i)?;
-    Ok((
-        i,
-        Node::Raw(
-            Ws(pws1.is_some(), nws1.is_some()),
-            contents,
-            Ws(pws2.is_some(), nws2.is_some()),
-        ),
-    ))
+    let (_, (pws1, _, (nws1, _, (contents, (i, (_, pws2, _, nws2, _)))))) = p(i)?;
+    let (lws, val, rws) = match split_ws_parts(contents) {
+        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());
+    Ok((i, Node::Raw(ws1, lws, val, rws, ws2)))
 }
 
 fn break_statement<'a>(i: &'a str, s: &State<'_>) -> IResult<&'a str, Node<'a>> {


Test patch:
diff --git a/testing/templates/raw-ws.html b/testing/templates/raw-ws.html
new file mode 100644
index 0000000..d590be7
--- /dev/null
+++ b/testing/templates/raw-ws.html
@@ -0,0 +1,2 @@
+<{% raw -%} {{hello}} {%- endraw %}>
+< {%- raw %}{{bye}}{% endraw -%} >
diff --git a/testing/tests/simple.rs b/testing/tests/simple.rs
index c712900..e66485a 100644
--- a/testing/tests/simple.rs
+++ b/testing/tests/simple.rs
@@ -433,6 +433,16 @@ fn test_raw_complex() {
     );
 }
 
+#[derive(Template)]
+#[template(path = "raw-ws.html")]
+struct RawTemplateWs;
+
+#[test]
+fn test_raw_ws() {
+    let template = RawTemplateWs;
+    assert_eq!(template.render().unwrap(), "<{{hello}}>\n<{{bye}}>");
+}
+
 mod without_import_on_derive {
     #[derive(askama::Template)]
     #[template(source = "foo", ext = "txt")]