DjHTML
A pure-Python Django/Jinja template indenter without dependencies.
DjHTML indents mixed HTML/CSS/JavaScript templates that contain Django or Jinja template tags. It works similar to other code-formatting tools such as Black and interoperates nicely with pre-commit.
DjHTML is an indenter and not a formatter: it will only add/remove whitespace at the beginning of lines. It will not insert newlines or other characters. The goal is to correctly indent already well-structured templates, not to fix broken ones.
New! Multi-line HTML elements
As of version 3, DjHTML indents multi-line HTML elements and multi-line attribute values like this:
<blockquote cite="Guido Van Rossum"
style="font-style: italic;
{% if dark_mode %}
background: black;
{% endif %}
">
Don't you hate code that's not properly indented?
</blockquote>
New! Multi-line CSS indentation
Multi-line CSS values are now continued at the same indentation level:
<style>
@font-face {
font-family: Helvetica;
src: {% for format, filename in licensed_fonts %}
url('{% static filename %}') format('{{ format }}'),
{% endfor %}
url('Arial.woff2') format('woff2'),
url('Arial.woff') format('woff');
}
</style>
New! Improved JavaScript indentation
Many new JavaScript indention rules have been added, such as the indentation of method chaining:
<script>
window.fetch('/test.html')
.then((html) => {
document.body.innerHTML = html;
{% block extra_statements %}
{% endblock %}
});
</script>
New! Tabwidth guessing
Without the -t
/ --tabwidth
argument, DjHTML no longer defaults to
a tabwidth of 4 but instead guesses the correct tabwidth.
Installation
DjHTML requires Python 3.8 or higher and is compatible with all operating systems supported by Python. Install DjHTML with the following command:
$ pip install djhtml
Note that
Windows still uses legacy code pages
instead of UTF-8. It is highly advised to set the environment variable
PYTHONUTF8
to 1
with the
setx
command:
C:\> setx /m PYTHONUTF8 1
Usage
After installation you can indent templates using the djhtml
command:
$ djhtml template.html
reindented template.html
1 template has been reindented.
You can also run djhtml .
to indent all HTML files beneath the
current directory.
An exit status of 0 means that everything went well, regardless of
whether any files were changed. When the option -c
/ --check
is
used, the exit status is 1 when one or more files would have changed,
but no changes are actually made. All available options are given by
djthml -h
/ djthml --help
.
fmt:off
and fmt:on
You can exclude specific lines from being processed with the
{# fmt:off #}
and {# fmt:on #}
operators:
{# fmt:off #}
,-._|\
/ .\
\_,--._/
{# fmt:on #}
Contents inside <pre> ... </pre>
, <!-- ... --->
, /* ... */
, and
{% comment %} ... {% endcomment %}
tags are also ignored (depending
on the current mode).
Modes
The indenter operates in one of three different modes:
-
DjHTML mode: the default mode. Invoked by using the
djhtml
command or the pre-commit hook. -
DjCSS mode. Will be entered when a
<style>
tag is encountered in DjHTML mode. It can also be invoked directly with the commanddjcss
. -
DjJS mode. Will be entered when a
<script>
tag is encountered in DjHTML mode. It can also be invoked directly with the commanddjjs
.
pre-commit configuration
A great way to use DjHTML is as a pre-commit hook, so all your HTML, CSS and JavaScript files will automatically be indented upon every commit.
First, install pre-commit:
$ pip install pre-commit
$ pre-commit install
Then, add the following to your .pre-commit-config.yaml
:
repos:
- repo: https://github.com/rtts/djhtml
rev: 'main' # replace with the latest tag on GitHub
hooks:
- id: djhtml
- id: djcss
- id: djjs
Now run pre-commit autoupdate
to automatically replace main
with
the latest tag on GitHub,
as recommended by pre-commit.
If you want to override a command-line option, for example to change
the default tabwidth, you change the entry
point of these hooks:
hooks:
- id: djhtml
# Use a tabwidth of 2 for HTML files
entry: djhtml --tabwidth 2
- id: djcss
- id: djjs
If you want to limit the files these hooks operate on, you can use pre-commit mechanisms for filtering. For example:
hooks:
- id: djhtml
# Indent only HTML files in template directories
files: .*/templates/.*\.html$
- id: djcss
# Run this hook only on SCSS files (CSS and SCSS is the default)
types: [scss]
- id: djjs
# Exclude JavaScript files in vendor directories
exclude: .*/vendor/.*
Now when you run git commit
you will see something like the
following output:
$ git commit
DjHTML...................................................................Failed
- hook id: djhtml
- files were modified by this hook
reindented template.html
1 template has been reindented.
To inspect the changes that were made, use git diff
. If you are
happy with the changes, you can commit them normally. If you are not
happy, please do the following:
-
Run
SKIP=djhtml git commit
to commit anyway, skipping thedjhtml
hook. -
Consider opening an issue with the relevant part of the input file that was incorrectly formatted, and an example of how it should have been formatted.
Your feedback for improving DjHTML is very welcome!
Development
First of all, clone this repository:
$ git clone https://github.com/rtts/djhtml
$ cd djhtml
Then, create a Python virtualenv and activate it:
$ python -m venv ~/.virtualenvs/djhtml
$ . ~/.virtualenvs/djhtml/bin/activate
Then, install the package in development
mode
including the dev
dependencies, and install the pre-commit hooks:
$ python -m pip install -e '.[dev]'
$ pre-commit install --install-hooks
You can run the unittests with:
$ python -m unittest
Or use nox
to test all supported Python
interpreters:
$ nox
Finally, to get a little insight into the tokenization step of the
indenting algorithm, you can run DjHTML with the -d
/ --debug
argument. You will see a Python representation of the tokens that are
created.