djLint/src/djlint/lint.py
2021-08-02 12:31:59 -05:00

75 lines
2 KiB
Python

"""Djlint html linter."""
from pathlib import Path
import regex as re
import yaml
rules = yaml.load(
(Path(__file__).parent / "rules.yaml").read_text(encoding="utf8"),
Loader=yaml.SafeLoader,
)
flags = {
"re.A": re.A,
"re.ASCII": re.ASCII,
"re.I": re.I,
"re.IGNORECASE": re.IGNORECASE,
"re.M": re.M,
"re.MULTILINE": re.MULTILINE,
"re.S": re.S,
"re.DOTALL": re.DOTALL,
"re.X": re.X,
"re.VERBOSE": re.VERBOSE,
"re.L": re.L,
"re.LOCALE": re.LOCALE,
}
def build_flags(flag_list):
"""Build list of regex flags."""
split_flags = flag_list.split("|")
combined_flags = 0
for flag in split_flags:
combined_flags |= flags[flag.strip()]
return combined_flags
def get_line(start, line_ends):
"""Get the line number and index of match."""
line = list(filter(lambda pair: pair["end"] > start, line_ends))[0]
return "%d:%d" % (line_ends.index(line) + 1, start - line["start"])
def lint_file(ignore: str, this_file: Path):
"""Check file for formatting errors."""
file_name = str(this_file)
errors: dict = {file_name: []}
html = this_file.read_text(encoding="utf8")
# build list of line ends for file
line_ends = [
{"start": m.start(), "end": m.end()}
for m in re.finditer(r"(?:.*\n)|(?:[^\n]+$)", html)
]
for rule in list(
filter(lambda x: x["rule"]["name"] not in ignore.split(","), rules)
):
rule = rule["rule"]
for pattern in rule["patterns"]:
for match in re.finditer(
pattern, html, flags=build_flags(rule.get("flags", "re.DOTALL"))
):
errors[file_name].append(
{
"code": rule["name"],
"line": get_line(match.start(), line_ends),
"match": match.group()[:20].strip(),
"message": rule["message"],
}
)
return errors