83 lines
3.5 KiB
Python
83 lines
3.5 KiB
Python
|
|
#!/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')
|