"""Понижение XML-док комментариев с private/internal членов до обычных `//` (код-стайл «Дейл» §5). Правило: XML-doc — только на public/protected. Приватные детали реализации — при необходимости короткий обычный комментарий. Скрипт сохраняет текст (теги / и т.п. разворачиваются в читаемый вид). Режимы: python scripts/fix_private_docs.py --preview [N] # показать N примеров (по умолчанию 10) python scripts/fix_private_docs.py --apply # применить python scripts/fix_private_docs.py --check # сколько блоков будет изменено """ import os import re import sys ROOT = r"C:\telbase\src" EXC = ("bin", "obj", "node_modules", ".git") DOC_RE = re.compile(r"^(\s*)///[ \t]?(.*)$") ACC_RE = re.compile(r"\b(public|protected|private|internal)\b") def demote(block_content): """block_content: список строк после '///'. Возвращает список строк текста обычного комментария.""" t = "\n".join(block_content) t = re.sub(r"\s*(.*?)\s*", r"\1", t, flags=re.S) t = re.sub(r"\s*(.*?)\s*", r"\1", t, flags=re.S) t = re.sub(r"", r"\1", t) t = re.sub(r"", r"\1", t) t = re.sub(r"\s*(.*?)\s*", r"\1: \2", t, flags=re.S) t = re.sub(r"\s*(.*?)\s*", r"\1: \2", t, flags=re.S) t = re.sub(r"\s*(.*?)\s*", r"Возвращает: \1", t, flags=re.S) t = re.sub( r"\s*(.*?)\s*", r"Исключение \1: \2", t, flags=re.S, ) t = re.sub(r"", r"\1", t) t = re.sub(r"", "", t) t = re.sub(r"", "", t) t = re.sub(r"", "", t) t = re.sub(r"<[^>]+>", "", t) out = [] for line in t.split("\n"): s = line.strip() if s: out.append(s) return out def iter_cs(): for dp, dn, fn in os.walk(ROOT): dn[:] = [d for d in dn if d not in EXC] for f in fn: if f.endswith(".cs"): yield os.path.join(dp, f) def find_blocks(lines): """Возвращает список (start, end, block_content, decl_index, decl).""" res = [] i = 0 while i < len(lines): m = DOC_RE.match(lines[i].rstrip("\r")) if not m: i += 1 continue start = i block = [] while i < len(lines): m2 = DOC_RE.match(lines[i].rstrip("\r")) if not m2: break block.append(m2.group(2)) i += 1 end = i # exclusive j = i decl = "" while j < len(lines): s = lines[j].strip() if s == "" or s.startswith("["): j += 1 continue decl = s break if not decl: continue if not any("" in b or "" in b for b in block): continue am = ACC_RE.search(decl) if am and am.group(1) in ("private", "internal"): res.append((start, end, block, decl)) return res def process_file(path, apply): raw = open(path, "rb").read().decode("utf-8") lines = raw.splitlines(keepends=True) plain = [l.rstrip("\r\n") for l in lines] endings = [l[len(l.rstrip("\r\n")):] for l in lines] blocks = find_blocks(plain) if not blocks: return 0, [] changes = [] for start, end, block, decl in blocks: indent = DOC_RE.match(plain[start]).group(1) comments = demote(block) new_lines = [indent + "//" + (" " + c if c else "") for c in comments] changes.append((start, end, new_lines, decl)) # применяем с конца for start, end, new_lines, decl in reversed(changes): ending = endings[start] if endings[start] else (endings[end - 1] if end > start else "\n") replacement = [nl + ending for nl in new_lines] lines[start:end] = replacement if apply: open(path, "w", encoding="utf-8", newline="").write("".join(lines)) return len(changes), [(b[0], b[3], demote(b[2])) for b in [(s, e, bl, d) for s, e, bl, d in blocks]] def main(): if "--preview" in sys.argv: n = 10 idx = sys.argv.index("--preview") if idx + 1 < len(sys.argv) and sys.argv[idx + 1].isdigit(): n = int(sys.argv[idx + 1]) shown = 0 for path in iter_cs(): raw = open(path, "rb").read().decode("utf-8") plain = [l.rstrip("\r\n") for l in raw.split("\n")] for start, end, block, decl in find_blocks(plain): print("FILE:", os.path.relpath(path, r"C:\telbase"), f"line {start + 1}") print(" DECL:", decl[:100]) print(" BEFORE:") for b in plain[start:end]: print(" ", b) print(" AFTER:") for c in demote(block): print(" // " + c) print() shown += 1 if shown >= n: return return apply = "--apply" in sys.argv total = 0 files = 0 for path in iter_cs(): cnt, _ = process_file(path, apply) if cnt: files += 1 total += cnt print(("APPLIED" if apply else "DRY-RUN"), "blocks:", total, "files:", files) if __name__ == "__main__": main()