diff --git a/docs/build-manual.py b/docs/build-manual.py index 524bba2..e2ab213 100644 --- a/docs/build-manual.py +++ b/docs/build-manual.py @@ -20,8 +20,14 @@ import generate_component_registry DOCS = Path(__file__).parent MANUAL = DOCS / "manual" FUNCTIONS = DOCS.parent / "functions" +COMPLETIONS = DOCS.parent / "completions" SLUG_DIR = "reference" +# File-tree branches whose real directory contents get listed inline on the +# Starlight site (never in the plain-text manual/man page, since only the +# --site path runs box-drawing trees through _as_file_tree). +EXPANDABLE_TREE_DIRS = {"functions/": FUNCTIONS, "completions/": COMPLETIONS} + def _is_function_page(path: Path, root: Path) -> bool: """True for a Section 5 category stub (not its index).""" @@ -366,6 +372,11 @@ def _as_file_tree(para: list[str]) -> str | None: depth = len(prefix.replace('\t', ' ')) // 4 indent = " " * (depth + 1) out.append(f"{indent}- {name} {desc}".rstrip()) + expand_dir = EXPANDABLE_TREE_DIRS.get(name) + if expand_dir is not None and expand_dir.is_dir(): + child_indent = " " * (depth + 2) + for entry in sorted((p.name for p in expand_dir.iterdir() if p.is_file()), key=str.lower): + out.append(f"{child_indent}- {entry}") out.append("") return "\n".join(out) diff --git a/docs/verify-manual.py b/docs/verify-manual.py index 29b6a9f..7873a6f 100644 --- a/docs/verify-manual.py +++ b/docs/verify-manual.py @@ -828,6 +828,61 @@ def test_as_file_tree_accepts_deeper_trees(): ), f"unexpected file tree output:\n{out}" +def test_as_file_tree_expands_functions_and_completions(): + """The functions/ and completions/ branches list real directory contents.""" + import build_manual + + with tempfile.TemporaryDirectory() as d: + root = Path(d) + functions_dir = root / "functions" + functions_dir.mkdir() + (functions_dir / "zeta.fish").write_text("") + (functions_dir / "Alpha.fish").write_text("") + + completions_dir = root / "completions" + completions_dir.mkdir() + (completions_dir / "bd.fish").write_text("") + + original = dict(build_manual.EXPANDABLE_TREE_DIRS) + build_manual.EXPANDABLE_TREE_DIRS["functions/"] = functions_dir + build_manual.EXPANDABLE_TREE_DIRS["completions/"] = completions_dir + try: + para = [ + "~/.config/fish/", + "├── functions/ Custom functions, one per file", + "└── completions/ Tab completion scripts", + ] + out = build_manual._as_file_tree(para) + finally: + build_manual.EXPANDABLE_TREE_DIRS.clear() + build_manual.EXPANDABLE_TREE_DIRS.update(original) + + assert out == ( + "\n" + "- ~/.config/fish/\n" + " - functions/ Custom functions, one per file\n" + " - Alpha.fish\n" + " - zeta.fish\n" + " - completions/ Tab completion scripts\n" + " - bd.fish\n" + "" + ), f"unexpected file tree output:\n{out}" + + +def test_as_file_tree_leaves_unrelated_branches_unexpanded(): + """Only the mapped directory names get expanded; everything else is untouched.""" + 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 "secrets.fish" in out and "local.fish" in out + assert len(out.splitlines()) == 5, f"unexpected expansion of unrelated branches:\n{out}" + + def test_customization_notes_render_as_aside(): """The real 07-customization NOTE paragraph converts to one intact