Skip to content

Fix "Standalone block" test. - #206

Closed
mageowl wants to merge 2 commits into
mustache:masterfrom
mageowl:master
Closed

mageowl wants to merge 2 commits into
mustache:masterfrom
mageowl:master

Conversation

@mageowl

@mageowl mageowl commented May 14, 2026

Copy link
Copy Markdown

I'm currently reimplementing Mustache in Rust because the old crate is unmaintained. After some thorough testing, I believe that this test is contradictory to the expected result.

Currently, it expects the following output:

Hi,
  one
  two

Note the fact that the last line has a trailing newline.

However, the template for this test is as follows:

{{<parent}}{{$block}}
one
two{{/block}}
{{/parent}}

Note the fact that there is no newline in between the "two" and the closing tag.

And the parent looks like this:

Hi,
  {{$block}}{{/block}}

Because the block is a standalone tag, the specification says that the newline should be removed after it.

Therefore, I believe that the test should actually expect this, without a trailing newline:

Hi,
  one
  two⏎

@jgonggrijp

Copy link
Copy Markdown
Member

Compare it to the previous test, "standalone parent". Do you agree with its expected output? If so, why do you disagree with the expected output of "standalone block"?

@mageowl

mageowl commented May 14, 2026

Copy link
Copy Markdown
Author

I agree with the current implementation of "standalone parent".

The root template is as follows:

Hi,
  {{<parent}}{{/parent}}

And the parent is as such:

one
two

It makes sense that there would be a trailing newline because there is a trailing newline in the parent template. However, in "standalone block", the parent has no trailing newline because it ends with a standalone block.

@jgonggrijp

Copy link
Copy Markdown
Member

How about "inherit indentation" on lines 154-164? Do you agree with its expectation?

@mageowl

mageowl commented May 14, 2026

Copy link
Copy Markdown
Author

Yes. How I interpreted the specification, a tag is only standalone if it is one of the standalone tag types (partial, section, set delim, etc.) and is on a line with only other standalone tags. In this test, there is also content on the same line (inside the block), so it's not standalone.

@jgonggrijp

jgonggrijp commented May 15, 2026

Copy link
Copy Markdown
Member

Ah, that explains your confusion. Consider this test:

spec/specs/sections.yml

Lines 273 to 277 in e8ec001

- name: Indented Inline Sections
desc: Single-line sections should not alter surrounding whitespace.
data: { boolean: true }
template: " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n"
expected: " YES\n GOOD\n"

Your interpretation would explain why this section is not standalone, but the historical interpretation has been that this would even not be standalone if the content between the tags wasn't there. In other words, a section, inverted section, end section, change delimiters, comment or partial tag would not be standalone anymore as soon as there was another section, inverted section, end section, change delimiters, comment or partial tag on the same line.

People have agreed with you before that this was too restrictive. In fact, some would like to go a bit further than your interpretation and suggest that the section in the above test should be standalone because it clears on the outside. If the boolean was false, it would make sense that the section didn't leave an empty line, after all.

However, when the "inherit indentation" test was added, the prevailing interpretation was that anything other than whitespace on the same line would stop a tag from being standalone, even if the only other content was another tag that by itself would be standalone, too. In that light, it was also a bit surprising that the nineties block would still affect indentation despite not being standalone. That interpretation contrasts with partials, which only affect indentation when standalone. It's as if the block as a whole is standalone and not standalone at the same time.

This is the historical context I had to work with when I added the standalone parent and standalone block tests in #131. The intention was to promote parents and blocks all the way to the natural interpretation where only outside clearance would determine standaloneness, like with the impossible alternative interpretation of the indented inline sections test. For parents, it was still possible to do this while staying consistent with the existing tests, but the inherit indentation test prevented the same for blocks. This is why you see an apparent discrepancy between standalone blocks and standalone parents. See #130 for a longer discussion of the actual rules that these tests are based on.

That being said, I tried to find another test to prove your interpretation wrong and couldn't. You found a second, simpler set of rules that was also consistent with the spec (except for standalone blocks). Would you like to add a second test, perhaps in the sections spec, that would provide an earlier hint for people like yourself that your interpretation was too simple?

@mageowl

mageowl commented May 15, 2026

Copy link
Copy Markdown
Author

Thanks for the clarification. I understand how this is wrong now -- the behavior of a block tag shouldn't depend on whether or not there is default content inside it. I think the best test to prevent this would be something like this:

{{$parent}}{{$block}}
these tags *are* standalone
{{/block}}{{/parent}}
# parent
{{$block}}these tags are not standalone{{/block}}
{{$block}}{{/block}}

Ultimately, I think it would help people understand two things:

  1. block tags have different rules inside and outside parent tags
  2. the parameters for a parent never end with a newline (either the tag is on the same line as the content: no newline; or the block is standalone: no newline.)

I'll open a new pull request once I get the problem fixed with my library.

@mageowl mageowl closed this May 15, 2026
@jgonggrijp

Copy link
Copy Markdown
Member

Thanks for the clarification. I understand how this is wrong now -- the behavior of a block tag shouldn't depend on whether or not there is default content inside it.

This interpretation is still not quite right. There is a (admittedly counter-intuitive and confusing!) distinction between standalone tags and standalone pairs. For blocks specifically, the former concept determines whether surrounding whitespace is part of the tag or of the content, while the latter concept determines how indentation is handled. The rules are simpler for all the other tags.

Going over the "standalone blocks" test, reviewing the template template first:

{{<parent}}{{$block}}
one
two{{/block}}
{{/parent}}
  • The {{<parent}} tag is standalone, because it clears on the left and its right clearance is irrelevant.
  • The {{$block}} tag is standalone, because its left clearance is irrelevant and it clears on the right. For this reason, the line end after it is part of the tag rather than of the content. Also, intrinsic indentation is removed from each line of the content between it and its matching closing tag (though the intrinsic indentation is the empty string in this case).
  • The {{/block}} tag is standalone, because it has what may be regarded as the end-of-file as its left clearance and its right clearance is irrelevant. Closing block tags within parent tag pairs are always standalone for this reason, as you correctly observed. However, for these same reasons, it never removes newlines; more on this below.
  • The {{$block}}...{{/block}} pair cannot be standalone or not standalone, because its outer clearance is irrelevant.
  • The {{/parent}} tag is standalone, because its left clearance is irrelevant and it clears on the right.
  • The {{<parent}}...{{/parent}} pair is standalone, because both tags are standalone. For this reason, the pair as a whole behaves like a standalone partial tag, with regard to both surrounding whitespace and indentation.

As for the parent template:

Hi,
  {{$block}}{{/block}}
  • The {{$block}} tag is NOT standalone, because it does not clear on the right. For this reason, its surrounding whitespace is part of the literal template contents rather than of the tag.
  • The {{/block}} tag is NOT standalone, because it does not clear on the left. This is why the trailing line end is retained in the output.
  • The {{$block}}{{/block}} pair, however, is standalone (!), because its opening tag clears on the left and its closing tag clears on the right. This is why the preceding indentation is applied to each line of the argument block in the template template.

I think the best test to prevent this would be something like this:

{{$parent}}{{$block}}
these tags *are* standalone
{{/block}}{{/parent}}
# parent
{{$block}}these tags are not standalone{{/block}}
{{$block}}{{/block}}

A nuance on the latter template: as I explained above, the tags are not standalone, but the pairs are.

Other than that, I cannot really comment on this idea without seeing it in a more complete context with explicit expectations. You might need to add surrounding content in order to more easily demonstrate that lines are/aren't disappearing in the output.

Ultimately, I think it would help people understand two things:

  1. block tags have different rules inside and outside parent tags

Correct.

  1. the parameters for a parent never end with a newline (either the tag is on the same line as the content: no newline; or the block is standalone: no newline.)

Incorrect. A standalone tag absorbs the preceding whitespace, up to but not including a preceding newline. Hence, a block simply ends with a newline if there is a newline before its closing tag. This is true regardless of whether it is an argument block or a parameter block.

@jgonggrijp

Copy link
Copy Markdown
Member

Ah, almost forgot to mention: an easy way to test theories about whitespace handling is to pass examples through the playground. The playground uses an implementation that passes all the specs and that was written by the same author as the inheritance indentation tests (me). Which is not to say that it is flawless, but it tends to be very accurate.

If it gives you surprising results, always ask for help, either in the spec Q&A or in the playground issue tracker (or Stack Overflow, I watch the mustache tag over there). It either reveals something you weren't aware of or something that is wrong with the playground; either case should be addressed.

@mageowl

mageowl commented May 16, 2026

Copy link
Copy Markdown
Author

Hey, thanks for explaining all of this! I really appreciate the support. I'm not sure if I totally understand it all, but I haven't been able to find any case where my implementation does something differently than the playground. I'm totally okay with writing the tests outlined before, but I'm worried I would get something wrong. Do you want to write them instead?

@jgonggrijp

Copy link
Copy Markdown
Member

No, you go ahead and write them. I and others can guide you through it. The worst that could happen is that we end up not merging it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants