Skip to content

Commit

Permalink
feat(formatter): added max-blank-lines flag to consolidate blank lines
Browse files Browse the repository at this point in the history
closes #675
  • Loading branch information
christopherpickering committed Jun 12, 2023
1 parent 19425f9 commit 4a7897f
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 7 deletions.
23 changes: 23 additions & 0 deletions docs/src/_data/configuration.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,27 @@
[
{
"name": "max_blank_lines",
"description": {
"en": "Consolidate blank lines down to x lines. Default is 0 meaning blank lines will be removed.",
"ru": "Объедините пустые строки до х строк. По умолчанию 0, что означает, что пустые строки будут удалены.",
"fr": "Consolider les lignes vierges en les ramenant à x lignes. La valeur par défaut est 0, ce qui signifie que les lignes vierges seront supprimées."
},
"usage": [
{
"name": "pyproject.toml",
"value": "max_blank_lines=5"
},
{
"name": ".djlintrc",
"value": "\"max_blank_lines\": 5"
},
{
"name": "cli",
"value": "--max_blank_lines 5"
}
],
"tags": ["formatter"]
},
{
"name": "no_set_formatting",
"description": {
Expand Down
1 change: 1 addition & 0 deletions docs/src/_includes/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Options:
matter.
--no-function-formatting Do not attempt to format function contents.
--no-set-formatting Do not attempt to format set contents.
--max-blank-lines INTEGER Consolidate blank lines down to x lines. [default: 0]
-h, --help Show this message and exit.
```
Expand Down
2 changes: 1 addition & 1 deletion docs/src/docs/linter.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ This can also be done through the [{{ "configuration" | i18n }}]({{ "lang_code_u
| H033 | Extra whitespace found in form action. | ✔️ |
| J004 | (Jinja) Static urls should follow {% raw %}`{{ url_for('static'..) }}`{% endraw %} pattern. | ✔️ |
| J018 | (Jinja) Internal links should use the {% raw %}`{% url ... %}`{% endraw %} pattern. | ✔️ |
| T001 | Variables should be wrapped in a single whitespace. Ex: {% raw %}`{{ this }}`{% endraw %} | ✔️ |
| T001 | Variables should be wrapped in whitespace. Ex: {% raw %}`{{ this }}`{% endraw %} | ✔️ |
| T002 | Double quotes should be used in tags. Ex {% raw %}`{% extends "this.html" %}`{% endraw %} | ✔️ |
| T003 | Endblock should have name. Ex: {% raw %}`{% endblock body %}`{% endraw %}. | ✔️ |
| T027 | Unclosed string found in template syntax. | ✔️ |
Expand Down
2 changes: 1 addition & 1 deletion docs/src/fr/docs/linter.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Cela peut également se faire par l'intermédiaire de l'option [{{ "configuratio
| H033 | Espace supplémentaire dans l'action du formulaire. | ✔️ |
| J004 | (Jinja) Les urls statiques doivent suivre le modèle {% raw %}`{ url_for('static'..) }}`{% endraw %}. | ✔️ |
| J018 | (Jinja) Les liens internes doivent utiliser le modèle {% raw %}`{% url ... %}`{% endraw %}. | ✔️ |
| T001 | Les variables doivent être entourées d'un seul espace. Ex : {% raw %}`{{ this }}`{% endraw %} | ✔️ |
| T001 | Les variables doivent être entourées d'un espace. Ex : {% raw %}`{{ this }}`{% endraw %} | ✔️ |
| T002 | Les doubles quotes doivent être utilisées dans les balises. Ex : {% raw %}`{% extends "this.html" %}`{% endraw %} | ✔️ |
| T003 | Le bloc de fin doit avoir un nom. Ex : {% raw %}`{% endblock body %}`{% endraw %}. | ✔️ |
| T027 | Chaîne non fermée trouvée dans la syntaxe du modèle. | ✔️ |
Expand Down
2 changes: 1 addition & 1 deletion docs/src/ru/docs/linter.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ djlint . --lint --include=H017,H035 --ignore=H013,H015
| H033 | В действии формы обнаружен лишний пробел. | ✔️ |
| J004 | (Jinja) Статические урлы должны следовать шаблону {% raw %}`{{ url_for('static'...)}}`{% endraw %}. | ✔️ |
| J018 | (Jinja) Внутренние ссылки должны использовать шаблон {% raw %}`{% url ... %}`{% endraw %}. | ✔️ |
| T001 | Переменные должны быть заключены в один пробел. Например: {% raw %}`{{ this }}`{% endraw %} | ✔️ |
| T001 | Переменные должны быть заключены в пробел. Например: {% raw %}`{{ this }}`{% endraw %} | ✔️ |
| T002 | В тегах следует использовать двойные кавычки. Ex {% raw %}`{% extends "this.html" %}`{% endraw %} | ✔️ |
| T003 | Конечный блок должен иметь имя. Например: {% raw %}`{% endblock body %}`{% endraw %}. | ✔️ |
| T027 | В синтаксисе шаблона найдена незакрытая строка. | ✔️ |
Expand Down
8 changes: 8 additions & 0 deletions src/djlint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,12 @@
is_flag=True,
help="Do not attempt to format set contents.",
)
@click.option(
"--max-blank-lines",
type=int,
help="Consolidate blank lines down to x lines. [default: 0]",
show_default=False,
)
@colorama_text(autoreset=True)
def main(
src: List[str],
Expand Down Expand Up @@ -288,6 +294,7 @@ def main(
no_line_after_yaml: bool,
no_function_formatting: bool,
no_set_formatting: bool,
max_blank_lines: Optional[int],
) -> None:
"""djLint · HTML template linter and formatter."""
config = Config(
Expand Down Expand Up @@ -330,6 +337,7 @@ def main(
no_line_after_yaml=no_line_after_yaml,
no_function_formatting=no_function_formatting,
no_set_formatting=no_set_formatting,
max_blank_lines=max_blank_lines,
)

temp_file = None
Expand Down
17 changes: 13 additions & 4 deletions src/djlint/formatter/condense.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ def strip_space(config: Config, html: str, match: re.Match) -> str:
if inside_protected_trans_block(config, html[: match.end()], match):
return match.group().rstrip()

return match.group(1)
lines = len(
re.findall(
r"\n",
match.group(2),
)
)
blank_lines = "\n" * lines
if lines > config.max_blank_lines:
blank_lines = "\n" * max(config.max_blank_lines, 0)
return match.group(1) + blank_lines

func = partial(strip_space, config, html)

Expand All @@ -50,7 +59,7 @@ def strip_space(config: Config, html: str, match: re.Match) -> str:
if not config.preserve_leading_space:
# remove any leading/trailing space
html = re.sub(
re.compile(rf"^[ \t]*{line_contents}[{trailing_contents}]*$", re.M),
re.compile(rf"^[ \t]*{line_contents}([{trailing_contents}]*)$", re.M),
func,
html,
)
Expand All @@ -59,12 +68,12 @@ def strip_space(config: Config, html: str, match: re.Match) -> str:
# only remove leading space in front of tags
# <, {%
html = re.sub(
re.compile(rf"^[ \t]*((?:<|{{%).*?)[{trailing_contents}]*$", re.M),
re.compile(rf"^[ \t]*((?:<|{{%).*?)([{trailing_contents}]*)$", re.M),
func,
html,
)
html = re.sub(
re.compile(rf"^{line_contents}[{trailing_contents}]*$", re.M), func, html
re.compile(rf"^{line_contents}([{trailing_contents}]*)$", re.M), func, html
)

def add_blank_line_after(config: Config, html: str, match: re.Match) -> str:
Expand Down
12 changes: 12 additions & 0 deletions src/djlint/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ def __init__(
no_line_after_yaml: bool = False,
no_function_formatting: bool = False,
no_set_formatting: bool = False,
max_blank_lines: Optional[int] = None,
):
self.reformat = reformat
self.check = check
Expand Down Expand Up @@ -405,6 +406,17 @@ def __init__(
self.indent_size = indent
self.indent: str = int(indent) * " "

try:
self.max_blank_lines = int(
djlint_settings.get("max_blank_lines", max_blank_lines or 0)
)
except ValueError:
echo(
Fore.RED
+ f"Error: Invalid pyproject.toml indent value {djlint_settings['max_blank_lines']}"
)
self.max_blank_lines = max_blank_lines or 0

default_exclude: str = r"""
\.venv
| venv/
Expand Down
55 changes: 55 additions & 0 deletions tests/test_config/test_max_blank_lines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Test for max blank lines.
poetry run pytest tests/test_config/test_max_blank_lines.py
"""
import pytest

from src.djlint.reformat import formatter
from tests.conftest import config_builder, printer

test_data = [
pytest.param(
("<img>\n\n\n\n\n\n\n\n\n\n<meta>"),
("<img>\n" "<meta>\n"),
({}),
id="default",
),
pytest.param(
("<img>\n\n\n\n\n\n\n\n\n\n<meta>"),
("<img>\n\n\n\n\n\n" "<meta>\n"),
({"max_blank_lines": 5}),
id="5",
),
pytest.param(
("<img>\n\n\n\n\n\n\n\n\n\n<meta>"),
("<img>\n\n" "<meta>\n"),
({"max_blank_lines": 1}),
id="1",
),
pytest.param(
("<img>\n\n\n\n\n\n\n\n\n\n<meta>"),
("<img>\n" "<meta>\n"),
({"max_blank_lines": -1}),
id="-1",
),
pytest.param(
("<img>\n\n\n\n\n\n\n\n\n\n<meta>"),
("<img>\n\n\n\n\n\n\n\n\n\n" "<meta>\n"),
({"max_blank_lines": 30}),
id="30",
),
pytest.param(
("<img>\n\n<div>\n\n<p>\n\n</p>\n\n</div>\n\n<meta>"),
("<img>\n" "\n" "<div>\n" "\n" " <p></p>\n" "\n" "</div>\n" "\n" "<meta>\n"),
({"max_blank_lines": 30}),
id="div",
),
]


@pytest.mark.parametrize(("source", "expected", "args"), test_data)
def test_base(source, expected, args):
output = formatter(config_builder(args), source)

printer(expected, source, output)
assert expected == output

0 comments on commit 4a7897f

Please sign in to comment.