import re
import sys
import textwrap
from pathlib import Path
WIDTH = 100
ITEM_INDENT = " " * 13
ITEM_RE = re.compile(r"^\* \[([ x~])\]\s*-+\s*(P[0-3]|\?\?|--)")
def wrap_line(line: str) -> list[str]:
if len(line) <= WIDTH:
return [line]
stripped = line.lstrip(" ")
if stripped.startswith("#") or stripped.startswith("|"):
return [line] if line.startswith("* ["):
subsequent = ITEM_INDENT
else:
subsequent = line[: len(line) - len(stripped)]
wrapped = textwrap.wrap(
line,
width=WIDTH,
subsequent_indent=subsequent,
break_long_words=False,
break_on_hyphens=False,
)
return wrapped or [line]
def format_file(path: Path) -> int:
out: list[str] = []
wrapped_count = 0
in_fence = False
for line in path.read_text().splitlines():
if line.lstrip().startswith("```"):
in_fence = not in_fence
out.append(line)
continue
if in_fence:
out.append(line)
continue
pieces = wrap_line(line)
if len(pieces) > 1:
wrapped_count += 1
out.extend(pieces)
path.write_text("\n".join(out) + "\n")
return wrapped_count
def stats_file(path: Path) -> None:
states = {" ": 0, "x": 0, "~": 0}
open_by_priority: dict[str, int] = {}
for line in path.read_text().splitlines():
m = ITEM_RE.match(line)
if not m:
continue
state, priority = m.groups()
states[state] += 1
if state == " ":
open_by_priority[priority] = open_by_priority.get(priority, 0) + 1
total = sum(states.values())
print(
f"{path}: {total} items: {states['x']} done"
f" · {states['~']} partial · {states[' ']} open"
)
order = ["P0", "P1", "P2", "P3", "??", "--"]
split = " · ".join(
f"{p} {open_by_priority[p]}" for p in order if p in open_by_priority
)
print(f"open by priority: {split or 'none'}")
def main() -> None:
args = sys.argv[1:]
stats = "--stats" in args
paths = [Path(a) for a in args if a != "--stats"] or [Path("ROADMAP.md")]
for path in paths:
if not path.is_file():
sys.exit(f"fmt-roadmap: no such file: {path}")
if stats:
stats_file(path)
else:
n = format_file(path)
print(f"{path}: wrapped {n} over-long line(s)")
if __name__ == "__main__":
main()