rtts / djhtml

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

Avoid modifying indentation of multiline attributes #92

Closed adamchainz closed 1 year ago

adamchainz commented 1 year ago

I have an example from a project using Alpine.js. There's a bunch of inline JS within an x-data attribute, like:

<div x-data="{
  init() {
    doSomething();
  }
}">
  hi
</div>

Running djhtml 3.0.5 on the file flattens all the indentation:

<div x-data="{
             init() {
             doSomething();
             }
             }">
  hi
</div>

In this case, it makes the JavaScript much less readable, but doesn't affect its meaning, because JS doesn't care about whitespace. But if the attr contained some Python code for e.g. pyscript, it would be rendered unparseable.

In general you cannot change the whitespace within an attribute. You can for class and style, and maybe some others, so perhaps an "allow list" approach could be added to retain the current behaviour for common attributes, but avoid touching indentation at all for others?

Btw djhtml 2.0.0 does this to the file as well, just without the alignment to the quote, as you might expect:

<div x-data="{
  init() {
  doSomething();
  }
  }">
  hi
</div>
JaapJoris commented 1 year ago

Currently, DjHTML does not preserve pre-existing indentation. It first removes all the existing indentation, then reindents the file from scratch. In other words, the final indentation is not dependent on the existing indentation.

It also indents every line, even the contents of attribute values, as in this example:

<a href="/"
   class="button
          {% if active %}
            active
          {% endif %}
         ">Home</a>

Since indenting files consistently, independent of any pre-existing indentation, is DjHTML's main use case, I'm not willing to change this behavior. I'm sorry.

However, there is one exception, an "escape hatch" if you will. The following will preserve existing indentation:

<div x-data="{# fmt:off #}
             {
               init() {
                 doSomething();
               }
             }
             {# fmt:on #}">
  hi
</div>

You can play around with where you place the {# fmt #} tags, the following would also work:

<div x-data="{
  {# fmt:off #}
  init() {
    doSomething();
  }
  {# fmt:on #}
             }">
  hi
</div>

But note that the final }"> will still be indented in that case.

adamchainz commented 1 year ago

Okay, I guess that's fair. In most cases the indentation probably doesn't affect meaning, and in those it does, at least a developer should still be consciously applying the change.