feat(linter): added --statistics flag to print summary of linter findings

closes #389
This commit is contained in:
Christopher Pickering 2022-09-19 09:41:54 +02:00
parent 756c760e44
commit 489decb2e1
No known key found for this signature in database
GPG key ID: E14DB3B0A0FACF84
8 changed files with 89 additions and 16 deletions

View file

@ -5,13 +5,13 @@ exclude: >
) )
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.1.0 rev: v4.3.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: check-yaml - id: check-yaml
- repo: https://github.com/myint/autoflake - repo: https://github.com/myint/autoflake
rev: v1.4 rev: v1.6.0
hooks: hooks:
- id: autoflake - id: autoflake
exclude: &fixtures tests/functional/|tests/input|tests/extensions/data|tests/regrtest_data/|tests/data/ exclude: &fixtures tests/functional/|tests/input|tests/extensions/data|tests/regrtest_data/|tests/data/
@ -22,7 +22,7 @@ repos:
- --remove-duplicate-keys - --remove-duplicate-keys
- --remove-unused-variables - --remove-unused-variables
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.31.0 rev: v2.38.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py36-plus] args: [--py36-plus]
@ -33,11 +33,11 @@ repos:
exclude: docs* exclude: docs*
additional_dependencies: [toml] additional_dependencies: [toml]
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.1.0 rev: 22.8.0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.2.0 rev: v2.4.0
hooks: hooks:
- id: pretty-format-ini - id: pretty-format-ini
args: [--autofix] args: [--autofix]

View file

@ -49,7 +49,9 @@ Options:
--preserve-blank-lines Attempt to preserve blank lines. --preserve-blank-lines Attempt to preserve blank lines.
--format-css Also format contents of <style> tags. --format-css Also format contents of <style> tags.
--format-js Also format contents of <script> tags. --format-js Also format contents of <script> tags.
--configuration Path to global configuration file in .djlintrc format --configuration PATH Path to global configuration file in .djlintrc format
--statistics Count the number of occurrences of each
error/warning code.
-h, --help Show this message and exit. -h, --help Show this message and exit.
``` ```

View file

@ -49,7 +49,9 @@ Options:
--preserve-blank-lines Attempt to preserve blank lines. --preserve-blank-lines Attempt to preserve blank lines.
--format-css Also format contents of <style> tags. --format-css Also format contents of <style> tags.
--format-js Also format contents of <script> tags. --format-js Also format contents of <script> tags.
--configuration Path to global configuration file in .djlintrc format --configuration PATH Path to global configuration file in .djlintrc format
--statistics Count the number of occurrences of each
error/warning code.
-h, --help Show this message and exit. -h, --help Show this message and exit.
``` ```

View file

@ -49,7 +49,9 @@ Options:
--preserve-blank-lines Attempt to preserve blank lines. --preserve-blank-lines Attempt to preserve blank lines.
--format-css Also format contents of <style> tags. --format-css Also format contents of <style> tags.
--format-js Also format contents of <script> tags. --format-js Also format contents of <script> tags.
--configuration Path to global configuration file in .djlintrc format --configuration PATH Path to global configuration file in .djlintrc format
--statistics Count the number of occurrences of each
error/warning code.
-h, --help Show this message and exit. -h, --help Show this message and exit.
``` ```

View file

@ -122,6 +122,11 @@ from .src import get_src
required=False, required=False,
help="Path to global configuration file in .djlintrc format", help="Path to global configuration file in .djlintrc format",
) )
@click.option(
"--statistics",
is_flag=True,
help="Count the number of occurrences of each error/warning code.",
)
@colorama_text(autoreset=True) @colorama_text(autoreset=True)
def main( def main(
src: List[str], src: List[str],
@ -141,6 +146,7 @@ def main(
format_css: bool, format_css: bool,
format_js: bool, format_js: bool,
configuration: Optional[str], configuration: Optional[str],
statistics: bool,
) -> None: ) -> None:
"""djLint · HTML template linter and formatter.""" """djLint · HTML template linter and formatter."""
config = Config( config = Config(
@ -161,6 +167,7 @@ def main(
format_css=format_css, format_css=format_css,
format_js=format_js, format_js=format_js,
configuration=configuration, configuration=configuration,
statistics=statistics,
) )
temp_file = None temp_file = None

View file

@ -1,7 +1,8 @@
"""Build djLint console output.""" """Build djLint console output."""
import shutil import shutil
from collections import Counter
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List from typing import Any, Dict, List, Optional
import regex as re import regex as re
from click import echo from click import echo
@ -33,14 +34,22 @@ def print_output(
if config.stdin is False or config.lint: if config.stdin is False or config.lint:
echo() echo()
for error in sorted(file_errors, key=lambda x: next(iter(list(x.values())[0]))): if config.statistics:
if error.get("format_message") and config.stdin is False: lint_error_count = build_stats_output(
# reformat message [x.get("lint_message") for x in file_errors], config
format_error_count += build_check_output(error["format_message"], config) )
if error.get("lint_message"): else:
# lint message for error in sorted(file_errors, key=lambda x: next(iter(list(x.values())[0]))):
lint_error_count += build_output(error["lint_message"], config) if error.get("format_message") and config.stdin is False:
# reformat message
format_error_count += build_check_output(
error["format_message"], config
)
if error.get("lint_message"):
# lint message
lint_error_count += build_output(error["lint_message"], config)
tense_message = ( tense_message = (
build_quantity(format_error_count) + " would be" build_quantity(format_error_count) + " would be"
@ -180,3 +189,37 @@ def build_quantity_tense(size: int) -> str:
+ " " + " "
+ ("were" if size > 1 or size == 0 else "was") + ("were" if size > 1 or size == 0 else "was")
) )
def build_stats_output(errors: List[Optional[Any]], config: Config) -> int:
"""Build output for linter statistics."""
if len(errors) == 0:
return 0
codes = []
for error in errors:
if error:
for code in list(error.values())[0]:
codes.append(code["code"])
messages = {
rule["rule"]["name"]: rule["rule"]["message"] for rule in config.linter_rules
}
if messages and codes:
longest_code = len(max(messages.keys(), key=len))
longest_count = len(
str(max(Counter(codes).values(), key=lambda x: len(str(x))))
)
for code in sorted(Counter(codes).items()):
code_space = (longest_code - len(str(code[0]))) * " "
count_space = (longest_count - len(str(code[1]))) * " "
echo(
f"{Fore.YELLOW}{code[0]}{Fore.BLUE} {code_space}{code[1]}{Style.RESET_ALL} {count_space}{messages[code[0]]}"
)
return sum(Counter(codes).values())

View file

@ -206,6 +206,7 @@ class Config:
format_css: bool = False, format_css: bool = False,
format_js: bool = False, format_js: bool = False,
configuration: Optional[str] = None, configuration: Optional[str] = None,
statistics: bool = False,
): ):
self.reformat = reformat self.reformat = reformat
@ -306,6 +307,8 @@ class Config:
) )
) )
self.statistics = statistics
# base options # base options
default_indent = 4 default_indent = 4
if not indent: if not indent:

View file

@ -954,3 +954,17 @@ def test_ignoring_rules(runner: CliRunner, tmp_file: TextIO) -> None:
) )
result = runner.invoke(djlint, [tmp_file.name]) result = runner.invoke(djlint, [tmp_file.name])
assert "H006" not in result.output assert "H006" not in result.output
def test_statistics_empty(runner: CliRunner, tmp_file: TextIO) -> None:
write_to_file(tmp_file.name, b"")
result = runner.invoke(djlint, [tmp_file.name, "--statistics"])
assert result.exit_code == 0
def test_statistics_with_results(runner: CliRunner, tmp_file: TextIO) -> None:
write_to_file(tmp_file.name, b"<div>")
result = runner.invoke(djlint, [tmp_file.name, "--statistics"])
assert result.exit_code == 1