From 4c7825d30979cb1bd922f75d1980b0e8773f155c Mon Sep 17 00:00:00 2001 From: Thibaut Decombe Date: Sun, 23 Aug 2026 17:19:21 +0200 Subject: [PATCH] Fix raw slice of a nested Jinja block missing its opening tag A nested block reached from a parent block's children loop had its opening tag consumed before the start offset was recorded, so `Node::raw` began after it. Raw content printing ({% raw %} blocks, ignore directives) emits `raw` verbatim, which dropped the opening tag and made formatting unstable. --- markup_fmt/src/parser.rs | 24 +++++++++---------- .../fmt/jinja/control-structure/fixture.jinja | 6 +++++ .../fmt/jinja/control-structure/fixture.snap | 6 +++++ .../tests/fmt/jinja/ignore/nested-block.jinja | 6 +++++ .../tests/fmt/jinja/ignore/nested-block.snap | 9 +++++++ 5 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 markup_fmt/tests/fmt/jinja/ignore/nested-block.jinja create mode 100644 markup_fmt/tests/fmt/jinja/ignore/nested-block.snap diff --git a/markup_fmt/src/parser.rs b/markup_fmt/src/parser.rs index ebca70b3..54af5725 100644 --- a/markup_fmt/src/parser.rs +++ b/markup_fmt/src/parser.rs @@ -1396,6 +1396,7 @@ impl<'s> Parser<'s> { body.push(JinjaTagOrChildren::Children(children)); } } + let next_tag_start = self.peek_pos(); if let Ok(next_tag) = self.parse_jinja_tag() { let next_tag_name = parse_jinja_tag_name(&next_tag); if next_tag_name @@ -1409,20 +1410,17 @@ impl<'s> Parser<'s> { && matches!(next_tag_name, "elif" | "elseif" | "else") { body.push(JinjaTagOrChildren::Tag(next_tag)); - } else if let Some(JinjaTagOrChildren::Children(nodes)) = body.last_mut() { - nodes.push( - self.with_taken(|parser| { - parser.parse_jinja_tag_or_block(Some(next_tag), children_parser) - }) - .map(|(kind, raw)| T::build(kind, raw))?, - ); } else { - body.push(JinjaTagOrChildren::Children(vec![ - self.with_taken(|parser| { - parser.parse_jinja_tag_or_block(Some(next_tag), children_parser) - }) - .map(|(kind, raw)| T::build(kind, raw))?, - ])); + let kind = + self.parse_jinja_tag_or_block(Some(next_tag), children_parser)?; + let node = T::build(kind, unsafe { + self.source.get_unchecked(next_tag_start..self.peek_pos()) + }); + if let Some(JinjaTagOrChildren::Children(nodes)) = body.last_mut() { + nodes.push(node); + } else { + body.push(JinjaTagOrChildren::Children(vec![node])); + } } } else { break; diff --git a/markup_fmt/tests/fmt/jinja/control-structure/fixture.jinja b/markup_fmt/tests/fmt/jinja/control-structure/fixture.jinja index b5cbd7bf..ff61a3f6 100644 --- a/markup_fmt/tests/fmt/jinja/control-structure/fixture.jinja +++ b/markup_fmt/tests/fmt/jinja/control-structure/fixture.jinja @@ -90,3 +90,9 @@ There are {{ count }} {{ name }} objects.
Realname
{% endcall %} + +{% if outer %} + {% raw %} +

keep

+ {% endraw %} +{% endif %} diff --git a/markup_fmt/tests/fmt/jinja/control-structure/fixture.snap b/markup_fmt/tests/fmt/jinja/control-structure/fixture.snap index cac169f4..a3a38e40 100644 --- a/markup_fmt/tests/fmt/jinja/control-structure/fixture.snap +++ b/markup_fmt/tests/fmt/jinja/control-structure/fixture.snap @@ -98,3 +98,9 @@ source: markup_fmt/tests/fmt.rs
Realname
{% endcall %} + +{% if outer %} + {% raw %} +

keep

+ {% endraw %} +{% endif %} diff --git a/markup_fmt/tests/fmt/jinja/ignore/nested-block.jinja b/markup_fmt/tests/fmt/jinja/ignore/nested-block.jinja new file mode 100644 index 00000000..c14c33d9 --- /dev/null +++ b/markup_fmt/tests/fmt/jinja/ignore/nested-block.jinja @@ -0,0 +1,6 @@ +{% if outer %} + + {% if inner %} +

keep

+ {% endif %} +{% endif %} diff --git a/markup_fmt/tests/fmt/jinja/ignore/nested-block.snap b/markup_fmt/tests/fmt/jinja/ignore/nested-block.snap new file mode 100644 index 00000000..85c26453 --- /dev/null +++ b/markup_fmt/tests/fmt/jinja/ignore/nested-block.snap @@ -0,0 +1,9 @@ +--- +source: markup_fmt/tests/fmt.rs +--- +{% if outer %} + + {% if inner %} +

keep

+ {% endif %} +{% endif %}