mirror of
https://github.com/Hopiu/djLint.git
synced 2026-04-29 01:14:44 +00:00
feat(linter): added --statistics flag to print summary of linter findings
closes #389
This commit is contained in:
parent
756c760e44
commit
489decb2e1
8 changed files with 89 additions and 16 deletions
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue