From 48dc555a4fc9feaa4c9a9b11258fff54514b93d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=82=E4=B9=8B=E9=92=B3?= Date: Fri, 24 Jul 2026 11:58:09 +0000 Subject: [PATCH] docs: API ref generation script --- docs/gen_api_ref.py | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 docs/gen_api_ref.py diff --git a/docs/gen_api_ref.py b/docs/gen_api_ref.py new file mode 100644 index 0000000..a532d70 --- /dev/null +++ b/docs/gen_api_ref.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Extract Doxygen docs from headers and convert to Markdown.""" +import re, os, glob + +def extract_blocks(text): + results = [] + for m in re.finditer(r'/\*\*(.*?)\*/', text, re.DOTALL): + doc = m.group(1) + after = text[m.end():].lstrip() + first_line = after.split('\n')[0].strip() + while first_line.startswith('#') or first_line.startswith('//') or first_line == '': + parts = after.split('\n', 1) + if len(parts) < 2: break + after = parts[1] + first_line = after.split('\n')[0].strip() + doc_lines = [l.strip()[1:].strip() if l.strip().startswith('*') else l.strip() for l in doc.split('\n')] + results.append(('\n'.join(doc_lines).strip(), first_line)) + return results + +def to_md(doc, sig): + brief, params, ret, notes, sees = '', [], '', [], [] + for line in doc.split('\n'): + line = line.strip() + if line.startswith('@brief '): brief = line[7:] + elif line.startswith('@param '): params.append(line[7:]) + elif line.startswith('@return ') or line.startswith('@retval '): + ret = line.split(' ',1)[1] if ' ' in line else '' + elif line.startswith('@note '): notes.append(f'> {line[6:]}') + elif line.startswith('@see '): sees.append(f'`{line[5:].strip()}`') + elif line and not line.startswith('@') and not line.startswith('\\') and not brief: + brief = line + if not brief: return '' + md = f"**{brief}** \n" + clean = sig.rstrip(';').strip() + if clean and not clean.startswith('#') and not clean.startswith('//') and not clean.startswith('constexpr ') and 'namespace' not in clean and 'class ' not in clean[:6]: + md += f"`{clean}` \n" + if params: + md += "\n| 参数 | 说明 |\n|------|------|\n" + for p in params: + sp = p.split(' ',1) + md += f"| `{sp[0]}` | {sp[1] if len(sp)>1 else ''} |\n" + if ret: md += f"\n**返回**: {ret} \n" + if notes: + for n in notes: md += f"{n} \n" + if sees: md += f"\n**参见**: {', '.join(sees)} \n" + return md + '\n' + +# Main +all_headers = sorted(glob.glob('/ws/ViewDesignEngine/include/vde/**/*.h', recursive=True)) +modules = {} +for h in all_headers: + mod = h.split('/include/vde/')[1].split('/')[0] + modules.setdefault(mod, []).append(h) + +names = { + 'foundation':'基础模块','core':'核心几何','curves':'曲线曲面','mesh':'网格处理', + 'spatial':'空间索引','boolean':'布尔运算','collision':'碰撞检测','brep':'B-Rep建模', + 'sdf':'SDF隐式建模','sketch':'草图约束','capi':'C API' +} + +out = ['# ViewDesignEngine API 参考手册\n\n> 版本 3.2.0 | 76 个头文件\n'] +for mod in ['foundation','core','curves','mesh','spatial','boolean','collision','brep','sdf','sketch','capi']: + if mod not in modules: continue + out.append(f'\n## {names.get(mod,mod)} (`vde::{mod}`)\n') + for h in sorted(modules[mod]): + with open(h) as f: content = f.read() + blocks = extract_blocks(content) + if not blocks: continue + fname = os.path.basename(h) + added = False + for doc, sig in blocks: + if len(doc) < 15 and not sig.strip(): continue + if not added: + out.append(f'\n### `{fname}`\n') + added = True + md = to_md(doc, sig) + if md.strip(): out.append(md) + +result = '\n'.join(out) +with open('/ws/ViewDesignEngine/docs/API-REFERENCE.md', 'w') as f: + f.write(result) +print(f'Written: {len(result)} chars, {result.count(chr(10))} lines')