mirror of
https://github.com/Hopiu/djLint.git
synced 2026-03-17 05:50:24 +00:00
220 lines
6.5 KiB
Python
220 lines
6.5 KiB
Python
"""Build djLint console output."""
|
|
import shutil
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
import regex as re
|
|
from click import echo
|
|
from colorama import Fore, Style
|
|
|
|
from .settings import Config
|
|
|
|
try:
|
|
# this is used for windows + gitbash to set encoding correctly.
|
|
import sys
|
|
|
|
sys.stdout.reconfigure(encoding="utf-8") # type: ignore[attr-defined]
|
|
# pylint:disable=W0703
|
|
except BaseException:
|
|
pass
|
|
|
|
|
|
def print_output(
|
|
config: Config, file_errors: List[Dict[Any, Any]], file_count: int
|
|
) -> int:
|
|
"""Print results to console."""
|
|
file_quantity = build_quantity(file_count)
|
|
# format errors
|
|
reformat_success_message = ""
|
|
lint_success_message = ""
|
|
lint_error_count = 0
|
|
format_error_count = 0
|
|
print_blanks = config.stdin is False and config.quiet is False
|
|
|
|
if print_blanks:
|
|
echo()
|
|
|
|
for error in sorted(file_errors, key=lambda x: next(iter(list(x.values())[0]))):
|
|
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)
|
|
|
|
if config.statistics and config.lint:
|
|
build_stats_output([x.get("lint_message") for x in file_errors], config)
|
|
|
|
tense_message = (
|
|
build_quantity(format_error_count) + " would be"
|
|
if config.check is True
|
|
else build_quantity_tense(format_error_count)
|
|
)
|
|
reformat_success_message = f"{tense_message} updated."
|
|
|
|
error_case = "error" if lint_error_count == 1 else "errors"
|
|
lint_success_message += (
|
|
f"Linted {file_quantity}, found {lint_error_count} {error_case}."
|
|
)
|
|
|
|
if print_blanks:
|
|
echo()
|
|
|
|
if (
|
|
config.quiet is False
|
|
and config.stdin is False
|
|
and (config.reformat or config.check)
|
|
):
|
|
reformat_success_color = (
|
|
Fore.RED + Style.BRIGHT if (format_error_count) > 0 else Fore.BLUE
|
|
)
|
|
echo(f"{reformat_success_color}{reformat_success_message}{Style.RESET_ALL}")
|
|
|
|
if config.lint and config.quiet is False:
|
|
lint_success_color = (
|
|
Fore.RED + Style.BRIGHT if (lint_error_count) > 0 else Fore.BLUE
|
|
)
|
|
echo(f"{lint_success_color}{lint_success_message}{Style.RESET_ALL}")
|
|
|
|
if print_blanks:
|
|
echo()
|
|
|
|
return lint_error_count + format_error_count
|
|
|
|
|
|
def build_relative_path(url: str, project_root: Path) -> str:
|
|
"""Get path relative to project."""
|
|
url_path = Path(url)
|
|
if project_root != url_path and project_root in url_path.parents:
|
|
return str(url_path.relative_to(project_root.resolve()))
|
|
|
|
return url
|
|
|
|
|
|
def build_output(error: dict, config: Config) -> int:
|
|
"""Build output for file errors."""
|
|
errors = sorted(
|
|
list(error.values())[0], key=lambda x: tuple(map(int, x["line"].split(":")))
|
|
)
|
|
|
|
width, _ = shutil.get_terminal_size()
|
|
|
|
if len(errors) == 0:
|
|
return 0
|
|
|
|
filename = build_relative_path(list(error.keys())[0], config.project_root.resolve())
|
|
|
|
if "{filename}" not in config.linter_output_format and not config.stdin:
|
|
echo(
|
|
f"{Fore.GREEN}{Style.BRIGHT}\n{filename}\n{Style.DIM}"
|
|
+ "".join(["─" for x in range(1, width)])
|
|
+ Style.RESET_ALL
|
|
)
|
|
|
|
for message_dict in errors:
|
|
line = Fore.BLUE + message_dict["line"] + Style.RESET_ALL
|
|
code = (
|
|
(Fore.RED if message_dict["code"][:1] == "E" else Fore.YELLOW)
|
|
+ message_dict["code"]
|
|
+ Style.RESET_ALL
|
|
)
|
|
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)
|
|
|
|
|
|
def build_check_output(errors: dict, config: Config) -> int:
|
|
"""Build output for reformat check."""
|
|
if len(errors) == 0:
|
|
return 0
|
|
|
|
color = {"-": Fore.YELLOW, "+": Fore.GREEN, "@": Style.BRIGHT + Fore.BLUE}
|
|
width, _ = shutil.get_terminal_size()
|
|
|
|
if config.quiet is False and len(list(errors.values())[0]) > 0:
|
|
echo(
|
|
Fore.GREEN
|
|
+ Style.BRIGHT
|
|
+ "\n"
|
|
+ build_relative_path(list(errors.keys())[0], config.project_root.resolve())
|
|
+ "\n"
|
|
+ Style.DIM
|
|
+ "".join(["─" for x in range(1, width)])
|
|
+ Style.RESET_ALL
|
|
)
|
|
|
|
for diff in list(errors.values())[0][2:]:
|
|
echo(
|
|
f"{ color.get(diff[:1], Style.RESET_ALL)}{diff}{Style.RESET_ALL}",
|
|
err=False,
|
|
)
|
|
|
|
return len(list(filter(lambda x: len(x) > 0, errors.values())))
|
|
|
|
|
|
def build_quantity(size: int) -> str:
|
|
"""Count files in a list."""
|
|
return str(size) + " file" + ("s" if size > 1 or size == 0 else "")
|
|
|
|
|
|
def build_quantity_tense(size: int) -> str:
|
|
"""Count files in a list."""
|
|
return (
|
|
str(size)
|
|
+ " file"
|
|
+ ("s" if size > 1 or size == 0 else "")
|
|
+ " "
|
|
+ ("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
|
|
}
|
|
|
|
echo()
|
|
width, _ = shutil.get_terminal_size()
|
|
echo(
|
|
f"{Fore.GREEN}{Style.BRIGHT}Statistics{Style.RESET_ALL}\n{Style.DIM}{'─' * width}{Style.RESET_ALL}"
|
|
)
|
|
|
|
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())
|