diff --git a/crates/djls-ast/src/parser.rs b/crates/djls-ast/src/parser.rs index a9fbacc..40b2103 100644 --- a/crates/djls-ast/src/parser.rs +++ b/crates/djls-ast/src/parser.rs @@ -155,8 +155,18 @@ impl Parser { if tag == end_tag { self.consume()?; break; + } else if !tag.starts_with("end") { + // For intermediate tags (else, elif, empty, etc.) + self.consume()?; + // Create a new Tag node for the intermediate tag + children.push(Node::Django(DjangoNode::Tag { + kind: DjangoTagKind::from_str(&tag)?, + bits: vec![tag.clone()], + children: Vec::new(), + })); + } else { + return Err(ParserError::ErrorSignal(Signal::ClosingTagFound(tag))); } - // If it's not our end tag, keep collecting children } Err(e) => return Err(e), } @@ -453,16 +463,37 @@ impl Parser { const SYNC_TYPES: &[TokenType] = &[ TokenType::DjangoBlock(String::new()), TokenType::HtmlTagOpen(String::new()), + TokenType::HtmlTagClose(String::new()), // Added TokenType::HtmlTagVoid(String::new()), TokenType::ScriptTagOpen(String::new()), + TokenType::ScriptTagClose(String::new()), // Added TokenType::StyleTagOpen(String::new()), + TokenType::StyleTagClose(String::new()), // Added TokenType::Newline, TokenType::Eof, ]; + let mut nesting = 0; while !self.is_at_end() { - if SYNC_TYPES.contains(self.peek()?.token_type()) { - return Ok(()); + let token = self.peek()?; + match token.token_type() { + TokenType::HtmlTagOpen(_) + | TokenType::ScriptTagOpen(_) + | TokenType::StyleTagOpen(_) => { + nesting += 1; + } + TokenType::HtmlTagClose(_) + | TokenType::ScriptTagClose(_) + | TokenType::StyleTagClose(_) => { + nesting -= 1; + if nesting < 0 { + return Ok(()); + } + } + _ if SYNC_TYPES.contains(token.token_type()) && nesting == 0 => { + return Ok(()); + } + _ => {} } self.consume()?; } @@ -609,7 +640,7 @@ mod tests { } // hangs for some reason - // #[test] + #[test] fn test_parse_full() { let source = r#" diff --git a/crates/djls-ast/src/snapshots/djls_ast__parser__tests__parse_full.snap b/crates/djls-ast/src/snapshots/djls_ast__parser__tests__parse_full.snap new file mode 100644 index 0000000..c6202f5 --- /dev/null +++ b/crates/djls-ast/src/snapshots/djls_ast__parser__tests__parse_full.snap @@ -0,0 +1,106 @@ +--- +source: crates/djls-ast/src/parser.rs +expression: ast +--- +nodes: + - Html: + Doctype: "!DOCTYPE" + - Html: + Element: + tag_name: html + attributes: {} + children: + - Html: + Element: + tag_name: head + attributes: {} + children: + - Style: + Element: + attributes: + type: + Value: text/css + children: + - Style: + Comment: Style header + - Text: ".header " + - Text: "{" + - Text: "color: blue; }" + - Script: + Element: + attributes: + script: Boolean + type: + Value: text/javascript + children: + - Script: + Comment: + content: Init app + kind: SingleLine + - Text: "const app = " + - Text: "{" + - Script: + Comment: + content: Config + kind: MultiLine + - Text: "debug: true" + - Text: "};" + - Html: + Element: + tag_name: body + attributes: {} + children: + - Html: + Comment: Header section + - Html: + Element: + tag_name: div + attributes: + class: + Value: header + data-value: + Value: "123" + disabled: Boolean + id: + Value: main + children: + - Django: + Tag: + kind: If + bits: + - if + - user.is_authenticated + children: + - Django: + Comment: Welcome message + - Html: + Element: + tag_name: h1 + attributes: {} + children: + - Text: "Welcome, " + - Django: + Variable: + bits: + - user + - name + filters: + - name: default + arguments: + - Guest + - name: title + arguments: [] + - Text: "!" + - Django: + Tag: + kind: If + bits: + - if + - user.is_staff + children: + - Html: + Element: + tag_name: span + attributes: {} + children: + - Text: Admin