feat(docs): add _as_file_tree detector for box-drawing trees
This commit is contained in:
+21
-1
@@ -314,6 +314,26 @@ def _as_ruled_table(para: list[str]) -> str | None:
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
TREE_ROOT_RE = re.compile(r"^[~$][\w./{}-]*/$")
|
||||
TREE_BRANCH_RE = re.compile(r"^[│ ]*[├└]──\s*(\S+)\s*(.*)$")
|
||||
|
||||
|
||||
def _as_file_tree(para: list[str]) -> str | None:
|
||||
"""Render a hand-drawn box-drawing tree as a Starlight <FileTree>, else None."""
|
||||
if len(para) < 2 or not TREE_ROOT_RE.match(para[0].strip()):
|
||||
return None
|
||||
branches = []
|
||||
for line in para[1:]:
|
||||
m = TREE_BRANCH_RE.match(line)
|
||||
if not m:
|
||||
return None
|
||||
branches.append(m.groups())
|
||||
out = ["<FileTree>", f"- {para[0].strip()}"]
|
||||
out += [f" - {name} {desc}".rstrip() for name, desc in branches]
|
||||
out.append("</FileTree>")
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def _render_para(para: list[str], entry_name: str | None, deeper: bool) -> str:
|
||||
"""Render one paragraph of a former indented block.
|
||||
|
||||
@@ -335,7 +355,7 @@ def _render_para(para: list[str], entry_name: str | None, deeper: bool) -> str:
|
||||
title, body = m.group(1), para[1:]
|
||||
info = f'fish title="{title}"' if title else "fish"
|
||||
return f"```{info}\n" + "\n".join(body) + "\n```"
|
||||
table = _as_ruled_table(para) or _as_table(para)
|
||||
table = _as_ruled_table(para) or _as_table(para) or _as_file_tree(para)
|
||||
if table is not None:
|
||||
return table
|
||||
return "\n".join(INDENT + line for line in para)
|
||||
|
||||
@@ -633,6 +633,38 @@ def test_prettify_converts_a_flat_note_paragraph_to_an_aside():
|
||||
), f"note paragraph was not converted:\n{out}"
|
||||
|
||||
|
||||
def test_as_file_tree_converts_a_box_drawing_tree():
|
||||
"""A root path plus ├──/└── branches becomes a Starlight <FileTree>."""
|
||||
import build_manual
|
||||
|
||||
para = [
|
||||
"$__fish_user_dots_path/",
|
||||
"├── secrets.fish API keys, tokens, passwords, personal identifiers",
|
||||
"└── local.fish Machine-specific paths, env vars, and sourcing secrets",
|
||||
]
|
||||
out = build_manual._as_file_tree(para)
|
||||
assert out == (
|
||||
"<FileTree>\n"
|
||||
"- $__fish_user_dots_path/\n"
|
||||
" - secrets.fish API keys, tokens, passwords, personal identifiers\n"
|
||||
" - local.fish Machine-specific paths, env vars, and sourcing secrets\n"
|
||||
"</FileTree>"
|
||||
), f"unexpected file tree output:\n{out}"
|
||||
|
||||
|
||||
def test_as_file_tree_rejects_non_trees():
|
||||
"""Returning None is always safe for anything that isn't a root+branches shape."""
|
||||
import build_manual
|
||||
|
||||
cases = {
|
||||
"no root slash": ["$__fish_user_dots_path", "├── secrets.fish"],
|
||||
"single line": ["$__fish_user_dots_path/"],
|
||||
"non-branch second line": ["$__fish_user_dots_path/", "secrets.fish"],
|
||||
}
|
||||
for label, para in cases.items():
|
||||
assert build_manual._as_file_tree(para) is None, f"{label} was wrongly treed"
|
||||
|
||||
|
||||
def test_prettify_is_site_only():
|
||||
"""The SSOT keeps the indented form the man-page pipeline depends on."""
|
||||
import build_manual
|
||||
|
||||
Reference in New Issue
Block a user