feat(formatter): added option to add blank line before tags

closes #307
This commit is contained in:
Christopher Pickering 2022-07-28 09:53:33 -05:00
parent ee87ef7024
commit b2bd1ea298
No known key found for this signature in database
GPG key ID: E14DB3B0A0FACF84
14 changed files with 205 additions and 5 deletions

View file

@ -143,6 +143,24 @@
}
]
},
{
"name": "blank_line_before_tag",
"description": {
"en": "Add an additional blank line before `{% <tag> ... %}` tag groups. Blank lines will never be added to start of file or between similar tags.",
"ru": "Добавляет дополнительную пустую строку перед группами тегов `{% <tag> ... %}`. Пустые строки никогда не будут добавлены в начало файла или между похожими тегами.",
"fr": "Ajoute une ligne blanche supplémentaire avant les groupes de balises `{% <tag> ... %}`. Les lignes vides ne seront jamais ajoutées au début du fichier ou entre des balises similaires."
},
"usage": [
{
"name": "pyproject.toml",
"value": "blank_line_before_tag=\"load,extends,include\""
},
{
"name": ".djlintrc",
"value": "\"blank_line_before_tag\": \"load,extends,include\""
}
]
},
{
"name": "profile",
"description": {

View file

@ -70,24 +70,43 @@ def condense_html(html: str, config: Config) -> str:
)
return True
def if_blank_line_before_match(config: Config, html: str) -> bool:
"""Check if there should be a blank line before."""
if config.blank_line_before_tag:
return not any(
re.findall(
re.compile(
rf"((?:{{%\s*?{tag}[^}}]+?%}}\n?)+)",
re.IGNORECASE | re.MULTILINE | re.DOTALL,
),
html,
)
for tag in [x.strip() for x in config.blank_line_before_tag.split(",")]
)
return True
def condense_line(config: Config, match: re.Match) -> str:
"""Put contents on a single line if below max line length."""
if (
len(match.group(1) + match.group(3) + match.group(4))
< config.max_line_length
) and if_blank_line_after_match(config, match.group(3)):
(
len(match.group(1) + match.group(3) + match.group(4))
< config.max_line_length
)
and if_blank_line_after_match(config, match.group(3))
and if_blank_line_before_match(config, match.group(3))
):
return match.group(1) + match.group(3) + match.group(4)
return match.group()
def add_blank_line(config: Config, html: str, match: re.Match) -> str:
def add_blank_line_after(config: Config, html: str, match: re.Match) -> str:
"""Add break after if not in ignored block."""
if inside_ignored_block(config, html, match):
return match.group()
return match.group() + "\n"
func = partial(add_blank_line, config, html)
func = partial(add_blank_line_after, config, html)
# should we add blank lines after load tags?
if config.blank_line_after_tag:
@ -101,6 +120,27 @@ def condense_html(html: str, config: Config) -> str:
html,
)
def add_blank_line_before(config: Config, html: str, match: re.Match) -> str:
"""Add break before if not in ignored block and not first line in file."""
if inside_ignored_block(config, html, match) or match.start() == 0:
return match.group()
return "\n" + match.group()
func = partial(add_blank_line_before, config, html)
# should we add blank lines before load tags?
if config.blank_line_before_tag:
for tag in [x.strip() for x in config.blank_line_before_tag.split(",")]:
html = re.sub(
re.compile(
rf"(?<!^\n$)((?:{{%\s*?{tag}\b[^}}]+?%}}\n?)+)",
re.IGNORECASE | re.MULTILINE | re.DOTALL,
),
func,
html,
)
func = partial(condense_line, config)
# put short single line tags on one line

View file

@ -334,6 +334,11 @@ class Config:
"blank_line_after_tag", None
)
# add blank line before load tags
self.blank_line_before_tag: Optional[str] = djlint_settings.get(
"blank_line_before_tag", None
)
# contents of tags will not be formatted
self.ignored_block_opening: str = r"""
<style

View file

@ -0,0 +1 @@
{% extends "nothing.html" %}{% load stuff %}{% load stuff 2 %}{% include "html_two.html" %}<div></div>

View file

@ -0,0 +1,3 @@
{% extends nothing %}
<div></div>

View file

@ -0,0 +1,6 @@
<tr>
<td rowspan="{{ quality.row_span }}">
{% include "common/pdfs/partials/display_quality_improvements.html" %}
</td>
</tr>

View file

@ -0,0 +1,3 @@
{% block this %}
{% load i18n %}
{% endblock this %}

View file

@ -0,0 +1,6 @@
{% blocktrans %}my words{% endblocktrans %}
{% block body %}
<div></div>
{% endblock body %}
{% block js %}{% endblock %}

View file

@ -0,0 +1,4 @@
{% block include %}
{# {% include 'common/sticky-topbar-hidden-nav.html' %}#}
{% endblock %}

View file

@ -0,0 +1,8 @@
<div class="tab-cnt">
<div class="tab-dta active" id="details">
<div class="em-grid">
{% include "pages/task/details_source.html.j2" %}
</div>
</div>
</div>

View file

@ -0,0 +1,2 @@
{% load stuff %}
<div></div>

View file

@ -0,0 +1,3 @@
[tool]
[tool.djlint]
blank_line_before_tag = "load, extends, include ,endblock "

View file

@ -0,0 +1,101 @@
"""Djlint tests specific to pyproject.toml configuration.
run::
pytest tests/test_config/test_blank_lines_before_tag/test_config.py --cov=src/djlint --cov-branch \
--cov-report xml:coverage.xml --cov-report term-missing
for a single test, run::
pytest tests/test_config.py::test_custom_html --cov=src/djlint \
--cov-branch --cov-report xml:coverage.xml --cov-report term-missing
"""
# pylint: disable=C0116
from click.testing import CliRunner
from src.djlint import main as djlint
def test_blank_lines_before_tag(runner: CliRunner) -> None:
result = runner.invoke(
djlint, ["tests/test_config/test_blank_lines_before_tag/html.html", "--check"]
)
assert (
"""+{% extends "nothing.html" %}
+
+{% load stuff %}
+{% load stuff 2 %}
+
+{% include "html_two.html" %}
+<div></div>"""
in result.output
)
assert """1 file would be updated.""" in result.output
assert result.exit_code == 1
result = runner.invoke(
djlint,
["tests/test_config/test_blank_lines_before_tag/html_two.html", "--check"],
)
assert result.exit_code == 0
# check blocks that do not start on a newline - they should be left as is.
result = runner.invoke(
djlint,
["tests/test_config/test_blank_lines_before_tag/html_three.html", "--check"],
)
assert """0 files would be updated.""" in result.output
assert result.exit_code == 0
result = runner.invoke(
djlint,
["tests/test_config/test_blank_lines_before_tag/html_four.html", "--check"],
)
assert result.exit_code == 1
assert (
""" {% block this %}
-{% load i18n %}
+
+ {% load i18n %}
+
{% endblock this %}
"""
in result.output
)
# something perfect should stay perfect :)
result = runner.invoke(
djlint,
["tests/test_config/test_blank_lines_before_tag/html_five.html", "--check"],
)
assert result.exit_code == 0
# something perfect should stay perfect :)
result = runner.invoke(
djlint,
["tests/test_config/test_blank_lines_before_tag/html_six.html", "--check"],
)
assert result.exit_code == 0
# make sure endblock doesn't pick up endblocktrans :)
result = runner.invoke(
djlint,
["tests/test_config/test_blank_lines_before_tag/html_seven.html", "--check"],
)
assert result.exit_code == 0
# check that multiple blank lines are not added
result = runner.invoke(
djlint,
[
"tests/test_config/test_blank_lines_before_tag/html_eight.html",
"--preserve-blank-lines",
"--check",
],
)
assert result.exit_code == 0