diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 02d63d3..18dbd97 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: [3.7, 3.8, 3.9, '3.10'] + python-version: [3.7, 3.8, 3.9, '3.10', 3.11-dev] fail-fast: true steps: @@ -45,7 +45,7 @@ jobs: # remove windows-latest, half tests seem to randomly pass matrix: os: [ubuntu-latest] - python-version: [3.7, 3.8, 3.9, '3.10'] + python-version: [3.7, 3.8, 3.9, '3.10', 3.11-dev] node: [ 12, 14, 16 ] fail-fast: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e19ae09..16a879c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,9 +43,3 @@ repos: args: [--autofix] - id: pretty-format-yaml args: [--autofix, --indent, '2'] -repos: - - repo: https://github.com/christopherpickering/pre-commit-hooks - rev: 0.0.6 - hooks: - - id: poetry-to-requirements - args: [--output=requirements.txt] \ No newline at end of file diff --git a/docs/src/docs/linter.md b/docs/src/docs/linter.md index 206098b..8a486fe 100644 --- a/docs/src/docs/linter.md +++ b/docs/src/docs/linter.md @@ -90,6 +90,7 @@ The first letter of a code follows the pattern: | T027 | Unclosed string found in template syntax. | | T028 | Consider using spaceless tags inside attribute values. {% raw %}`{%- if/for -%}`{% endraw %} | | T032 | Extra whitespace found in template tags. | +| T034 | Did you intend to use {% raw %}{% ... %} instead of {% ... }%? {% endraw %} | ### Adding Rules diff --git a/docs/src/fr/docs/linter.md b/docs/src/fr/docs/linter.md index 694ec8e..0cab56f 100644 --- a/docs/src/fr/docs/linter.md +++ b/docs/src/fr/docs/linter.md @@ -90,6 +90,7 @@ La première lettre d'un code suit le modèle : | T027 | Chaîne non fermée trouvée dans la syntaxe du modèle. | | T028 | Envisagez d'utiliser des balises sans espace à l'intérieur des valeurs d'attributs. {% raw %}`{%- if/for -%}`{% endraw %} | | T032 | Espace blanc supplémentaire trouvé dans les balises du modèle. | +| T034 | Aviez-vous l'intention d'utiliser {% raw %}{% ... %} au lieu de {% ... }% ? {% endraw %} | ### Ajout de règles diff --git a/docs/src/ru/docs/linter.md b/docs/src/ru/docs/linter.md index 12721f8..4582650 100644 --- a/docs/src/ru/docs/linter.md +++ b/docs/src/ru/docs/linter.md @@ -90,6 +90,7 @@ djlint /path/to/this.html.j2 --profile=jinja | T027 | В синтаксисе шаблона найдена незакрытая строка. | | T028 | Рассмотрите возможность использования тегов без пробелов внутри значений атрибутов. {% raw %}`{%- if/for -%}`{% endraw %} | | T032 | В тегах шаблона обнаружены лишние пробелы. | +| T034 | Вы намеревались использовать {% raw %}{% ... %} вместо {% ... }%? {% endraw %} | ### Добавление правил diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7fcc4dd..0000000 --- a/requirements.txt +++ /dev/null @@ -1,50 +0,0 @@ -astroid==2.12.9; python_full_version >= "3.7.2" -attrs==22.1.0; python_version >= "3.7" -black==22.8.0; python_full_version >= "3.6.2" -click==8.1.3; python_version >= "3.7" -colorama==0.4.5; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") -coverage==6.4.4; python_version >= "3.7" -cssbeautifier==1.14.6 -dill==0.3.5.1; python_full_version >= "3.7.2" -distlib==0.3.6; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -editorconfig==0.12.3 -execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -filelock==3.8.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -flake8==3.9.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -html-tag-names==0.1.2; python_version >= "3.7" and python_version < "4.0" -html-void-elements==0.1.0; python_version >= "3.7" and python_version < "4.0" -importlib-metadata==4.12.0; python_version >= "3.7" -iniconfig==1.1.1; python_version >= "3.7" -isort==5.10.1; python_full_version >= "3.6.1" and python_version < "4.0" -jsbeautifier==1.14.6 -lazy-object-proxy==1.7.1; python_version >= "3.6" and python_full_version >= "3.7.2" -mccabe==0.7.0; python_full_version >= "3.7.2" and python_version >= "3.7" -mypy-extensions==0.4.3; python_full_version >= "3.6.2" and python_version >= "3.6" -mypy==0.971; python_version >= "3.6" -packaging==21.3; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -pathspec==0.10.1; python_version >= "3.7" -pep8-naming==0.13.2; python_version >= "3.7" -platformdirs==2.5.2; python_version >= "3.7" and python_full_version >= "3.7.2" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7") -pluggy==1.0.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -py==1.11.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -pycodestyle==2.9.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -pyflakes==2.5.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -pylint==2.15.2; python_full_version >= "3.7.2" -pyparsing==3.0.9; python_full_version >= "3.6.8" and python_version >= "3.7" -pytest-cov==3.0.0; python_version >= "3.6" -pytest-forked==1.4.0; python_version >= "3.6" -pytest-xdist==2.5.0; python_version >= "3.6" -pytest==7.1.3; python_version >= "3.7" -pyyaml==6.0; python_version >= "3.6" -regex==2022.9.11; python_version >= "3.6" -six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" -tomli==2.0.1; python_full_version <= "3.11.0a6" and python_full_version >= "3.7.2" and python_version >= "3.7" and python_version < "3.11" and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0") or python_version < "3.11" -tomlkit==0.11.4; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.7.2" -tox==3.26.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") -tqdm==4.64.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") -typed-ast==1.5.4; python_version < "3.8" and implementation_name == "cpython" and python_full_version >= "3.7.2" and python_version >= "3.6" -types-pyyaml==6.0.11 -typing-extensions==4.3.0; python_version < "3.8" and python_version >= "3.7" and python_full_version >= "3.7.2" and (python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.7") -virtualenv==20.16.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" -wrapt==1.14.1 -zipp==3.8.1; python_version < "3.8" and python_version >= "3.7" and (python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.7") diff --git a/src/djlint/formatter/indent.py b/src/djlint/formatter/indent.py index de813d8..df2eaee 100644 --- a/src/djlint/formatter/indent.py +++ b/src/djlint/formatter/indent.py @@ -62,34 +62,24 @@ def indent_html(rawcode: str, config: Config) -> str: # if a one-line, inline tag, just process it, only if line starts w/ it elif ( - re.findall( - rf"(^<({slt_html})>)(.*?)([^<]*?$)", - item, - re.IGNORECASE | re.VERBOSE | re.MULTILINE, - ) - or re.findall( - re.compile( - rf"(<({slt_html})\b.+?>)(.*?)([^<]*?$)", + ( + re.findall( + rf"""^ # start of a line + (?: + (?:<({slt_html})>)(?:.*?)(?:[ \t]*?) # stuff >>>> match 1 + |(?:<({slt_html})\b[^>]+?>)(?:.*?)(?:[ \t]*?) # stuff >>> match 2 + |(?:<(?:{always_self_closing_html})\b[^>]*?/?>[ \t]*?) # + |(?:<(?:{slt_html})\b[^>]*?/>[ \t]*?) # + |(?:{{%[ ]*?({slt_template})[ ]+?.*?%}})(?:.*?)(?:{{%[ ]+?end(\3)[ ]+?.*?%}}[ \t]*?) # >>> match 3 + ) + +?[^<]*?$ # with no other tags following until end of line + """, + item, re.IGNORECASE | re.VERBOSE | re.MULTILINE, - ), - item, + ) ) - or re.findall( - rf"^({{%[ ]*?({slt_template})[ ]+?.*?%}})(.*?)({{%[ ]+?end(\2)[ ]+?.*?%}})", - item, - re.IGNORECASE | re.MULTILINE | re.VERBOSE, - ) - or re.findall( - rf"(<({slt_html})\b.*?/>)", item, flags=re.IGNORECASE | re.VERBOSE - ) - or re.findall( - re.compile( - rf"(<({always_self_closing_html})\b.*?/?>)", - re.IGNORECASE | re.VERBOSE, - ), - item, - ) - ) and is_block_raw is False: + and is_block_raw is False + ): tmp = (indent * indent_level) + item + "\n" # if unindent, move left @@ -108,7 +98,7 @@ def indent_html(rawcode: str, config: Config) -> str: re.IGNORECASE | re.VERBOSE | re.MULTILINE, ) and not re.findall( - rf"(<({slt_html})\\b.+?>)(.*?)([^<]*?$)", + rf"(<({slt_html})\\b[^>]+?>)(.*?)([^<]*?$)", item, re.IGNORECASE | re.VERBOSE | re.MULTILINE, ) @@ -123,7 +113,7 @@ def indent_html(rawcode: str, config: Config) -> str: ) or re.findall( re.compile( - rf"(^<({slt_html})\b.+?>)(.*?)()", + rf"(^<({slt_html})\b[^>]+?>)(.*?)()", re.IGNORECASE | re.VERBOSE | re.MULTILINE, ), item, @@ -157,6 +147,17 @@ def indent_html(rawcode: str, config: Config) -> str: ), item, ) + # # and not ending in a slt like . + # and not re.findall( + # rf"(<({slt_html})>)(.*?)([^<]*?$)", + # item, + # re.IGNORECASE | re.VERBOSE | re.MULTILINE, + # ) + # and not re.findall( + # rf"(<({slt_html})\\b.+?>)(.*?)([^<]*?$)", + # item, + # re.IGNORECASE | re.VERBOSE | re.MULTILINE, + # ) and is_block_raw is False ): tmp = (indent * indent_level) + item + "\n" diff --git a/src/djlint/rules.yaml b/src/djlint/rules.yaml index 884d8d7..bd9c779 100644 --- a/src/djlint/rules.yaml +++ b/src/djlint/rules.yaml @@ -26,7 +26,7 @@ message: Double quotes should be used in tags. flags: re.DOTALL patterns: - - "{%[ \t]*?extends[ \t]+?'[^']*'" + - "{%[ \t]*?(?:trans(?:late)?|with|extends|include|now)?[ \t]+?(?:[^']+?=)?'[^']*'" - rule: name: T003 message: 'Endblock should have name. Ex: {% endblock body %}.' @@ -251,3 +251,9 @@ patterns: - ]+?action=['|"]\s - ]+?action=(['|"])({{(?:(?!}}).)*}}|{%(?:(?!%}).)*%}|([^"'{]))*\s+?\1 +- rule: + name: T034 + message: Did you intend to use {% ... %} instead of {% ... }%? + flags: re.DOTALL + patterns: + - '{%(?:(?!%}).)*}%' diff --git a/tests/test_config/test_preserve_blank_lines/test_config.py b/tests/test_config/test_preserve_blank_lines/test_config.py index 4f50c0d..4906ade 100644 --- a/tests/test_config/test_preserve_blank_lines/test_config.py +++ b/tests/test_config/test_preserve_blank_lines/test_config.py @@ -25,7 +25,7 @@ def test_config(runner: CliRunner) -> None: "--preserve-blank-lines", ], ) - + print(result.output) assert result.exit_code == 0 diff --git a/tests/test_html/test_tag_span.py b/tests/test_html/test_tag_span.py index fd98117..3fef8fb 100644 --- a/tests/test_html/test_tag_span.py +++ b/tests/test_html/test_tag_span.py @@ -63,5 +63,26 @@ def test_nested_string(runner: CliRunner, tmp_file: TextIO) -> None:

+""" + ) + + write_to_file( + tmp_file.name, + b""" +""", + ) + runner.invoke(djlint, [tmp_file.name, "--reformat"]) + + assert ( + Path(tmp_file.name).read_text(encoding="utf8") + == """ """ ) diff --git a/tests/test_linter/test_linter.py b/tests/test_linter/test_linter.py index c2872a9..d2203ee 100644 --- a/tests/test_linter/test_linter.py +++ b/tests/test_linter/test_linter.py @@ -7,7 +7,7 @@ run:: # for a single test - pytest tests/test_linter/test_linter.py::test_ignoring_rules + pytest tests/test_linter/test_linter.py::test_T034 """ # pylint: disable=C0116,C0103 @@ -79,6 +79,31 @@ def test_T002(runner: CliRunner, tmp_file: TextIO) -> None: result = runner.invoke(djlint, [tmp_file.name, "--profile", "django"]) assert "T002" not in result.output + write_to_file(tmp_file.name, b"{% with a='this' %}") + result = runner.invoke(djlint, [tmp_file.name, "--profile", "django"]) + assert result.exit_code == 1 + assert "T002" in result.output + + write_to_file(tmp_file.name, b"{% trans 'this' %}") + result = runner.invoke(djlint, [tmp_file.name, "--profile", "django"]) + assert result.exit_code == 1 + assert "T002" in result.output + + write_to_file(tmp_file.name, b"{% translate 'this' %}") + result = runner.invoke(djlint, [tmp_file.name, "--profile", "django"]) + assert result.exit_code == 1 + assert "T002" in result.output + + write_to_file(tmp_file.name, b"{% include 'this' %}") + result = runner.invoke(djlint, [tmp_file.name, "--profile", "django"]) + assert result.exit_code == 1 + assert "T002" in result.output + + write_to_file(tmp_file.name, b"{% now 'Y-m-d G:i:s' %}") + result = runner.invoke(djlint, [tmp_file.name, "--profile", "django"]) + assert result.exit_code == 1 + assert "T002" in result.output + def test_T003(runner: CliRunner, tmp_file: TextIO) -> None: write_to_file(tmp_file.name, b"{% endblock %}") @@ -837,6 +862,16 @@ def test_H033(runner: CliRunner, tmp_file: TextIO) -> None: assert "H033" in result.output +def test_T034(runner: CliRunner, tmp_file: TextIO) -> None: + write_to_file(tmp_file.name, b"{% not ok }%") + result = runner.invoke(djlint, [tmp_file.name, "--profile", "jinja"]) + assert "T034" in result.output + + write_to_file(tmp_file.name, b"{% not ok \n%}") + result = runner.invoke(djlint, [tmp_file.name, "--profile", "jinja"]) + assert "T034" not in result.output + + def test_rules_not_matched_in_ignored_block( runner: CliRunner, tmp_file: TextIO ) -> None: diff --git a/tox.ini b/tox.ini index 910e867..624d5a8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,10 @@ [tox] -envlist = py3{7,8,9,10}-test +envlist = test skip_missing_interpreters = True isolated_build = True setenv = PYTHONDONTWRITEBYTECODE=1 - [testenv:isort] commands = isort src/djlint @@ -48,16 +47,12 @@ allowlist_externals = mypy pylint - - [testenv] commands = pytest --cov=src/djlint --cov-branch --cov-report xml:coverage.xml --cov-report term-missing {posargs:} -n auto --dist loadgroup skip_install: false allowlist_externals = pytest - - [testenv:test-fast] commands = pytest -n 4