docs
This commit is contained in:
@@ -10,18 +10,13 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import html
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
BUSINESS = ROOT / "business"
|
||||
SOURCE = ROOT.parent / "docs_source" / "business"
|
||||
TARGETS = (
|
||||
"lots-and-quantities.md",
|
||||
"lots-and-quantities.en.md",
|
||||
)
|
||||
|
||||
|
||||
MATERIAL_ICON = re.compile(r":material-[a-z0-9-]+:\s*")
|
||||
|
||||
|
||||
@@ -190,6 +185,56 @@ def render_callout(lines: list[str]) -> str:
|
||||
)
|
||||
|
||||
|
||||
def is_bullet_line(line: str) -> bool:
|
||||
return bool(re.match(r"^\s*-\s+", line))
|
||||
|
||||
|
||||
def bullet_level(line: str) -> int:
|
||||
return len(re.match(r"^\s*", line).group(0)) // 2
|
||||
|
||||
|
||||
def render_list(lines: list[str]) -> str:
|
||||
root: list[dict[str, object]] = []
|
||||
stack: list[tuple[int, list[dict[str, object]]]] = [(-1, root)]
|
||||
last_item: dict[str, object] | None = None
|
||||
|
||||
for line in lines:
|
||||
if is_bullet_line(line):
|
||||
level = bullet_level(line)
|
||||
content = re.sub(r"^\s*-\s+", "", line).strip()
|
||||
while stack[-1][0] >= level:
|
||||
stack.pop()
|
||||
item: dict[str, object] = {
|
||||
"content": content,
|
||||
"children": [],
|
||||
}
|
||||
stack[-1][1].append(item)
|
||||
stack.append((level, item["children"]))
|
||||
last_item = item
|
||||
elif line.strip() and last_item is not None:
|
||||
last_item["content"] = str(last_item["content"]) + " " + line.strip()
|
||||
|
||||
def render_items(items: list[dict[str, object]], level: int = 0) -> str:
|
||||
margin = "1.1rem" if level == 0 else "1.35rem"
|
||||
bullet = "disc" if level == 0 else "circle"
|
||||
parts = [
|
||||
f'<ul style="margin:0.65rem 0 1rem {margin}; padding-left:1rem; list-style-type:{bullet};">'
|
||||
]
|
||||
for item in items:
|
||||
children = item["children"]
|
||||
parts.append(
|
||||
'<li style="margin:0.38rem 0;">'
|
||||
f"{render_inline(str(item['content']))}"
|
||||
)
|
||||
if children:
|
||||
parts.append(render_items(children, level + 1))
|
||||
parts.append("</li>")
|
||||
parts.append("</ul>")
|
||||
return "\n".join(parts)
|
||||
|
||||
return render_items(root)
|
||||
|
||||
|
||||
def render_source(text: str) -> str:
|
||||
lines = text.splitlines()
|
||||
out = [
|
||||
@@ -244,20 +289,47 @@ def render_source(text: str) -> str:
|
||||
out.extend(table)
|
||||
continue
|
||||
|
||||
if is_bullet_line(line):
|
||||
list_lines = []
|
||||
while i < len(lines) and (
|
||||
is_bullet_line(lines[i])
|
||||
or (
|
||||
lines[i].startswith(" ")
|
||||
and lines[i].strip()
|
||||
and not lines[i].lstrip().startswith("|")
|
||||
)
|
||||
):
|
||||
list_lines.append(lines[i])
|
||||
i += 1
|
||||
out.append(render_list(list_lines))
|
||||
continue
|
||||
|
||||
out.append(line)
|
||||
i += 1
|
||||
|
||||
return "\n".join(out).strip() + "\n"
|
||||
|
||||
|
||||
def render_all(normalize: bool = False) -> None:
|
||||
def source_files() -> list[Path]:
|
||||
return sorted(path for path in SOURCE.rglob("*.md") if path.is_file())
|
||||
|
||||
|
||||
def render_all(normalize: bool = False, check: bool = False) -> bool:
|
||||
SOURCE.mkdir(parents=True, exist_ok=True)
|
||||
for name in TARGETS:
|
||||
source = SOURCE / name
|
||||
target = BUSINESS / name
|
||||
ok = True
|
||||
for source in source_files():
|
||||
target = BUSINESS / source.relative_to(SOURCE)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
if normalize:
|
||||
source.write_text(normalize_source(source.read_text(encoding="utf-8")), encoding="utf-8")
|
||||
target.write_text(render_source(source.read_text(encoding="utf-8")), encoding="utf-8")
|
||||
rendered = render_source(source.read_text(encoding="utf-8"))
|
||||
if check:
|
||||
if not target.exists() or target.read_text(encoding="utf-8") != rendered:
|
||||
print(f"Outdated generated doc: {target.relative_to(ROOT.parent)}")
|
||||
ok = False
|
||||
else:
|
||||
target.write_text(rendered, encoding="utf-8")
|
||||
return ok
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -267,8 +339,16 @@ def main() -> None:
|
||||
action="store_true",
|
||||
help="Clean existing source files from wiki-only syntax before rendering.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--check",
|
||||
action="store_true",
|
||||
help="Fail if generated wiki files are not up to date.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
render_all(normalize=args.normalize_source)
|
||||
if args.normalize_source and args.check:
|
||||
parser.error("--normalize-source cannot be used with --check")
|
||||
if not render_all(normalize=args.normalize_source, check=args.check):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user