snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
197
tools/lib/pretty_print.py
Normal file
197
tools/lib/pretty_print.py
Normal file
@@ -0,0 +1,197 @@
|
||||
import subprocess
|
||||
|
||||
from zulint.printer import BOLDRED, CYAN, ENDC, GREEN
|
||||
|
||||
from .template_parser import Token
|
||||
|
||||
|
||||
def shift_indents_to_the_next_tokens(tokens: list[Token]) -> None:
|
||||
"""
|
||||
During the parsing/validation phase, it's useful to have separate
|
||||
tokens for "indent" chunks, but during pretty printing, we like
|
||||
to attach an `.indent` field to the substantive node, whether
|
||||
it's an HTML tag or template directive or whatever.
|
||||
"""
|
||||
tokens[0].indent = ""
|
||||
|
||||
for i, token in enumerate(tokens[:-1]):
|
||||
next_token = tokens[i + 1]
|
||||
|
||||
if token.kind == "indent":
|
||||
next_token.indent = token.s
|
||||
token.new_s = ""
|
||||
|
||||
if token.kind == "newline" and next_token.kind != "indent":
|
||||
next_token.indent = ""
|
||||
|
||||
|
||||
def token_allows_children_to_skip_indents(token: Token) -> bool:
|
||||
# To avoid excessive indentation in templates with other
|
||||
# conditionals, we don't require extra indentation for template
|
||||
# logic blocks don't contain further logic as direct children.
|
||||
|
||||
# Each blocks are excluded from this rule, since we want loops to
|
||||
# stand out.
|
||||
if token.tag == "each":
|
||||
return False
|
||||
|
||||
return token.kind in ("django_start", "handlebars_start") or token.tag == "a"
|
||||
|
||||
|
||||
def adjust_block_indentation(tokens: list[Token], fn: str) -> None:
|
||||
start_token: Token | None = None
|
||||
|
||||
for token in tokens:
|
||||
if token.kind in ("indent", "whitespace", "newline"):
|
||||
continue
|
||||
|
||||
if token.tag in ("code", "pre"):
|
||||
continue
|
||||
|
||||
# print(token.line, repr(start_token.indent) if start_token else "?", repr(token.indent), token.s, token.end_token and "start", token.start_token and "end")
|
||||
|
||||
if token.tag == "else":
|
||||
assert token.start_token
|
||||
if token.indent is not None:
|
||||
token.indent = token.start_token.indent
|
||||
continue
|
||||
|
||||
if start_token and token.indent is not None:
|
||||
if (
|
||||
not start_token.indent_is_final
|
||||
and token.indent == start_token.orig_indent
|
||||
and token_allows_children_to_skip_indents(start_token)
|
||||
):
|
||||
start_token.child_indent = start_token.indent
|
||||
start_token.indent_is_final = True
|
||||
|
||||
# Detect start token by its having a end token
|
||||
if token.end_token:
|
||||
if token.indent is not None:
|
||||
token.orig_indent = token.indent
|
||||
if start_token:
|
||||
assert start_token.child_indent is not None
|
||||
token.indent = start_token.child_indent
|
||||
else:
|
||||
token.indent = ""
|
||||
token.child_indent = token.indent + " "
|
||||
token.parent_token = start_token
|
||||
start_token = token
|
||||
continue
|
||||
|
||||
# Detect end token by its having a start token
|
||||
if token.start_token:
|
||||
if start_token != token.start_token:
|
||||
raise AssertionError(
|
||||
f"""
|
||||
{token.kind} was unexpected in {token.s}
|
||||
in row {token.line} of {fn}
|
||||
"""
|
||||
)
|
||||
|
||||
if token.indent is not None:
|
||||
token.indent = start_token.indent
|
||||
start_token = start_token.parent_token
|
||||
continue
|
||||
|
||||
if token.indent is None:
|
||||
continue
|
||||
|
||||
if start_token is None:
|
||||
token.indent = ""
|
||||
continue
|
||||
|
||||
if start_token.child_indent is not None:
|
||||
token.indent = start_token.child_indent
|
||||
|
||||
|
||||
def fix_indents_for_multi_line_tags(tokens: list[Token]) -> None:
|
||||
def fix(frag: str) -> str:
|
||||
frag = frag.strip()
|
||||
return continue_indent + frag if frag else ""
|
||||
|
||||
for token in tokens:
|
||||
if token.kind == "code":
|
||||
continue
|
||||
|
||||
if token.line_span == 1 or token.indent is None:
|
||||
continue
|
||||
|
||||
if token.kind in ("django_comment", "handlebars_comment", "html_comment", "text"):
|
||||
continue_indent = token.indent
|
||||
else:
|
||||
continue_indent = token.indent + " "
|
||||
|
||||
frags = token.new_s.split("\n")
|
||||
|
||||
token.new_s = frags[0] + "\n" + "\n".join(fix(frag) for frag in frags[1:])
|
||||
|
||||
|
||||
def apply_token_indents(tokens: list[Token]) -> None:
|
||||
for token in tokens:
|
||||
if token.indent:
|
||||
token.new_s = token.indent + token.new_s
|
||||
|
||||
|
||||
def pretty_print_html(tokens: list[Token], fn: str) -> str:
|
||||
for token in tokens:
|
||||
token.new_s = token.s
|
||||
|
||||
shift_indents_to_the_next_tokens(tokens)
|
||||
adjust_block_indentation(tokens, fn)
|
||||
fix_indents_for_multi_line_tags(tokens)
|
||||
apply_token_indents(tokens)
|
||||
|
||||
return "".join(token.new_s for token in tokens)
|
||||
|
||||
|
||||
def numbered_lines(s: str) -> str:
|
||||
return "".join(f"{i + 1: >5} {line}\n" for i, line in enumerate(s.split("\n")))
|
||||
|
||||
|
||||
def validate_indent_html(fn: str, tokens: list[Token], fix: bool) -> bool:
|
||||
with open(fn) as f:
|
||||
html = f.read()
|
||||
phtml = pretty_print_html(tokens, fn)
|
||||
if html.split("\n") != phtml.split("\n"):
|
||||
if fix:
|
||||
print(GREEN + f"Automatically fixing indentation for {fn}" + ENDC)
|
||||
with open(fn, "w") as f:
|
||||
f.write(phtml)
|
||||
# Since we successfully fixed the issues, we return True.
|
||||
return True
|
||||
print(
|
||||
f"""
|
||||
{BOLDRED}PROBLEM{ENDC}: formatting errors in {fn}
|
||||
|
||||
Here is how we would like you to format
|
||||
{CYAN}{fn}{ENDC}:
|
||||
---
|
||||
{numbered_lines(phtml)}
|
||||
---
|
||||
|
||||
Here is the diff that you should either execute in your editor
|
||||
or apply automatically with the --fix option.
|
||||
|
||||
({CYAN}Scroll up{ENDC} to see how we would like the file formatted.)
|
||||
|
||||
Proposed {BOLDRED}diff{ENDC} for {CYAN}{fn}{ENDC}:
|
||||
""",
|
||||
flush=True,
|
||||
)
|
||||
subprocess.run(["diff", fn, "-"], input=phtml, text=True, check=False)
|
||||
print(
|
||||
f"""
|
||||
---
|
||||
|
||||
{BOLDRED}PROBLEM!!!{ENDC}
|
||||
|
||||
You have formatting errors in {CYAN}{fn}{ENDC}
|
||||
(Usually these messages are related to indentation.)
|
||||
|
||||
This problem can be fixed with the {CYAN}`--fix`{ENDC} option.
|
||||
Scroll up for more details about {BOLDRED}what you need to fix ^^^{ENDC}.
|
||||
"""
|
||||
)
|
||||
return False
|
||||
return True
|
||||
Reference in New Issue
Block a user