From ed3951838f69cb4176a8afe8c9c06a95e94e21f9 Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Wed, 13 Oct 2021 15:26:53 +0100 Subject: [PATCH 1/2] Add require_pragma option This patch adds a configuration option for requiring files to have a pragma comment at the beginning of the file in order to reformat the file. This behaviour is useful for a slow adoption of the tool instead of requiring the whole codebase to be converted at once. The patch also contains tests for the different kinds of supported pragma comments, based on the profile defined for the project. --- docs/djlint/configuration.rst | 23 ++++++ src/djlint/__init__.py | 7 ++ src/djlint/reformat.py | 39 ++++++++++ src/djlint/settings.py | 5 ++ tests/config_pragmas/html_five.html | 2 + tests/config_pragmas/html_four.html | 5 ++ tests/config_pragmas/html_one.html | 1 + tests/config_pragmas/html_six.html | 2 + tests/config_pragmas/html_three.html | 4 ++ tests/config_pragmas/html_two.html | 2 + tests/config_pragmas/pyproject.toml | 3 + tests/test_config.py | 102 +++++++++++++++++++++++++++ 12 files changed, 195 insertions(+) create mode 100644 tests/config_pragmas/html_five.html create mode 100644 tests/config_pragmas/html_four.html create mode 100644 tests/config_pragmas/html_one.html create mode 100644 tests/config_pragmas/html_six.html create mode 100644 tests/config_pragmas/html_three.html create mode 100644 tests/config_pragmas/html_two.html create mode 100644 tests/config_pragmas/pyproject.toml diff --git a/docs/djlint/configuration.rst b/docs/djlint/configuration.rst index 4c2fca2..a6d1566 100644 --- a/docs/djlint/configuration.rst +++ b/docs/djlint/configuration.rst @@ -104,3 +104,26 @@ Usage: .. code:: ini profile="django" + +require_pragma +------ + +Only formats files that starts with a comment with only the word 'format'. The comment can be a HTML comment or a comment in the templating language defined by the profile setting. If no profile is specified, a comment in any of the templating languages is accepted. + +Usage: + +.. code:: ini + + require_pragma = true + +.. code:: html + + + or + {# format #} + or + {% comment %} format {% endcomment %} + or + {{ /* format */ }} + or + {{!-- format --}} diff --git a/src/djlint/__init__.py b/src/djlint/__init__.py index 46580c2..c7948d0 100644 --- a/src/djlint/__init__.py +++ b/src/djlint/__init__.py @@ -200,6 +200,11 @@ def build_quantity_tense(size: int) -> str: type=str, help="Enable defaults by template language. ops: django, jinja, nunjucks, handlebars, golang", ) +@click.option( + "--require-pragma", + is_flag=True, + help="Only formats files that starts with a comment with only the word 'format'", +) def main( src: List[str], extension: str, @@ -209,6 +214,7 @@ def main( check: bool, quiet: bool, profile: str, + require_pragma: str, ) -> None: """djLint ยท lint and reformat HTML templates.""" config = Config( @@ -218,6 +224,7 @@ def main( indent=indent, quiet=quiet, profile=profile, + require_pragma=require_pragma, ) temp_file = None diff --git a/src/djlint/reformat.py b/src/djlint/reformat.py index 0c1adef..3970edf 100644 --- a/src/djlint/reformat.py +++ b/src/djlint/reformat.py @@ -5,17 +5,56 @@ Much code is borrowed from https://github.com/rareyman/HTMLBeautify, many thanks import difflib from pathlib import Path +import regex as re from .formatter.compress_html import compress_html from .formatter.expand_html import expand_html from .formatter.indent_html import indent_html from .settings import Config +html_patterns = [re.compile(r"")] +django_jinja_patterns = [ + re.compile(r"\{#\s*format\s*#\}"), + re.compile(r"\{%\s*comment\s*%\}\s*format\s*\{%\s*endcomment\s*%\}"), +] +nunjucks_patterns = [re.compile(r"\{#\s*format\s*#\}")] +handlebars_patterns = [re.compile(r"\{\{!--\s*format\s*--\}\}")] +golang_patterns = [ + re.compile(r"\{\{\s*/\*\s*format\s*\*/\s*\}\}"), + re.compile(r"\{\{-\s*/\*\s*format\s*/\*\s*-\}\}"), +] + + +def has_pragma(config: Config, html_str: str): + first_line = html_str.partition("\n")[0] + + pragma_patterns = { + "django": django_jinja_patterns + html_patterns, + "jinja": django_jinja_patterns + html_patterns, + "nunjucks": nunjucks_patterns + html_patterns, + "handlebars": handlebars_patterns + html_patterns, + "golang": golang_patterns + html_patterns, + "all": django_jinja_patterns + + nunjucks_patterns + + handlebars_patterns + + golang_patterns + + html_patterns, + } + + for pattern in pragma_patterns[config.profile]: + if re.match(pattern, first_line): + return True + return False + def reformat_file(config: Config, check: bool, this_file: Path) -> dict: """Reformat html file.""" rawcode = this_file.read_text(encoding="utf8") + if config.require_pragma and not has_pragma(config, rawcode): + # The file should not be reformatted + return {this_file: []} + expanded = expand_html(rawcode, config) compressed = compress_html(expanded, config) indented = indent_html(compressed, config) diff --git a/src/djlint/settings.py b/src/djlint/settings.py index 9a0955a..c496d8c 100644 --- a/src/djlint/settings.py +++ b/src/djlint/settings.py @@ -117,6 +117,7 @@ class Config: indent: Optional[int] = None, quiet: Optional[bool] = False, profile: Optional[str] = None, + require_pragma: Optional[bool] = False, ): djlint_settings = load_pyproject_settings(Path(src)) @@ -124,6 +125,10 @@ class Config: # custom configuration options self.extension: str = str(extension or djlint_settings.get("extension", "html")) self.quiet: str = str(quiet or djlint_settings.get("quiet", "")) + self.require_pragma: bool = ( + require_pragma + or str(djlint_settings.get("require_pragma", "false")).lower() == "true" + ) self.custom_blocks: str = str( build_custom_blocks(djlint_settings.get("custom_blocks")) or "" ) diff --git a/tests/config_pragmas/html_five.html b/tests/config_pragmas/html_five.html new file mode 100644 index 0000000..c27f06f --- /dev/null +++ b/tests/config_pragmas/html_five.html @@ -0,0 +1,2 @@ + +{% extends "nothing.html" %}{% load stuff %}{% load stuff 2 %}{% include "html_two.html" %}
diff --git a/tests/config_pragmas/html_four.html b/tests/config_pragmas/html_four.html new file mode 100644 index 0000000..b376898 --- /dev/null +++ b/tests/config_pragmas/html_four.html @@ -0,0 +1,5 @@ +{{ /* format */ }} +

Test

{{ .Variable }}

+{{ range .Items }}

{{ . }} + +

{{ end }} diff --git a/tests/config_pragmas/html_one.html b/tests/config_pragmas/html_one.html new file mode 100644 index 0000000..0cf231e --- /dev/null +++ b/tests/config_pragmas/html_one.html @@ -0,0 +1 @@ +{% extends "nothing.html" %}{% load stuff %}{% load stuff 2 %}{% include "html_two.html" %}
diff --git a/tests/config_pragmas/html_six.html b/tests/config_pragmas/html_six.html new file mode 100644 index 0000000..8a76b61 --- /dev/null +++ b/tests/config_pragmas/html_six.html @@ -0,0 +1,2 @@ +{% comment %} format {% endcomment %} +{% extends "nothing.html" %}{% load stuff %}{% load stuff 2 %}{% include "html_two.html" %}
diff --git a/tests/config_pragmas/html_three.html b/tests/config_pragmas/html_three.html new file mode 100644 index 0000000..34736ba --- /dev/null +++ b/tests/config_pragmas/html_three.html @@ -0,0 +1,4 @@ +{{!-- format --}} +

+ +{{firstname}}

{{lastname}}

diff --git a/tests/config_pragmas/html_two.html b/tests/config_pragmas/html_two.html new file mode 100644 index 0000000..f9834b0 --- /dev/null +++ b/tests/config_pragmas/html_two.html @@ -0,0 +1,2 @@ +{# format #} +{% extends "nothing.html" %}{% load stuff %}{% load stuff 2 %}{% include "html_two.html" %}
diff --git a/tests/config_pragmas/pyproject.toml b/tests/config_pragmas/pyproject.toml new file mode 100644 index 0000000..8cc97e6 --- /dev/null +++ b/tests/config_pragmas/pyproject.toml @@ -0,0 +1,3 @@ +[tool] +[tool.djlint] +require_pragma=true diff --git a/tests/test_config.py b/tests/test_config.py index 430355f..bdb39a4 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -183,3 +183,105 @@ def test_profile(runner: CliRunner) -> None: +{{ test }}""" in result.output ) + + +def test_require_pragma(runner: CliRunner) -> None: + result = runner.invoke( + djlint, ["tests/config_pragmas/html_one.html", "--check", "--profile", "django"] + ) + + assert """0 files would be updated.""" in result.output + assert result.exit_code == 0 + + result = runner.invoke( + djlint, ["tests/config_pragmas/html_two.html", "--check", "--profile", "django"] + ) + assert ( + """ {# format #} +-{% extends "nothing.html" %}{% load stuff %}{% load stuff 2 %}{% include "html_two.html" %}
++{% extends "nothing.html" %} ++{% load stuff %} ++{% load stuff 2 %} ++{% include "html_two.html" %} ++
""" + in result.output + ) + assert """1 file would be updated.""" in result.output + assert result.exit_code == 1 + + result = runner.invoke( + djlint, + ["tests/config_pragmas/html_three.html", "--check", "--profile", "handlebars"], + ) + + assert ( + """ {{!-- format --}} +

+- +-{{firstname}}

{{lastname}}

++ {{firstname}} ++

++

++ {{lastname}} ++

""" + in result.output + ) + assert """1 file would be updated.""" in result.output + assert result.exit_code == 1 + + result = runner.invoke( + djlint, + ["tests/config_pragmas/html_four.html", "--check", "--profile", "golang"], + ) + + assert ( + """ {{ /* format */ }} +-

Test

{{ .Variable }}

+-{{ range .Items }}

{{ . }} +- +-

{{ end }} ++

Test

++

++ {{ .Variable }} ++

++{{ range .Items }} ++

++ {{ . }} ++

++{{ end }} + +1 file would be updated.""" + in result.output + ) + assert """1 file would be updated.""" in result.output + assert result.exit_code == 1 + + result = runner.invoke(djlint, ["tests/config_pragmas/html_five.html", "--check"]) + assert ( + """ +-{% extends "nothing.html" %}{% load stuff %}{% load stuff 2 %}{% include "html_two.html" %}
++{% extends "nothing.html" %} ++{% load stuff %} ++{% load stuff 2 %} ++{% include "html_two.html" %} ++
""" + in result.output + ) + assert """1 file would be updated.""" in result.output + assert result.exit_code == 1 + + result = runner.invoke( + djlint, ["tests/config_pragmas/html_six.html", "--check", "--profile", "django"] + ) + assert ( + """ {% comment %} format {% endcomment %} +-{% extends "nothing.html" %}{% load stuff %}{% load stuff 2 %}{% include "html_two.html" %}
++{% extends "nothing.html" %} ++{% load stuff %} ++{% load stuff 2 %} ++{% include "html_two.html" %} ++
""" + in result.output + ) + assert """1 file would be updated.""" in result.output + assert result.exit_code == 1 From 545d5c0e3f82208c4f032470934b2b383b86cd77 Mon Sep 17 00:00:00 2001 From: Christopher Pickering Date: Wed, 13 Oct 2021 18:09:27 +0300 Subject: [PATCH 2/2] updated docs --- docs/djlint/configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/djlint/configuration.rst b/docs/djlint/configuration.rst index a6d1566..fd7e94f 100644 --- a/docs/djlint/configuration.rst +++ b/docs/djlint/configuration.rst @@ -106,9 +106,9 @@ Usage: profile="django" require_pragma ------- +-------------- -Only formats files that starts with a comment with only the word 'format'. The comment can be a HTML comment or a comment in the templating language defined by the profile setting. If no profile is specified, a comment in any of the templating languages is accepted. +Only format files that starts with a comment with only the word 'format'. The comment can be a HTML comment or a comment in the template language defined by the profile setting. If no profile is specified, a comment in any of the template languages is accepted. Usage: