diff --git a/docs/djlint/best_practices.rst b/docs/djlint/best_practices.rst index 9d2e536..46b91b8 100644 --- a/docs/djlint/best_practices.rst +++ b/docs/djlint/best_practices.rst @@ -20,10 +20,10 @@ This pattern is not recommended:
content
^ space here -Spaceless Conditional Attributes --------------------------------- +``format_attribute_template_tags`` and Spaceless Conditional Attributes +----------------------------------------------------------------------- -Conditional attributes should use spaceless tags, for example ``{% if a -%}`` in nunjuck and jinja, to remove spaces inside the. +If ``format_attribute_template_tags`` option is enabled, conditional attributes should use spaceless tags, for example ``{% if a -%}`` in nunjuck and jinja, or ``{% spaceless %}{% if a %}{% endspaceless %}`` in django, to remove spaces inside the. djLint will format long attributes onto multiple lines, and the whitespace saved inside of attributes could break your code. diff --git a/docs/djlint/changelog.rst b/docs/djlint/changelog.rst index 054ceeb..f3ae32e 100644 --- a/docs/djlint/changelog.rst +++ b/docs/djlint/changelog.rst @@ -1,6 +1,10 @@ Changelog ========= +next release +------------ +- Added config option ``format_attribute_template_tags`` as opt-in for template tag formatting inside of attributes + 0.6.6 ----- - Big fixes diff --git a/docs/djlint/configuration.rst b/docs/djlint/configuration.rst index 3260725..518e1dc 100644 --- a/docs/djlint/configuration.rst +++ b/docs/djlint/configuration.rst @@ -160,3 +160,24 @@ Usage: .. code:: ini use_gitignore = True + +format_attribute_template_tags +------------------------------ + +Formatter will attempt to format template syntax inside of tag attributes. Disabled by default. + +Usage: + +.. code:: ini + + format_attribute_template_tags=true + +For example, with this option enabled, the following html will be acceptable: + +.. code:: html + + diff --git a/src/djlint/formatter/attributes.py b/src/djlint/formatter/attributes.py index 667bd71..9d754b7 100644 --- a/src/djlint/formatter/attributes.py +++ b/src/djlint/formatter/attributes.py @@ -124,7 +124,9 @@ def format_template_tags(config: Config, attributes: str) -> str: ) base_indent_space = base_indent * " " - indented += f"\n{leading_space}{base_indent_space}{tmp}" + + if tmp.strip() != "": + indented += f"\n{leading_space}{base_indent_space}{tmp}" end_text = re.findall(re.compile(r"[\"']$", re.M), line.strip()) @@ -260,7 +262,8 @@ def format_attributes(config: Config, match: re.match) -> str: attributes = f"{leading_space}{tag}{attributes}{close}" # format template tags - attributes = format_template_tags(config, attributes) + if config.format_attribute_template_tags: + attributes = format_template_tags(config, attributes) # format styles func = partial(format_style) diff --git a/src/djlint/settings.py b/src/djlint/settings.py index c7fba60..dcf680f 100644 --- a/src/djlint/settings.py +++ b/src/djlint/settings.py @@ -180,6 +180,10 @@ class Config: build_custom_blocks(djlint_settings.get("custom_blocks")) or "" ) + self.format_attribute_template_tags = djlint_settings.get( + "format_attribute_template_tags", False + ) + # ignore is based on input and also profile self.ignore: str = str(ignore or djlint_settings.get("ignore", "")) diff --git a/tests/config_format_attribute_template_tags/html-one.html b/tests/config_format_attribute_template_tags/html-one.html new file mode 100644 index 0000000..dcdc8f4 --- /dev/null +++ b/tests/config_format_attribute_template_tags/html-one.html @@ -0,0 +1,77 @@ + + +report image + + + + + +
+
+ + + + +{% block body %} +
+
+{% endblock body %} + +
diff --git a/tests/config_format_attribute_template_tags/pyproject.toml b/tests/config_format_attribute_template_tags/pyproject.toml new file mode 100644 index 0000000..4ee6ddb --- /dev/null +++ b/tests/config_format_attribute_template_tags/pyproject.toml @@ -0,0 +1,3 @@ +[tool] +[tool.djlint] +format_attribute_template_tags=true diff --git a/tests/conftest.py b/tests/conftest.py index 2fb8fbb..cdffb34 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ import os import tempfile from pathlib import Path +from types import SimpleNamespace from typing import Generator, TextIO import pytest @@ -32,7 +33,9 @@ def write_to_file(the_file: str, the_text: bytes) -> None: open_file.write(the_text) -def reformat(the_file: TextIO, runner: CliRunner, the_text: bytes) -> dict: +def reformat(the_file: TextIO, runner: CliRunner, the_text: bytes) -> SimpleNamespace: write_to_file(the_file.name, the_text) result = runner.invoke(djlint, [the_file.name, "--reformat"]) - return {"text": Path(the_file.name).read_text(), "exit_code": result.exit_code} + return SimpleNamespace( + **{"text": Path(the_file.name).read_text(), "exit_code": result.exit_code} + ) diff --git a/tests/test_config_format_attribute_template_tags.py b/tests/test_config_format_attribute_template_tags.py new file mode 100644 index 0000000..6cecd1a --- /dev/null +++ b/tests/test_config_format_attribute_template_tags.py @@ -0,0 +1,171 @@ +"""Djlint tests specific to pyproject.toml > format_attribute_template_tags configuration. + +run:: + + pytest tests/test_config_format_attribute_template_tags.py --cov=src/djlint --cov-branch \ + --cov-report xml:coverage.xml --cov-report term-missing + +for a single test, run:: + + pytest tests/test_config_format_attribute_template_tags.py::test_attribute_include --cov=src/djlint \ + --cov-branch --cov-report xml:coverage.xml --cov-report term-missing + +""" +# pylint: disable=C0116 + +from typing import TextIO + +from click.testing import CliRunner + +from src.djlint import main as djlint + +from .conftest import reformat + + +def test_with_config(runner: CliRunner) -> None: + result = runner.invoke( + djlint, ["tests/config_format_attribute_template_tags", "--check"] + ) + print(result.output) + assert """0 files would be updated.""" in result.output + assert result.exit_code == 0 + + +def test_without_config(runner: CliRunner, tmp_file: TextIO) -> None: + + output = reformat( + tmp_file, + runner, + b'\n', + ) + + assert output.exit_code == 0 + + output = reformat( + tmp_file, + runner, + b"""report image""", + ) + assert output.exit_code == 1 + + assert ( + output.text + == r"""report image +""" + ) + + output = reformat( + tmp_file, + runner, + b"""""", + ) + assert output.exit_code == 1 + + assert ( + output.text + == r""" + + + + +""" + ) + output = reformat( + tmp_file, + runner, + b"""
""", + ) + assert output.exit_code == 1 + + assert ( + output.text + == r"""
+""" + ) + output = reformat( + tmp_file, + runner, + b"""""", + ) + assert output.exit_code == 1 + + assert ( + output.text + == """ +""" + ) + + output = reformat( + tmp_file, + runner, + b"""""", + ) + assert output.exit_code == 1 + + assert ( + output.text + == """ +""" + ) + + output = reformat( + tmp_file, + runner, + b"""
+ +{% endblock body %} +""", + ) + assert output.exit_code == 0 + + +def test_attribute_for_loop(runner: CliRunner, tmp_file: TextIO) -> None: + output = reformat( + tmp_file, + runner, + b"""