53863559f4
Retarget git remote, install path, and deploy docs from crypto_monitor to crypto_monitor_user. Co-authored-by: Cursor <cursoragent@cursor.com>
118 lines
2.8 KiB
Python
118 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""将全角/易混淆标点规范为半角 ASCII(注释, 文档, 配置模板).
|
|
|
|
不转换弯引号 “ ” ‘ ’,避免破坏 Python/JS 字符串字面量.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parents[1]
|
|
|
|
SKIP_DIRS = frozenset({
|
|
".git",
|
|
".venv",
|
|
"node_modules",
|
|
"__pycache__",
|
|
".cursor",
|
|
"agent-transcripts",
|
|
})
|
|
|
|
SCAN_SUFFIXES = frozenset({
|
|
".py",
|
|
".js",
|
|
".html",
|
|
".md",
|
|
".sh",
|
|
".css",
|
|
".json",
|
|
".cjs",
|
|
".txt",
|
|
".yml",
|
|
".yaml",
|
|
".example",
|
|
})
|
|
|
|
AMBIGUOUS_CHARS = frozenset(
|
|
"\uff08\uff09\uff1a\uff0c\uff1b\uff1f\uff01\u3002\u3001\u00a0"
|
|
)
|
|
|
|
TRANSLATION = str.maketrans(
|
|
{
|
|
"\uff08": "(",
|
|
"\uff09": ")",
|
|
"\uff1a": ":",
|
|
"\uff0c": ",",
|
|
"\uff1b": ";",
|
|
"\uff1f": "?",
|
|
"\uff01": "!",
|
|
"\u3002": ".",
|
|
"\u3001": ",",
|
|
"\u00a0": " ",
|
|
}
|
|
)
|
|
|
|
|
|
def should_scan(path: Path) -> bool:
|
|
if not path.is_file():
|
|
return False
|
|
if any(part in SKIP_DIRS for part in path.parts):
|
|
return False
|
|
if path.name == ".env.example" or path.name.endswith(".env.example"):
|
|
return True
|
|
return path.suffix in SCAN_SUFFIXES
|
|
|
|
|
|
def normalize_text(text: str) -> tuple[str, int]:
|
|
count = sum(1 for ch in text if ch in AMBIGUOUS_CHARS)
|
|
if not count:
|
|
return text, 0
|
|
return text.translate(TRANSLATION), count
|
|
|
|
|
|
def read_text_strip_bom(path: Path) -> tuple[str, bool]:
|
|
raw = path.read_text(encoding="utf-8")
|
|
if raw.startswith("\ufeff"):
|
|
return raw.lstrip("\ufeff"), True
|
|
return raw, False
|
|
|
|
|
|
def iter_targets(root: Path) -> list[Path]:
|
|
return sorted(p for p in root.rglob("*") if should_scan(p))
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Normalize ambiguous Unicode punctuation")
|
|
parser.add_argument("--dry-run", action="store_true")
|
|
parser.add_argument("--root", default=str(REPO))
|
|
args = parser.parse_args()
|
|
|
|
root = Path(args.root)
|
|
files_changed = 0
|
|
chars_changed = 0
|
|
|
|
for path in iter_targets(root):
|
|
try:
|
|
original, had_bom = read_text_strip_bom(path)
|
|
except (OSError, UnicodeDecodeError):
|
|
continue
|
|
normalized, n = normalize_text(original)
|
|
if not n and not had_bom:
|
|
continue
|
|
rel = path.relative_to(root)
|
|
if args.dry_run:
|
|
print(f"[dry-run] {rel}: {n} chars")
|
|
else:
|
|
# 保持原换行风格, 仅替换标点
|
|
path.write_text(normalized, encoding="utf-8", newline="")
|
|
files_changed += 1
|
|
chars_changed += n
|
|
|
|
print(f"done: {files_changed} files, {chars_changed} replacements")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|