From 3d075cea2077cc3a02d7856716bbed6ba5cef96e Mon Sep 17 00:00:00 2001 From: Pierre-Marie Charavel Date: Fri, 27 Feb 2026 10:38:29 -0500 Subject: [PATCH] Skip empty doc files and prune dead type-index entries - render_header() returns "" when a header has no documented content (no /** */ comments on any class, property, function, enum, or delegate) - generate.py skips writing those files and tracks which were written - type-index.txt is filtered to only include types from written files, preventing dead entries that would cause get_class_overview to fail - Summary line now reports how many files were skipped Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 1 + README.md | 4 ++-- docgen/generate.py | 11 ++++++++++- docgen/ue_markdown.py | 18 ++++++++++++------ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 79a2a8e..e334cd6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -55,6 +55,7 @@ Typical query flow: `search_types` → `get_class_overview` → `get_member` (~3 - Deprecated functions excluded from output (`not f.is_deprecated` in visibility filter). - Enums with no value descriptions use compact inline format (`Values: A, B, C`) instead of a table. - Placeholder descriptions (`------//`) filtered by `_PLACEHOLDER_RE` in `_fn_body()`. +- Headers with no documented content: `render_header()` returns `""`, `generate.py` skips writing the file and filters those types from `type-index.txt` to avoid dead entries. ## Critical Gotchas diff --git a/README.md b/README.md index e283d22..13aaa52 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ Generates compact, agent-readable Markdown documentation from Unreal Engine C++ ## How it works 1. **Parse** — `docgen/ue_parser.py` scans UE headers into dataclasses using a position-based scanner (handles nested braces, macros, delegates, namespaces). -2. **Render** — `docgen/ue_markdown.py` emits one `.md` per header: only items with C++ doc comments, no deprecated functions, compact enum format. -3. **Index** — `docgen/generate.py` produces `type-index.txt`: a flat `TypeName: path/to/File.md` lookup for instant type resolution. +2. **Render** — `docgen/ue_markdown.py` emits one `.md` per header: only items with C++ doc comments, no deprecated functions, compact enum format. Headers with no documented content produce no output file. +3. **Index** — `docgen/generate.py` produces `type-index.txt`: a flat `TypeName: path/to/File.md` lookup for instant type resolution. Only types from files that were actually written are indexed. 4. **Serve** — `ue_mcp_server.py` exposes the docs to Claude as callable MCP tools. ## Usage diff --git a/docgen/generate.py b/docgen/generate.py index f49fdd1..dcda56c 100644 --- a/docgen/generate.py +++ b/docgen/generate.py @@ -135,6 +135,8 @@ def main(): # --- Pass 2: render all --- success = 0 + skipped = 0 + written_mds: set[str] = set() for h, base, parsed in parsed_list: print(f"Rendering {h} ...") current_md = _md_rel(h, base) @@ -143,13 +145,20 @@ def main(): try: md = render_header(parsed, type_index=type_index, current_md=current_md) + if not md: + skipped += 1 + continue out_path.write_text(md, encoding='utf-8') + written_mds.add(current_md) success += 1 except Exception as exc: print(f" ERROR rendering {h}: {exc}", file=sys.stderr) + # Remove type-index entries whose files were not written (no documented content) + type_index = {name: path for name, path in type_index.items() if path in written_mds} write_type_index(type_index, output_dir) - print(f"\nGenerated {success}/{len(parsed_list)} files + type-index.txt in {output_dir}/") + print(f"\nGenerated {success}/{len(parsed_list)} files " + f"({skipped} skipped — no documented content) + type-index.txt in {output_dir}/") if __name__ == '__main__': diff --git a/docgen/ue_markdown.py b/docgen/ue_markdown.py index 62bc06b..7084e06 100644 --- a/docgen/ue_markdown.py +++ b/docgen/ue_markdown.py @@ -361,14 +361,13 @@ def _render_namespace(ns: NamespaceInfo) -> str: def render_header(parsed: ParsedHeader, type_index: dict[str, str] = None, current_md: str = "") -> str: + """ + Render a ParsedHeader to Markdown. Returns empty string if the header + has no documented content (so callers can skip writing the file). + """ if type_index is None: type_index = {} - lines = [] - lines.append(f"# `{parsed.filename}`") - lines.append(f"**Module**: `{parsed.module_name}`") - lines.append("") - sections = [] d_sec = _render_delegates(parsed.delegates) @@ -400,6 +399,13 @@ def render_header(parsed: ParsedHeader, ff_lines.append(_render_ff_compact(fn)) sections.append('\n'.join(ff_lines)) - lines.append('\n\n---\n\n'.join(sections)) + if not sections: + return "" + lines = [ + f"# `{parsed.filename}`", + f"**Module**: `{parsed.module_name}`", + "", + '\n\n---\n\n'.join(sections), + ] return '\n'.join(lines)