rtts / djhtml

Django/Jinja template indenter
GNU General Public License v3.0
572 stars 32 forks source link

Set assignment nested within set block messes up hierarchy #33

Closed markus-beuckelmann closed 3 years ago

markus-beuckelmann commented 3 years ago

When using a set assignment (think this is Jinja specific!) nested within a {% set %} block, the hierarchy doesn't quite work out and djhtml expects a closing {% endset %} statement for the assignment. For example this is what I get:

{% set foobar %}
  {% for item in sequence %}
    {{ item }}
    {% set iterated = true %}
    {% endfor %}
  {% endset %}

Set assignments alone seem to work fine though:

{% for item in sequence %}
  {{ item }}
  {% set iterated = true %}
{% endfor %}

Thanks for this great tool!

JaapJoris commented 3 years ago

Hi there, and thanks for reporting this bug! This issue is more or less the same as with the {% video %} tag of #32. When DjHTML sees a {% tagname %} identifier it decides whether or not to indent by looking ahead for a corresponding {% endtagname %} identifier.

In this case, it finds one, and so it assumes that both the {% set foobar %} and the {% set iterated = true %} tags should lead to indentation. Other than "blacklisting" specific tags such as {% set %} and {% video %}, I don't yet know how this problem could be solved.

Even worse, trying to escape the problematic tag using {# fmt:off #} and {# fmt:on #} will produce another error (the {% endfor %} tag is at the wrong indentation):

{% set foobar %}
  {% for item in sequence %}
    {{ item }}
    {# fmt:off #}
    {% set iterated = true %}
    {# fmt:on #}
{% endfor %}
{% endset %}

I will look into these problems and keep you updated!

JaapJoris commented 3 years ago

Update: I've released a new version that fixes the incorrect handling of {# fmt:off #} and {# fmt on #} tags, so the following now indents correctly:

{% set foobar %}
  {% for item in sequence %}
    {{ item }}
    {# fmt:off #}
    {% set iterated = true %}
    {# fmt:on #}
  {% endfor %}
{% endset %}

I anyone has an idea on how to make this example work without the {# fmt #} tags then I'd love to hear it!

sjoerdjob commented 3 years ago

One solution I could think of is adding a dict of regexes somewhere, and checking those:

NO_BLOCK = {
    "set": " = ",
    "video": " as ",
}

And in the "is it a block or not" check that dictionary in the case that the "end{foo}" is found. Of course: this reeks again of something that should be configurable, for when another library comes along that needs this.