From 4315773676527c94646315ac2a3475fd6eb2925a Mon Sep 17 00:00:00 2001
From: René Kijewski <rene.kijewski@fu-berlin.de>
Date: Tue, 1 Aug 2023 11:30:06 +0200
Subject: parser: add type for `Node::Comment`

---
 askama_derive/src/generator.rs |  10 +--
 askama_parser/src/node.rs      | 100 ++++++++++++++------------
 askama_parser/src/tests.rs     | 155 ++++++++++++++---------------------------
 3 files changed, 114 insertions(+), 151 deletions(-)

diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs
index 6e04a1a..c736847 100644
--- a/askama_derive/src/generator.rs
+++ b/askama_derive/src/generator.rs
@@ -5,7 +5,7 @@ use crate::CompileError;
 
 use parser::expr::Expr;
 use parser::node::{
-    Call, CondTest, If, Include, Let, Lit, Loop, Match, Node, Target, Whitespace, Ws,
+    Call, Comment, CondTest, If, Include, Let, Lit, Loop, Match, Node, Target, Whitespace, Ws,
 };
 use parser::Parsed;
 use proc_macro::TokenStream;
@@ -633,8 +633,8 @@ impl<'a> Generator<'a> {
                 Node::Lit(ref lit) => {
                     self.visit_lit(lit);
                 }
-                Node::Comment(ws) => {
-                    self.write_comment(ws);
+                Node::Comment(ref comment) => {
+                    self.write_comment(comment);
                 }
                 Node::Expr(ws, ref val) => {
                     self.write_expr(ws, val);
@@ -1293,8 +1293,8 @@ impl<'a> Generator<'a> {
         }
     }
 
-    fn write_comment(&mut self, ws: Ws) {
-        self.handle_ws(ws);
+    fn write_comment(&mut self, comment: &'a Comment<'_>) {
+        self.handle_ws(comment.ws);
     }
 
     /* Visitor methods for expression types */
diff --git a/askama_parser/src/node.rs b/askama_parser/src/node.rs
index 0d7552f..a45cc89 100644
--- a/askama_parser/src/node.rs
+++ b/askama_parser/src/node.rs
@@ -17,7 +17,7 @@ use super::{
 #[derive(Debug, PartialEq)]
 pub enum Node<'a> {
     Lit(Lit<'a>),
-    Comment(Ws),
+    Comment(Comment<'a>),
     Expr(Ws, Expr<'a>),
     Call(Call<'a>),
     Let(Let<'a>),
@@ -38,7 +38,7 @@ impl<'a> Node<'a> {
     pub(super) fn many(i: &'a str, s: &State<'_>) -> IResult<&'a str, Vec<Self>> {
         many0(alt((
             map(complete(|i| Lit::parse(i, s)), Self::Lit),
-            complete(|i| Self::comment(i, s)),
+            map(complete(|i| Comment::parse(i, s)), Self::Comment),
             complete(|i| Self::expr(i, s)),
             complete(|i| Self::parse(i, s)),
         )))(i)
@@ -107,46 +107,6 @@ impl<'a> Node<'a> {
         let (i, (_, (pws, expr, nws, _))) = p(i)?;
         Ok((i, Self::Expr(Ws(pws, nws), expr)))
     }
-
-    fn comment(i: &'a str, s: &State<'_>) -> IResult<&'a str, Self> {
-        fn body<'a>(mut i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
-            let mut level = 0;
-            loop {
-                let (end, tail) = take_until(s.syntax.comment_end)(i)?;
-                match take_until::<_, _, Error<_>>(s.syntax.comment_start)(i) {
-                    Ok((start, _)) if start.as_ptr() < end.as_ptr() => {
-                        level += 1;
-                        i = &start[2..];
-                    }
-                    _ if level > 0 => {
-                        level -= 1;
-                        i = &end[2..];
-                    }
-                    _ => return Ok((end, tail)),
-                }
-            }
-        }
-
-        let mut p = tuple((
-            |i| s.tag_comment_start(i),
-            cut(tuple((
-                opt(Whitespace::parse),
-                |i| body(i, s),
-                |i| s.tag_comment_end(i),
-            ))),
-        ));
-        let (i, (_, (pws, tail, _))) = p(i)?;
-        let nws = if tail.ends_with('-') {
-            Some(Whitespace::Suppress)
-        } else if tail.ends_with('+') {
-            Some(Whitespace::Preserve)
-        } else if tail.ends_with('~') {
-            Some(Whitespace::Minimize)
-        } else {
-            None
-        };
-        Ok((i, Self::Comment(Ws(pws, nws))))
-    }
 }
 
 #[derive(Debug, PartialEq)]
@@ -604,7 +564,7 @@ impl<'a> Match<'a> {
                 opt(Whitespace::parse),
                 |i| s.tag_block_end(i),
                 cut(tuple((
-                    ws(many0(ws(value((), |i| Node::comment(i, s))))),
+                    ws(many0(ws(value((), |i| Comment::parse(i, s))))),
                     many1(|i| When::when(i, s)),
                     cut(tuple((
                         opt(|i| When::r#match(i, s)),
@@ -866,6 +826,60 @@ impl<'a> Extends<'a> {
     }
 }
 
+#[derive(Debug, PartialEq)]
+pub struct Comment<'a> {
+    pub ws: Ws,
+    pub content: &'a str,
+}
+
+impl<'a> Comment<'a> {
+    fn parse(i: &'a str, s: &State<'_>) -> IResult<&'a str, Self> {
+        fn body<'a>(mut i: &'a str, s: &State<'_>) -> IResult<&'a str, &'a str> {
+            let mut level = 0;
+            loop {
+                let (end, tail) = take_until(s.syntax.comment_end)(i)?;
+                match take_until::<_, _, Error<_>>(s.syntax.comment_start)(i) {
+                    Ok((start, _)) if start.as_ptr() < end.as_ptr() => {
+                        level += 1;
+                        i = &start[2..];
+                    }
+                    _ if level > 0 => {
+                        level -= 1;
+                        i = &end[2..];
+                    }
+                    _ => return Ok((end, tail)),
+                }
+            }
+        }
+
+        let mut p = tuple((
+            |i| s.tag_comment_start(i),
+            cut(tuple((
+                opt(Whitespace::parse),
+                |i| body(i, s),
+                |i| s.tag_comment_end(i),
+            ))),
+        ));
+        let (i, (content, (pws, tail, _))) = p(i)?;
+        let nws = if tail.ends_with('-') {
+            Some(Whitespace::Suppress)
+        } else if tail.ends_with('+') {
+            Some(Whitespace::Preserve)
+        } else if tail.ends_with('~') {
+            Some(Whitespace::Minimize)
+        } else {
+            None
+        };
+        Ok((
+            i,
+            Self {
+                ws: Ws(pws, nws),
+                content,
+            },
+        ))
+    }
+}
+
 /// 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".
diff --git a/askama_parser/src/tests.rs b/askama_parser/src/tests.rs
index 9f0fbd1..dde127b 100644
--- a/askama_parser/src/tests.rs
+++ b/askama_parser/src/tests.rs
@@ -521,112 +521,61 @@ fn test_odd_calls() {
 
 #[test]
 fn test_parse_comments() {
-    let s = &Syntax::default();
+    fn one_comment_ws(source: &str, ws: Ws) {
+        let s = &Syntax::default();
+        let mut nodes = Ast::from_str(source, s).unwrap().nodes;
+        assert_eq!(nodes.len(), 1, "expected to parse one node");
+        match nodes.pop().unwrap() {
+            Node::Comment(comment) => assert_eq!(comment.ws, ws),
+            node => panic!("expected a comment not, but parsed {:?}", node),
+        }
+    }
 
-    assert_eq!(
-        Ast::from_str("{##}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(None, None))],
-    );
-    assert_eq!(
-        Ast::from_str("{#- #}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(Some(Whitespace::Suppress), None))],
-    );
-    assert_eq!(
-        Ast::from_str("{# -#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(None, Some(Whitespace::Suppress)))],
-    );
-    assert_eq!(
-        Ast::from_str("{#--#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Suppress),
-            Some(Whitespace::Suppress)
-        ))],
-    );
-    assert_eq!(
-        Ast::from_str("{#- foo\n bar -#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Suppress),
-            Some(Whitespace::Suppress)
-        ))],
-    );
-    assert_eq!(
-        Ast::from_str("{#- foo\n {#- bar\n -#} baz -#}", s)
-            .unwrap()
-            .nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Suppress),
-            Some(Whitespace::Suppress)
-        ))],
-    );
-    assert_eq!(
-        Ast::from_str("{#+ #}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(Some(Whitespace::Preserve), None))],
-    );
-    assert_eq!(
-        Ast::from_str("{# +#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(None, Some(Whitespace::Preserve)))],
-    );
-    assert_eq!(
-        Ast::from_str("{#++#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Preserve),
-            Some(Whitespace::Preserve)
-        ))],
-    );
-    assert_eq!(
-        Ast::from_str("{#+ foo\n bar +#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Preserve),
-            Some(Whitespace::Preserve)
-        ))],
-    );
-    assert_eq!(
-        Ast::from_str("{#+ foo\n {#+ bar\n +#} baz -+#}", s)
-            .unwrap()
-            .nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Preserve),
-            Some(Whitespace::Preserve)
-        ))],
-    );
-    assert_eq!(
-        Ast::from_str("{#~ #}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(Some(Whitespace::Minimize), None))],
-    );
-    assert_eq!(
-        Ast::from_str("{# ~#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(None, Some(Whitespace::Minimize)))],
-    );
-    assert_eq!(
-        Ast::from_str("{#~~#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Minimize),
-            Some(Whitespace::Minimize)
-        ))],
-    );
-    assert_eq!(
-        Ast::from_str("{#~ foo\n bar ~#}", s).unwrap().nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Minimize),
-            Some(Whitespace::Minimize)
-        ))],
-    );
-    assert_eq!(
-        Ast::from_str("{#~ foo\n {#~ bar\n ~#} baz -~#}", s)
-            .unwrap()
-            .nodes,
-        vec![Node::Comment(Ws(
-            Some(Whitespace::Minimize),
-            Some(Whitespace::Minimize)
-        ))],
+    one_comment_ws("{##}", Ws(None, None));
+    one_comment_ws("{#- #}", Ws(Some(Whitespace::Suppress), None));
+    one_comment_ws("{# -#}", Ws(None, Some(Whitespace::Suppress)));
+    one_comment_ws(
+        "{#--#}",
+        Ws(Some(Whitespace::Suppress), Some(Whitespace::Suppress)),
+    );
+    one_comment_ws(
+        "{#- foo\n bar -#}",
+        Ws(Some(Whitespace::Suppress), Some(Whitespace::Suppress)),
+    );
+    one_comment_ws(
+        "{#- foo\n {#- bar\n -#} baz -#}",
+        Ws(Some(Whitespace::Suppress), Some(Whitespace::Suppress)),
+    );
+    one_comment_ws("{#+ #}", Ws(Some(Whitespace::Preserve), None));
+    one_comment_ws("{# +#}", Ws(None, Some(Whitespace::Preserve)));
+    one_comment_ws(
+        "{#++#}",
+        Ws(Some(Whitespace::Preserve), Some(Whitespace::Preserve)),
+    );
+    one_comment_ws(
+        "{#+ foo\n bar +#}",
+        Ws(Some(Whitespace::Preserve), Some(Whitespace::Preserve)),
+    );
+    one_comment_ws(
+        "{#+ foo\n {#+ bar\n +#} baz -+#}",
+        Ws(Some(Whitespace::Preserve), Some(Whitespace::Preserve)),
+    );
+    one_comment_ws("{#~ #}", Ws(Some(Whitespace::Minimize), None));
+    one_comment_ws("{# ~#}", Ws(None, Some(Whitespace::Minimize)));
+    one_comment_ws(
+        "{#~~#}",
+        Ws(Some(Whitespace::Minimize), Some(Whitespace::Minimize)),
+    );
+    one_comment_ws(
+        "{#~ foo\n bar ~#}",
+        Ws(Some(Whitespace::Minimize), Some(Whitespace::Minimize)),
+    );
+    one_comment_ws(
+        "{#~ foo\n {#~ bar\n ~#} baz -~#}",
+        Ws(Some(Whitespace::Minimize), Some(Whitespace::Minimize)),
     );
 
-    assert_eq!(
-        Ast::from_str("{# foo {# bar #} {# {# baz #} qux #} #}", s)
-            .unwrap()
-            .nodes,
-        vec![Node::Comment(Ws(None, None))],
-    );
+    one_comment_ws("{# foo {# bar #} {# {# baz #} qux #} #}", Ws(None, None));
 }
 
 #[test]
-- 
cgit