From 1783c3babf728dd529a4a78efd5bfb8715737884 Mon Sep 17 00:00:00 2001 From: Christopher Pickering Date: Mon, 29 Nov 2021 13:05:58 +0100 Subject: [PATCH] added option to format linter output messages. closes #153 --- docs/djlint/changelog.rst | 1 + docs/djlint/configuration.rst | 19 +++++++++ src/djlint/lint.py | 12 +++--- src/djlint/output.py | 41 +++++++++++-------- src/djlint/settings.py | 6 ++- .../config_linter_output_format/html-one.html | 1 + .../config_linter_output_format/html-two.html | 1 + .../pyproject.toml | 3 ++ tests/test_config_linter_output_format.py | 32 +++++++++++++++ tests/test_linter.py | 4 +- 10 files changed, 96 insertions(+), 24 deletions(-) create mode 100644 tests/config_linter_output_format/html-one.html create mode 100644 tests/config_linter_output_format/html-two.html create mode 100644 tests/config_linter_output_format/pyproject.toml create mode 100644 tests/test_config_linter_output_format.py diff --git a/docs/djlint/changelog.rst b/docs/djlint/changelog.rst index f3ae32e..166a119 100644 --- a/docs/djlint/changelog.rst +++ b/docs/djlint/changelog.rst @@ -4,6 +4,7 @@ Changelog next release ------------ - Added config option ``format_attribute_template_tags`` as opt-in for template tag formatting inside of attributes +- Added config option ``linter_output_format`` to customize linter message variable order 0.6.6 ----- diff --git a/docs/djlint/configuration.rst b/docs/djlint/configuration.rst index 518e1dc..c89cb54 100644 --- a/docs/djlint/configuration.rst +++ b/docs/djlint/configuration.rst @@ -181,3 +181,22 @@ For example, with this option enabled, the following html will be acceptable: {% else %} that is long stuff asdf and more even {% endif %}"/> + + +linter_output_format +-------------------- + +Customize order of output message. Default="{code} {line} {message} {match}". If ``{filename}`` is not include in message, then the output will be grouped by file and a header will automatically be added to each group. + +Usage: + +.. code:: ini + + # optional variables: + # {filename} + # {line} + # {code} + # {message} + # {match} + + linter_output_format="{filename}:{line}: {code} {message} {match}" diff --git a/src/djlint/lint.py b/src/djlint/lint.py index 408db33..320363d 100644 --- a/src/djlint/lint.py +++ b/src/djlint/lint.py @@ -44,8 +44,8 @@ def get_line(start: int, line_ends: List) -> str: def lint_file(config: Config, this_file: Path) -> Dict: """Check file for formatting errors.""" - file_name = str(this_file) - errors: dict = {file_name: []} + filename = str(this_file) + errors: dict = {filename: []} html = this_file.read_text(encoding="utf8") # build list of line ends for file @@ -92,7 +92,7 @@ def lint_file(config: Config, this_file: Path) -> Dict: for match in open_tags: if inside_ignored_block(config, html, match) is False: - errors[file_name].append( + errors[filename].append( { "code": rule["name"], "line": get_line(match.start(), line_ends), @@ -109,7 +109,7 @@ def lint_file(config: Config, this_file: Path) -> Dict: html, ): if inside_ignored_block(config, html, match) is False: - errors[file_name].append( + errors[filename].append( { "code": rule["name"], "line": get_line(match.start(), line_ends), @@ -119,10 +119,10 @@ def lint_file(config: Config, this_file: Path) -> Dict: ) # remove duplicate matches - for file_name, error_dict in errors.items(): + for filename, error_dict in errors.items(): unique_errors = [] for dict_ in error_dict: if dict_ not in unique_errors: unique_errors.append(dict_) - errors[file_name] = unique_errors + errors[filename] = unique_errors return errors diff --git a/src/djlint/output.py b/src/djlint/output.py index 85be5f1..9126e9e 100644 --- a/src/djlint/output.py +++ b/src/djlint/output.py @@ -85,28 +85,37 @@ def build_output(error: dict, config: Config) -> int: if len(errors) == 0: return 0 - echo( - f"{Fore.GREEN}{Style.BRIGHT}\n{build_relative_path(list(error.keys())[0],config.project_root.resolve())}\n{Style.DIM}" - + "".join(["─" for x in range(1, width)]) - + Style.RESET_ALL - ) + filename = build_relative_path(list(error.keys())[0], config.project_root.resolve()) - for message in errors: + if "{filename}" not in config.linter_output_format: echo( - (Fore.RED if bool(message["code"][:1] == "E") else Fore.YELLOW) - + message["code"] + f"{Fore.GREEN}{Style.BRIGHT}\n{filename}\n{Style.DIM}" + + "".join(["─" for x in range(1, width)]) + Style.RESET_ALL - + Fore.BLUE - + " " - + message["line"] + ) + + for message_dict in errors: + + line = Fore.BLUE + message_dict["line"] + Style.RESET_ALL + code = ( + (Fore.RED if bool(message_dict["code"][:1] == "E") else Fore.YELLOW) + + message_dict["code"] + Style.RESET_ALL - + " " - + message["message"] - + Fore.BLUE - + " " - + re.sub(r"\s{2,}", " ", message["match"]), + ) + message = message_dict["message"] + match = ( + Fore.BLUE + + re.sub(r"\s{2,}|\n", " ", message_dict["match"]) + + Style.RESET_ALL + ) + + echo( + config.linter_output_format.format( + filename=filename, line=line, code=code, message=message, match=match + ), err=False, ) + return len(errors) diff --git a/src/djlint/settings.py b/src/djlint/settings.py index dcf680f..9dc809f 100644 --- a/src/djlint/settings.py +++ b/src/djlint/settings.py @@ -180,7 +180,7 @@ class Config: build_custom_blocks(djlint_settings.get("custom_blocks")) or "" ) - self.format_attribute_template_tags = djlint_settings.get( + self.format_attribute_template_tags: bool = djlint_settings.get( "format_attribute_template_tags", False ) @@ -203,6 +203,10 @@ class Config: profile or djlint_settings.get("profile", "all") ).lower() + self.linter_output_format: str = djlint_settings.get( + "linter_output_format", "{code} {line} {message} {match}" + ) + # load linter rules rule_set = validate_rules( yaml.load( diff --git a/tests/config_linter_output_format/html-one.html b/tests/config_linter_output_format/html-one.html new file mode 100644 index 0000000..2c4a538 --- /dev/null +++ b/tests/config_linter_output_format/html-one.html @@ -0,0 +1 @@ +

asdf

diff --git a/tests/config_linter_output_format/html-two.html b/tests/config_linter_output_format/html-two.html new file mode 100644 index 0000000..2c4a538 --- /dev/null +++ b/tests/config_linter_output_format/html-two.html @@ -0,0 +1 @@ +

asdf

diff --git a/tests/config_linter_output_format/pyproject.toml b/tests/config_linter_output_format/pyproject.toml new file mode 100644 index 0000000..aadeb7b --- /dev/null +++ b/tests/config_linter_output_format/pyproject.toml @@ -0,0 +1,3 @@ +[tool] +[tool.djlint] +linter_output_format="{filename}:{line}: {code} {message} {match}" diff --git a/tests/test_config_linter_output_format.py b/tests/test_config_linter_output_format.py new file mode 100644 index 0000000..edd2ec7 --- /dev/null +++ b/tests/test_config_linter_output_format.py @@ -0,0 +1,32 @@ +"""Djlint tests specific to linter output format. + +run:: + + pytest tests/test_config_linter_output_format.py --cov=src/djlint --cov-branch \ + --cov-report xml:coverage.xml --cov-report term-missing + +for a single test, run:: + + pytest tests/test_config_linter_output_format.py::test_with_config --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_with_config(runner: CliRunner) -> None: + result = runner.invoke(djlint, ["tests/config_linter_output_format", "--lint"]) + assert result.exit_code == 1 + + print(result.output) + assert ( + """html-one.html:1:0: H025 Tag seems to be an orphan.

+html-one.html:1:8: H025 Tag seems to be an orphan.

+html-two.html:1:0: H025 Tag seems to be an orphan.

+html-two.html:1:8: H025 Tag seems to be an orphan.

""" + in result.output + ) diff --git a/tests/test_linter.py b/tests/test_linter.py index 6e1d43d..8d98919 100644 --- a/tests/test_linter.py +++ b/tests/test_linter.py @@ -7,7 +7,7 @@ run:: # for a single test - pytest tests/test_linter.py::test_T028 --cov=src/djlint --cov-branch \ + pytest tests/test_linter.py::test_output_for_no_linebreaks --cov=src/djlint --cov-branch \ --cov-report xml:coverage.xml --cov-report term-missing """ @@ -576,6 +576,7 @@ def test_output_for_no_linebreaks(runner: CliRunner, tmp_file: TextIO) -> None: write_to_file(tmp_file.name, b"

asdf

\n

asdf

") result = runner.invoke(djlint, [tmp_file.name]) assert result.exit_code == 1 + assert "\n" not in result.output @@ -583,6 +584,7 @@ def test_output_order(runner: CliRunner, tmp_file: TextIO) -> None: write_to_file(tmp_file.name, b"

asdf

\n

asdf

") result = runner.invoke(djlint, [tmp_file.name]) assert result.exit_code == 1 + assert ( """H025 1:0 Tag seems to be an orphan.

H015 1:8 Follow h tags with a line break.