-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathformat_cpp.py
More file actions
executable file
·112 lines (89 loc) · 2.94 KB
/
format_cpp.py
File metadata and controls
executable file
·112 lines (89 loc) · 2.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env python3
# pyright: strict
"""Format C++ throughout the repo.
Usage:
format_cpp.py Reformat every C++ file (and Markdown example) in place.
format_cpp.py --check Don't change anything. Exit non-zero if any file
is not already formatted.
Requires `clang-format` on PATH. The pre-commit hook supplies one via the
`clang-format` PyPI wheel. Manual invocations can use any system install.
"""
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
from extract_example_code import try_extract_example_code
REPO_ROOT = Path(__file__).resolve().parent.parent
def _git_ls(*patterns: str) -> list[Path]:
result = subprocess.run(
[
"git",
"ls-files",
"--cached",
"--others",
"--exclude-standard",
"-z",
"--",
*patterns,
],
cwd=REPO_ROOT,
capture_output=True,
text=True,
check=True,
)
return sorted(REPO_ROOT / p for p in result.stdout.split("\0") if p)
def _format_stdin(code: str) -> str:
result = subprocess.run(
["clang-format", "--assume-filename=example.cpp"],
input=code,
text=True,
capture_output=True,
check=True,
)
return result.stdout
def _format_static(files: list[Path], *, check: bool) -> bool:
if not files:
return True
args = [str(f) for f in files]
cmd = (
["clang-format", "--dry-run", "--Werror", *args]
if check
else ["clang-format", "-i", *args]
)
return subprocess.run(cmd, cwd=REPO_ROOT).returncode == 0
def _format_markdown(md: Path, *, check: bool) -> bool:
content = md.read_text(encoding="utf-8")
try:
m = try_extract_example_code(content)
except ValueError as e:
raise ValueError(f"'{md.relative_to(REPO_ROOT)}': {e}") from None
if m is None:
return True
original = m.group("code")
formatted = _format_stdin(original).rstrip("\n")
if formatted == original:
return True
if check:
rel = md.relative_to(REPO_ROOT).as_posix()
print(f"{rel}: '## Example' block would be reformatted", file=sys.stderr)
return False
new_content = content[: m.start("code")] + formatted + content[m.end("code") :]
md.write_text(new_content, encoding="utf-8")
return True
def main() -> int:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"--check",
action="store_true",
help="dry-run; exit non-zero if anything would change",
)
args = parser.parse_args()
ok = _format_static(_git_ls("*.h", "*.cpp", "*.ixx"), check=args.check)
for md in _git_ls("*.md"):
ok = _format_markdown(md, check=args.check) and ok
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())