docs(api): add gaussian splat pages and fix doc generators

This commit is contained in:
2026-04-10 17:12:55 +08:00
parent 66ae9ec919
commit f917040e9a
8 changed files with 246 additions and 104 deletions

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
import re
from dataclasses import dataclass
@@ -10,7 +11,11 @@ from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
DOC_ROOT = SCRIPT_DIR.parent if SCRIPT_DIR.name == "_tools" else SCRIPT_DIR
REPO_ROOT = DOC_ROOT.parents[1]
INCLUDE_ROOT = REPO_ROOT / "engine" / "include" / "XCEngine"
DEFAULT_ROOT_NAME = "XCEngine"
DEFAULT_INCLUDE_ROOT = REPO_ROOT / "engine" / "include" / DEFAULT_ROOT_NAME
INCLUDE_ROOT = DEFAULT_INCLUDE_ROOT
CANONICAL_ROOT_NAME = DEFAULT_ROOT_NAME
CANONICAL_DOC_ROOT = DOC_ROOT / CANONICAL_ROOT_NAME
MAIN_INDEX = DOC_ROOT / "main.md"
@@ -21,7 +26,7 @@ def dir_index_name(doc_dir: Path) -> str:
def dir_index_path(doc_dir: Path) -> Path:
return doc_dir / dir_index_name(doc_dir)
MODULES = (
DEFAULT_MODULES = (
"Audio",
"Components",
"Core",
@@ -34,6 +39,7 @@ MODULES = (
"Scene",
"Threading",
)
MODULES = DEFAULT_MODULES
TYPE_START_RE = re.compile(
r"^(class|struct|enum class|enum)\s+([A-Za-z_]\w*)(?:\s*:\s*public\s+([^{;]+))?"
)
@@ -626,7 +632,7 @@ def infer_page_type(stem: str, primary: Declaration | None) -> str:
def scope_label(relative_parts: tuple[str, ...]) -> str:
if not relative_parts:
return "当前模块"
return "`XCEngine/" + "/".join(relative_parts) + "` 子目录"
return f"`{CANONICAL_ROOT_NAME}/" + "/".join(relative_parts) + "` 子目录"
def infer_description(stem: str, relative_parts: tuple[str, ...]) -> str:
@@ -637,11 +643,20 @@ def relative_link(from_path: Path, to_path: Path) -> str:
return os.path.relpath(to_path, from_path.parent).replace("\\", "/")
def canonical_header_reference(header_path: Path) -> str:
parts = list(header_path.parts)
if "include" in parts:
include_index = parts.index("include")
if include_index + 1 < len(parts):
return Path(*parts[include_index + 1 :]).as_posix()
return header_path.relative_to(INCLUDE_ROOT.parent).as_posix()
def build_page(header_path: Path) -> tuple[str, dict[str, str]]:
relative_header = header_path.relative_to(INCLUDE_ROOT.parent).as_posix()
relative_header = canonical_header_reference(header_path)
relative_doc_dir = header_path.parent.relative_to(INCLUDE_ROOT)
stem = header_path.stem
doc_path = DOC_ROOT / "XCEngine" / relative_doc_dir / stem / f"{stem}.md"
doc_path = CANONICAL_DOC_ROOT / relative_doc_dir / stem / f"{stem}.md"
lines = header_path.read_text(encoding="utf-8", errors="ignore").splitlines()
namespace_map = build_namespace_map(lines)
@@ -792,11 +807,45 @@ def refresh_dir_index(include_dir: Path, doc_dir: Path) -> None:
def main() -> int:
parser = argparse.ArgumentParser(description="Generate canonical API pages from public headers.")
parser.add_argument(
"--root-name",
default=DEFAULT_ROOT_NAME,
help="Canonical doc root name under docs/api, e.g. XCEngine or XCEditor.",
)
parser.add_argument(
"--include-root",
default=str(DEFAULT_INCLUDE_ROOT),
help="Source include root mirrored by the canonical doc tree.",
)
parser.add_argument(
"--modules",
nargs="*",
default=None,
help="Optional explicit top-level module list. Defaults to legacy XCEngine modules or auto-detected directories for other roots.",
)
args = parser.parse_args()
global INCLUDE_ROOT, CANONICAL_ROOT_NAME, CANONICAL_DOC_ROOT, MODULES
INCLUDE_ROOT = Path(args.include_root).resolve()
CANONICAL_ROOT_NAME = args.root_name
CANONICAL_DOC_ROOT = DOC_ROOT / CANONICAL_ROOT_NAME
if args.modules:
MODULES = tuple(args.modules)
elif INCLUDE_ROOT == DEFAULT_INCLUDE_ROOT and CANONICAL_ROOT_NAME == DEFAULT_ROOT_NAME:
MODULES = DEFAULT_MODULES
else:
MODULES = tuple(
path.name
for path in sorted(INCLUDE_ROOT.iterdir())
if path.is_dir()
)
generated = 0
method_generated = 0
for module in MODULES:
include_module_dir = INCLUDE_ROOT / module
doc_module_dir = DOC_ROOT / "XCEngine" / module
doc_module_dir = CANONICAL_DOC_ROOT / module
headers = sorted(include_module_dir.rglob("*.h"))
for header in headers:
stem = header.stem

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import os
from dataclasses import dataclass
from pathlib import Path
@@ -9,11 +10,12 @@ from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
API_ROOT = SCRIPT_DIR.parent if SCRIPT_DIR.name == "_tools" else SCRIPT_DIR
REPO_ROOT = API_ROOT.parents[1]
INCLUDE_ROOT = REPO_ROOT / "engine" / "include" / "XCEngine"
DOC_ROOT = API_ROOT / "XCEngine"
DEFAULT_ROOT_NAME = "XCEngine"
DEFAULT_INCLUDE_ROOT = REPO_ROOT / "engine" / "include" / DEFAULT_ROOT_NAME
DESCRIPTION_MAP = {
DESCRIPTION_MAPS = {
"XCEngine": {
"XCEngine": "与 `engine/include/XCEngine` 平行的 API 文档根目录。",
"XCEngine/Audio": "音频系统、混音器、效果器与后端接口。",
"XCEngine/Components": "组件系统与游戏对象 API。",
@@ -38,6 +40,26 @@ DESCRIPTION_MAP = {
"XCEngine/RHI/OpenGL": "OpenGL 后端 public headers。",
"XCEngine/Scene": "场景与场景管理器 API。",
"XCEngine/Threading": "线程封装、同步原语和任务系统。",
},
"XCEditor": {
"XCEditor": "与 `new_editor/include/XCEditor` 平行的 API 文档根目录。",
"XCEditor/Collections": "Editor 集合视图与滚动/重命名/标签条等组合控件协议。",
"XCEditor/Fields": "Editor inspector 字段控件、交互状态机与 property-grid 协议。",
"XCEditor/Foundation": "Editor 命令、快捷键与主题等基础协作契约。",
"XCEditor/Shell": "Editor 工作区、panel、dock、viewport 与 menu 壳层装配协议。",
"XCEditor/Widgets": "Editor 专用绘制/布局辅助与通用 widget primitive helper。",
},
}
NAMESPACE_MAPS = {
"XCEditor": {
"XCEditor": "XCEngine::UI::Editor",
"XCEditor/Collections": "XCEngine::UI::Editor::Widgets",
"XCEditor/Fields": "XCEngine::UI::Editor::Widgets",
"XCEditor/Foundation": "XCEngine::UI::Editor",
"XCEditor/Shell": "XCEngine::UI::Editor",
"XCEditor/Widgets": "XCEngine::UI::Editor::Widgets",
},
}
@@ -46,48 +68,64 @@ class DirInfo:
include_dir: Path
doc_dir: Path
relative: str
root_name: str
namespace: str
type_label: str
description: str
def to_namespace(relative: str) -> str:
if relative == "XCEngine":
return "XCEngine"
def to_namespace(relative: str, root_name: str) -> str:
mapped = NAMESPACE_MAPS.get(root_name, {}).get(relative)
if mapped:
return mapped
if relative == root_name:
return root_name
return "::".join(relative.split("/"))
def get_type_label(relative: str) -> str:
if relative == "XCEngine":
def get_type_label(relative: str, root_name: str) -> str:
if relative == root_name:
return "module-root"
if relative.count("/") == 1:
return "module"
return "submodule"
def get_description(relative: str) -> str:
return DESCRIPTION_MAP.get(relative, "与当前 public headers 平行的文档目录节点。")
def get_description(relative: str, root_name: str) -> str:
return DESCRIPTION_MAPS.get(root_name, {}).get(
relative,
"与当前 public headers 平行的文档目录节点。",
)
def iter_include_dirs() -> list[DirInfo]:
dirs = [INCLUDE_ROOT]
dirs.extend(path for path in sorted(INCLUDE_ROOT.rglob("*")) if path.is_dir())
def iter_include_dirs(
include_root: Path,
doc_root: Path,
root_name: str,
) -> list[DirInfo]:
dirs = [include_root]
dirs.extend(path for path in sorted(include_root.rglob("*")) if path.is_dir())
items: list[DirInfo] = []
for include_dir in dirs:
relative = "XCEngine"
if include_dir != INCLUDE_ROOT:
relative = "XCEngine/" + include_dir.relative_to(INCLUDE_ROOT).as_posix()
relative = root_name
if include_dir != include_root:
relative = root_name + "/" + include_dir.relative_to(include_root).as_posix()
doc_dir = DOC_ROOT if include_dir == INCLUDE_ROOT else DOC_ROOT / include_dir.relative_to(INCLUDE_ROOT)
doc_dir = (
doc_root
if include_dir == include_root
else doc_root / include_dir.relative_to(include_root)
)
items.append(
DirInfo(
include_dir=include_dir,
doc_dir=doc_dir,
relative=relative,
namespace=to_namespace(relative),
type_label=get_type_label(relative),
description=get_description(relative),
root_name=root_name,
namespace=to_namespace(relative, root_name),
type_label=get_type_label(relative, root_name),
description=get_description(relative, root_name),
)
)
return items
@@ -116,8 +154,8 @@ def build_dir_index(info: DirInfo) -> str:
index_path = dir_index_path(info.doc_dir)
title = info.relative.split("/")[-1]
if info.relative == "XCEngine":
title = "XCEngine API 平行目录"
if info.relative == info.root_name:
title = f"{info.root_name} API 平行目录"
lines: list[str] = []
lines.append(f"# {title}")
@@ -156,7 +194,7 @@ def build_dir_index(info: DirInfo) -> str:
lines.append("## 相关文档")
lines.append("")
if info.relative == "XCEngine":
if info.relative == info.root_name:
lines.append("- [API 总索引](../main.md)")
lines.append("- [API 文档重构状态](../_meta/rebuild-status.md)")
else:
@@ -167,12 +205,28 @@ def build_dir_index(info: DirInfo) -> str:
def main() -> int:
DOC_ROOT.mkdir(parents=True, exist_ok=True)
infos = iter_include_dirs()
parser = argparse.ArgumentParser(description="Scaffold parallel API directory index pages.")
parser.add_argument(
"--root-name",
default=DEFAULT_ROOT_NAME,
help="Canonical doc root name under docs/api, e.g. XCEngine or XCEditor.",
)
parser.add_argument(
"--include-root",
default=str(DEFAULT_INCLUDE_ROOT),
help="Source include root mirrored by the canonical doc tree.",
)
args = parser.parse_args()
include_root = Path(args.include_root).resolve()
doc_root = API_ROOT / args.root_name
doc_root.mkdir(parents=True, exist_ok=True)
infos = iter_include_dirs(include_root, doc_root, args.root_name)
for info in infos:
info.doc_dir.mkdir(parents=True, exist_ok=True)
dir_index_path(info.doc_dir).write_text(build_dir_index(info), encoding="utf-8")
print(f"Generated {len(infos)} directory index files under {DOC_ROOT}")
print(f"Generated {len(infos)} directory index files under {doc_root}")
return 0