2020-06-11 00:54:34 +02:00
|
|
|
import subprocess
|
2017-03-03 19:01:52 +01:00
|
|
|
from typing import Any, Dict, List
|
2017-02-15 05:39:42 +01:00
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
from zulint.printer import ENDC, GREEN
|
2019-07-18 02:41:47 +02:00
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
from .template_parser import is_django_block_tag, tokenize
|
2019-07-18 02:41:47 +02:00
|
|
|
|
2017-02-15 05:39:42 +01:00
|
|
|
|
python: Convert function type annotations to Python 3 style.
Generated by com2ann (slightly patched to avoid also converting
assignment type annotations, which require Python 3.6), followed by
some manual whitespace adjustment, and six fixes for runtime issues:
- def __init__(self, token: Token, parent: Optional[Node]) -> None:
+ def __init__(self, token: Token, parent: "Optional[Node]") -> None:
-def main(options: argparse.Namespace) -> NoReturn:
+def main(options: argparse.Namespace) -> "NoReturn":
-def fetch_request(url: str, callback: Any, **kwargs: Any) -> Generator[Callable[..., Any], Any, None]:
+def fetch_request(url: str, callback: Any, **kwargs: Any) -> "Generator[Callable[..., Any], Any, None]":
-def assert_server_running(server: subprocess.Popen[bytes], log_file: Optional[str]) -> None:
+def assert_server_running(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> None:
-def server_is_up(server: subprocess.Popen[bytes], log_file: Optional[str]) -> bool:
+def server_is_up(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> bool:
- method_kwarg_pairs: List[FuncKwargPair],
+ method_kwarg_pairs: "List[FuncKwargPair]",
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-19 03:48:37 +02:00
|
|
|
def pretty_print_html(html: str, num_spaces: int = 4) -> str:
|
2017-02-15 05:39:42 +01:00
|
|
|
# We use 1-based indexing for both rows and columns.
|
|
|
|
tokens = tokenize(html)
|
2021-02-12 08:20:45 +01:00
|
|
|
lines = html.split("\n")
|
2017-02-15 05:39:42 +01:00
|
|
|
|
|
|
|
# We will keep a stack of "start" tags so that we know
|
|
|
|
# when HTML ranges end. Note that some start tags won't
|
|
|
|
# be blocks from an indentation standpoint.
|
2020-04-22 01:09:50 +02:00
|
|
|
stack: List[Dict[str, Any]] = []
|
2017-02-15 05:39:42 +01:00
|
|
|
|
|
|
|
# Seed our stack with a pseudo entry to make depth calculations
|
|
|
|
# easier.
|
2020-04-22 01:09:50 +02:00
|
|
|
info: Dict[str, Any] = dict(
|
2017-02-15 05:39:42 +01:00
|
|
|
block=False,
|
|
|
|
depth=-1,
|
|
|
|
line=-1,
|
2021-02-12 08:20:45 +01:00
|
|
|
token_kind="html_start",
|
|
|
|
tag="html",
|
2017-06-13 07:14:48 +02:00
|
|
|
extra_indent=0,
|
2020-04-22 01:09:50 +02:00
|
|
|
ignore_lines=[],
|
|
|
|
)
|
2017-02-15 05:39:42 +01:00
|
|
|
stack.append(info)
|
|
|
|
|
|
|
|
# Our main job is to figure out offsets that we use to nudge lines
|
|
|
|
# over by.
|
2020-04-22 01:09:50 +02:00
|
|
|
offsets: Dict[int, int] = {}
|
2017-02-15 05:39:42 +01:00
|
|
|
|
|
|
|
# Loop through our start/end tokens, and calculate offsets. As
|
|
|
|
# we proceed, we will push/pop info dictionaries on/off a stack.
|
|
|
|
for token in tokens:
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
if (
|
|
|
|
token.kind
|
|
|
|
in (
|
2021-02-12 08:20:45 +01:00
|
|
|
"html_start",
|
|
|
|
"handlebars_start",
|
|
|
|
"handlebars_singleton",
|
|
|
|
"html_singleton",
|
|
|
|
"django_start",
|
|
|
|
"jinja2_whitespace_stripped_type2_start",
|
|
|
|
"jinja2_whitespace_stripped_start",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
and stack[-1]["tag"] != "pre"
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2017-02-15 05:39:42 +01:00
|
|
|
# An HTML start tag should only cause a new indent if we
|
|
|
|
# are on a new line.
|
2021-02-12 08:20:45 +01:00
|
|
|
if token.tag not in ("extends", "include", "else", "elif") and (
|
|
|
|
is_django_block_tag(token.tag) or token.kind != "django_start"
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2021-02-12 08:20:45 +01:00
|
|
|
is_block = token.line > stack[-1]["line"]
|
2017-02-15 05:39:42 +01:00
|
|
|
|
2017-02-16 20:25:53 +01:00
|
|
|
if is_block:
|
2021-02-12 08:19:30 +01:00
|
|
|
if (
|
|
|
|
(
|
2021-02-12 08:20:45 +01:00
|
|
|
token.kind == "handlebars_start"
|
|
|
|
and stack[-1]["token_kind"] == "handlebars_start"
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
|
|
|
or (
|
|
|
|
token.kind
|
|
|
|
in {
|
2021-02-12 08:20:45 +01:00
|
|
|
"django_start",
|
|
|
|
"jinja2_whitespace_stripped_type2_start",
|
|
|
|
"jinja2_whitespace_stripped_start",
|
2021-02-12 08:19:30 +01:00
|
|
|
}
|
2021-02-12 08:20:45 +01:00
|
|
|
and stack[-1]["token_kind"]
|
2021-02-12 08:19:30 +01:00
|
|
|
in {
|
2021-02-12 08:20:45 +01:00
|
|
|
"django_start",
|
|
|
|
"jinja2_whitespace_stripped_type2_start",
|
|
|
|
"jinja2_whitespace_stripped_start",
|
2021-02-12 08:19:30 +01:00
|
|
|
}
|
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
) and not stack[-1]["indenting"]:
|
2017-02-16 20:25:53 +01:00
|
|
|
info = stack.pop()
|
2021-02-12 08:20:45 +01:00
|
|
|
info["depth"] = info["depth"] + 1
|
|
|
|
info["indenting"] = True
|
|
|
|
info["adjust_offset_until"] = token.line
|
2017-02-16 20:25:53 +01:00
|
|
|
stack.append(info)
|
2021-02-12 08:20:45 +01:00
|
|
|
new_depth = stack[-1]["depth"] + 1
|
|
|
|
extra_indent = stack[-1]["extra_indent"]
|
2017-02-16 20:25:53 +01:00
|
|
|
line = lines[token.line - 1]
|
2021-02-12 08:19:30 +01:00
|
|
|
adjustment = len(line) - len(line.lstrip()) + 1
|
2017-02-16 20:25:53 +01:00
|
|
|
offset = (1 + extra_indent + new_depth * num_spaces) - adjustment
|
|
|
|
info = dict(
|
|
|
|
block=True,
|
|
|
|
depth=new_depth,
|
|
|
|
actual_depth=new_depth,
|
|
|
|
line=token.line,
|
2017-02-23 18:12:52 +01:00
|
|
|
tag=token.tag,
|
2017-02-16 20:25:53 +01:00
|
|
|
token_kind=token.kind,
|
2017-02-23 18:12:52 +01:00
|
|
|
line_span=token.line_span,
|
2017-02-16 20:25:53 +01:00
|
|
|
offset=offset,
|
|
|
|
extra_indent=token.col - adjustment + extra_indent,
|
2017-02-23 18:12:52 +01:00
|
|
|
extra_indent_prev=extra_indent,
|
|
|
|
adjustment=adjustment,
|
|
|
|
indenting=True,
|
2017-06-13 07:14:48 +02:00
|
|
|
adjust_offset_until=token.line,
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
ignore_lines=[],
|
2017-02-16 20:25:53 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
if token.kind in ("handlebars_start", "django_start"):
|
2017-02-16 20:25:53 +01:00
|
|
|
info.update(dict(depth=new_depth - 1, indenting=False))
|
|
|
|
else:
|
|
|
|
info = dict(
|
|
|
|
block=False,
|
2021-02-12 08:20:45 +01:00
|
|
|
depth=stack[-1]["depth"],
|
|
|
|
actual_depth=stack[-1]["depth"],
|
2017-02-16 20:25:53 +01:00
|
|
|
line=token.line,
|
2017-02-23 18:12:52 +01:00
|
|
|
tag=token.tag,
|
2017-02-16 20:25:53 +01:00
|
|
|
token_kind=token.kind,
|
2021-02-12 08:20:45 +01:00
|
|
|
extra_indent=stack[-1]["extra_indent"],
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
ignore_lines=[],
|
2017-02-16 20:25:53 +01:00
|
|
|
)
|
|
|
|
stack.append(info)
|
2021-02-12 08:19:30 +01:00
|
|
|
elif (
|
|
|
|
token.kind
|
|
|
|
in (
|
2021-02-12 08:20:45 +01:00
|
|
|
"html_end",
|
|
|
|
"handlebars_end",
|
|
|
|
"html_singleton_end",
|
|
|
|
"django_end",
|
|
|
|
"handlebars_singleton_end",
|
|
|
|
"jinja2_whitespace_stripped_end",
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
and (stack[-1]["tag"] != "pre" or token.tag == "pre")
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2017-02-15 05:39:42 +01:00
|
|
|
info = stack.pop()
|
2021-02-12 08:20:45 +01:00
|
|
|
if info["block"]:
|
2017-02-15 05:39:42 +01:00
|
|
|
# We are at the end of an indentation block. We
|
|
|
|
# assume the whole block was formatted ok before, just
|
|
|
|
# possibly at an indentation that we don't like, so we
|
|
|
|
# nudge over all lines in the block by the same offset.
|
2021-02-12 08:20:45 +01:00
|
|
|
start_line = info["line"]
|
2017-02-15 05:39:42 +01:00
|
|
|
end_line = token.line
|
2021-02-12 08:20:45 +01:00
|
|
|
if token.tag == "pre":
|
2017-02-23 18:12:52 +01:00
|
|
|
offsets[start_line] = 0
|
|
|
|
offsets[end_line] = 0
|
2021-02-12 08:20:45 +01:00
|
|
|
stack[-1]["ignore_lines"].append(start_line)
|
|
|
|
stack[-1]["ignore_lines"].append(end_line)
|
2017-02-23 18:12:52 +01:00
|
|
|
else:
|
2021-02-12 08:20:45 +01:00
|
|
|
offsets[start_line] = info["offset"]
|
2017-02-23 18:12:52 +01:00
|
|
|
line = lines[token.line - 1]
|
2021-02-12 08:19:30 +01:00
|
|
|
adjustment = len(line) - len(line.lstrip()) + 1
|
2021-02-12 08:20:45 +01:00
|
|
|
if adjustment == token.col and token.kind != "html_singleton_end":
|
2021-02-12 08:19:30 +01:00
|
|
|
offsets[end_line] = (
|
2021-02-12 08:20:45 +01:00
|
|
|
info["offset"]
|
|
|
|
+ info["adjustment"]
|
2021-02-12 08:19:30 +01:00
|
|
|
- adjustment
|
2021-02-12 08:20:45 +01:00
|
|
|
+ info["extra_indent"]
|
|
|
|
- info["extra_indent_prev"]
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
elif start_line + info["line_span"] - 1 == end_line and info["line_span"] > 1:
|
2021-02-12 08:19:30 +01:00
|
|
|
offsets[end_line] = (
|
2021-02-12 08:20:45 +01:00
|
|
|
1 + info["extra_indent"] + (info["depth"] + 1) * num_spaces
|
2021-02-12 08:19:30 +01:00
|
|
|
) - adjustment
|
2018-04-05 08:38:46 +02:00
|
|
|
# We would like singleton tags and tags which spread over
|
|
|
|
# multiple lines to have 2 space indentation.
|
|
|
|
offsets[end_line] -= 2
|
2021-02-12 08:20:45 +01:00
|
|
|
elif token.line != info["line"]:
|
|
|
|
offsets[end_line] = info["offset"]
|
|
|
|
if token.tag != "pre" and token.tag != "script":
|
2017-02-15 05:39:42 +01:00
|
|
|
for line_num in range(start_line + 1, end_line):
|
|
|
|
# Be careful not to override offsets that happened
|
|
|
|
# deeper in the HTML within our block.
|
|
|
|
if line_num not in offsets:
|
|
|
|
line = lines[line_num - 1]
|
2021-02-12 08:20:45 +01:00
|
|
|
new_depth = info["depth"] + 1
|
2021-02-12 08:19:30 +01:00
|
|
|
if (
|
2021-02-12 08:20:45 +01:00
|
|
|
line.lstrip().startswith("{{else}}")
|
|
|
|
or line.lstrip().startswith("{% else %}")
|
|
|
|
or line.lstrip().startswith("{% elif")
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2021-02-12 08:20:45 +01:00
|
|
|
new_depth = info["actual_depth"]
|
|
|
|
extra_indent = info["extra_indent"]
|
2021-02-12 08:19:30 +01:00
|
|
|
adjustment = len(line) - len(line.lstrip()) + 1
|
2017-02-15 05:39:42 +01:00
|
|
|
offset = (1 + extra_indent + new_depth * num_spaces) - adjustment
|
2021-02-12 08:20:45 +01:00
|
|
|
if line_num <= start_line + info["line_span"] - 1:
|
2018-04-05 08:38:46 +02:00
|
|
|
# We would like singleton tags and tags which spread over
|
|
|
|
# multiple lines to have 2 space indentation.
|
2018-04-03 10:07:23 +02:00
|
|
|
offset -= 2
|
2017-02-15 05:39:42 +01:00
|
|
|
offsets[line_num] = offset
|
2021-02-12 08:19:30 +01:00
|
|
|
elif (
|
2021-02-12 08:20:45 +01:00
|
|
|
token.kind in ("handlebars_end", "django_end")
|
|
|
|
and info["indenting"]
|
|
|
|
and line_num < info["adjust_offset_until"]
|
|
|
|
and line_num not in info["ignore_lines"]
|
2021-02-12 08:19:30 +01:00
|
|
|
):
|
2017-02-23 18:12:52 +01:00
|
|
|
offsets[line_num] += num_spaces
|
2021-02-12 08:20:45 +01:00
|
|
|
elif token.tag != "pre":
|
2017-02-15 05:39:42 +01:00
|
|
|
for line_num in range(start_line + 1, end_line):
|
|
|
|
if line_num not in offsets:
|
2021-02-12 08:20:45 +01:00
|
|
|
offsets[line_num] = info["offset"]
|
2017-02-23 18:12:52 +01:00
|
|
|
else:
|
|
|
|
for line_num in range(start_line + 1, end_line):
|
|
|
|
if line_num not in offsets:
|
|
|
|
offsets[line_num] = 0
|
2021-02-12 08:20:45 +01:00
|
|
|
stack[-1]["ignore_lines"].append(line_num)
|
2017-02-15 05:39:42 +01:00
|
|
|
|
|
|
|
# Now that we have all of our offsets calculated, we can just
|
|
|
|
# join all our lines together, fixing up offsets as needed.
|
|
|
|
formatted_lines = []
|
2021-02-12 08:20:45 +01:00
|
|
|
for i, line in enumerate(html.split("\n")):
|
2017-02-15 05:39:42 +01:00
|
|
|
row = i + 1
|
|
|
|
offset = offsets.get(row, 0)
|
|
|
|
pretty_line = line
|
2021-02-12 08:20:45 +01:00
|
|
|
if line.strip() == "":
|
|
|
|
pretty_line = ""
|
2017-02-15 05:39:42 +01:00
|
|
|
else:
|
|
|
|
if offset > 0:
|
2021-02-12 08:20:45 +01:00
|
|
|
pretty_line = (" " * offset) + pretty_line
|
2017-02-15 05:39:42 +01:00
|
|
|
elif offset < 0:
|
2021-02-12 08:19:30 +01:00
|
|
|
pretty_line = pretty_line[-1 * offset :]
|
2017-02-15 05:39:42 +01:00
|
|
|
assert line.strip() == pretty_line.strip()
|
|
|
|
formatted_lines.append(pretty_line)
|
|
|
|
|
2021-02-12 08:20:45 +01:00
|
|
|
return "\n".join(formatted_lines)
|
2017-03-12 22:24:26 +01:00
|
|
|
|
|
|
|
|
python: Convert function type annotations to Python 3 style.
Generated by com2ann (slightly patched to avoid also converting
assignment type annotations, which require Python 3.6), followed by
some manual whitespace adjustment, and six fixes for runtime issues:
- def __init__(self, token: Token, parent: Optional[Node]) -> None:
+ def __init__(self, token: Token, parent: "Optional[Node]") -> None:
-def main(options: argparse.Namespace) -> NoReturn:
+def main(options: argparse.Namespace) -> "NoReturn":
-def fetch_request(url: str, callback: Any, **kwargs: Any) -> Generator[Callable[..., Any], Any, None]:
+def fetch_request(url: str, callback: Any, **kwargs: Any) -> "Generator[Callable[..., Any], Any, None]":
-def assert_server_running(server: subprocess.Popen[bytes], log_file: Optional[str]) -> None:
+def assert_server_running(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> None:
-def server_is_up(server: subprocess.Popen[bytes], log_file: Optional[str]) -> bool:
+def server_is_up(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> bool:
- method_kwarg_pairs: List[FuncKwargPair],
+ method_kwarg_pairs: "List[FuncKwargPair]",
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-19 03:48:37 +02:00
|
|
|
def validate_indent_html(fn: str, fix: bool) -> int:
|
2020-04-09 21:51:58 +02:00
|
|
|
with open(fn) as f:
|
2019-07-14 21:37:08 +02:00
|
|
|
html = f.read()
|
2017-03-12 22:24:26 +01:00
|
|
|
phtml = pretty_print_html(html)
|
2021-02-12 08:20:45 +01:00
|
|
|
if not html.split("\n") == phtml.split("\n"):
|
2019-07-18 02:41:47 +02:00
|
|
|
if fix:
|
|
|
|
print(GREEN + "Automatically fixing problems..." + ENDC)
|
2021-02-12 08:20:45 +01:00
|
|
|
with open(fn, "w") as f:
|
2019-07-18 02:41:47 +02:00
|
|
|
f.write(phtml)
|
|
|
|
# Since we successfully fixed the issues, we exit with status 0
|
|
|
|
return 0
|
2021-02-12 08:19:30 +01:00
|
|
|
print(
|
2021-05-10 07:02:14 +02:00
|
|
|
"Invalid indentation detected in file: "
|
2021-02-12 08:20:45 +01:00
|
|
|
f"{fn}\nDiff for the file against expected indented file:",
|
2021-02-12 08:19:30 +01:00
|
|
|
flush=True,
|
|
|
|
)
|
2021-02-12 08:20:45 +01:00
|
|
|
subprocess.run(["diff", fn, "-"], input=phtml, universal_newlines=True)
|
2019-07-18 02:41:47 +02:00
|
|
|
print()
|
|
|
|
print("This problem can be fixed with the `--fix` option.")
|
2017-03-12 22:24:26 +01:00
|
|
|
return 0
|
|
|
|
return 1
|