【TRAE技巧便利店】再也不用手写文档了!这个Skill让我从"文档恐惧症"中解脱
作为一个懒人程序员
写代码我很快乐,但写文档?那是另一回事了。
每次项目快结束时要补文档,简直比写代码还痛苦:
- 函数列表要一个个复制粘贴
- 参数说明要手动整理
- README 更是懒得动笔
- 最惨的是代码改了,文档忘了更新
于是我想:能不能让代码自己写文档?
经过几天的摸索,我做出了 doc-generator 这个 Skill,现在分享给大家。
它能做什么
简单说,就是扫描你的代码,自动生成三份文档:
API.md - API 文档
自动提取所有函数和类,生成表格式的文档:
| 内容 | 说明 |
|---|---|
| 函数名 | 自动识别 |
| 参数列表 | 包含类型和默认值 |
| 返回值类型 | 从类型注解提取 |
| 函数说明 | 从 docstring 提取 |
README.md - 项目说明
生成标准的项目结构,包含:
- 项目统计(多少函数、多少类)
- 依赖列表
- 快速开始示例
CHANGELOG.md - 更新日志
自动记录版本历史
怎么用
基础用法
/gendoc ./src
就这么简单!它会扫描 src 目录下的所有代码,然后在 ./docs 目录生成三份文档。
进阶用法
# 只生成 API 文档
/gendoc ./api api
# 指定输出位置
/gendoc ./src all ./my-docs
# 生成英文文档
/gendoc ./src all ./docs en-US
支持的语言
目前支持两种语言:
Python - 提取 docstring、类型注解
JavaScript/TypeScript - 提取 JSDoc、TS 类型
如果你用的是 Go、Java 等其他语言,欢迎提 PR 扩展!
我是怎么实现它的
思路1:正则表达式大法
核心就是用正则表达式匹配代码模式:
# 匹配 Python 函数
func_pattern = r'def\s+(\w+)\s*\((.*?)\)(?:\s*->\s*(.+?))?:'
# 匹配类定义
class_pattern = r'class\s+(\w+)(?:\((.*?)\))?'
看起来很简单,但要注意处理各种边界情况:
- 有类型注解的参数
- 有默认值的参数
- 多行 docstring
思路2:智能注释提取
这个是最有用的部分!
Python 的 docstring:
def hello(name: str) -> str:
"""这是个打招呼函数"""
JavaScript 的 JSDoc:
/**
* 计算两数之和
* @param {number} a - 第一个数
* @param {number} b - 第二个数
* @returns {number} 求和结果
*/
function add(a, b) {
return a + b
}
我都会自动识别并提取到文档中。
思路3:模板化输出
生成文档时用字符串拼接,保持格式统一:
content = f"# {project_name}\n\n"
content += "## 函数列表\n\n"
for func in functions:
content += f"### `{func['name']}`\n"
# ...
生成的文档长这样
API 文档示例
# API 文档
生成时间: 2024-03-10 14:30:00
## 函数列表
### `execute`
**文件**: main.py
**语言**: Python
**描述**: Skill 执行入口函数
**参数**:
| 参数名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| kwargs | dict | - | - |
**返回值**: `Dict[str, Any]`
---
README 示例
# my-project
## 项目简介
(此处添加项目简介)
## 项目统计
- **函数数量**: 15
- **类数量**: 3
- **依赖包**: 8
## 主要依赖
- json
- os
- re
## 快速开始
### 安装
```bash
pip install -r requirements.txt
使用方法
result = execute()
print(result)
---
## 📊 效果对比
| 对比项 | 手写文档 | 使用这个 Skill |
|--------|----------|---------------|
| 时间成本 | 数小时 | 几秒钟 |
| 准确性 | 容易遗漏 | 自动提取 |
| 同步性 | 经常忘记更新 | 随时可重新生成 |
| 格式统一性 | 看心情 | 标准模板 |
---
## 📦 完整代码在这里
### skill.json
```json
{
"name": "doc-generator",
"version": "1.0.0",
"description": "智能文档生成器,根据代码自动生成API文档、README和使用指南",
"author": "TRAE社区",
"tags": ["文档生成", "API文档", "自动化"],
"trigger": {
"type": "command",
"command": "/gendoc"
},
"parameters": {
"type": "object",
"properties": {
"source_path": {
"type": "string",
"description": "源代码路径(文件或目录)"
},
"doc_type": {
"type": "string",
"enum": ["api", "readme", "changelog", "all"],
"default": "all",
"description": "文档类型"
},
"output_dir": {
"type": "string",
"default": "./docs",
"description": "输出目录"
},
"language": {
"type": "string",
"enum": ["zh-CN", "en-US"],
"default": "zh-CN",
"description": "文档语言"
}
},
"required": ["source_path"]
}
}
main.py
"""
文档生成 Skill - 主执行逻辑
功能:自动分析代码结构,生成规范的文档
"""
import os
import re
import json
from typing import Dict, List, Any, Optional
from datetime import datetime
class DocGenerator:
"""文档生成核心类"""
def __init__(self, source_path: str, doc_type: str = "all", output_dir: str = "./docs", language: str = "zh-CN"):
self.source_path = source_path
self.doc_type = doc_type
self.output_dir = output_dir
self.language = language
self.functions = []
self.classes = []
self.imports = []
self.constants = []
def generate(self) -> Dict[str, Any]:
"""主生成方法"""
if not os.path.exists(self.source_path):
return {"error": f"路径不存在: {self.source_path}"}
# 分析代码结构
if os.path.isfile(self.source_path):
self._analyze_file(self.source_path)
else:
self._analyze_directory(self.source_path)
# 创建输出目录
os.makedirs(self.output_dir, exist_ok=True)
results = {"generated_files": []}
# 生成不同类型的文档
if self.doc_type in ["api", "all"]:
api_file = self._generate_api_doc()
results["generated_files"].append(api_file)
if self.doc_type in ["readme", "all"]:
readme_file = self._generate_readme()
results["generated_files"].append(readme_file)
if self.doc_type in ["changelog", "all"]:
changelog_file = self._generate_changelog()
results["generated_files"].append(changelog_file)
return results
def _analyze_file(self, file_path: str):
"""分析单个文件"""
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
filename = os.path.basename(file_path)
if file_path.endswith('.py'):
self._parse_python_file(content, filename)
elif file_path.endswith(('.js', '.ts', '.jsx', '.tsx')):
self._parse_js_file(content, filename)
def _analyze_directory(self, dir_path: str):
"""分析目录"""
for root, dirs, files in os.walk(dir_path):
dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ['node_modules', '__pycache__', 'venv', 'build', 'dist']]
for file in files:
if file.endswith(('.py', '.js', '.ts', '.jsx', '.tsx')):
file_path = os.path.join(root, file)
self._analyze_file(file_path)
def _parse_python_file(self, content: str, filename: str):
"""解析 Python 文件"""
lines = content.split('\n')
# 提取导入
import_pattern = r'^(?:from|import)\s+([^\s]+)'
for line in lines:
match = re.match(import_pattern, line.strip())
if match:
self.imports.append(match.group(1))
# 提取函数
func_pattern = r'def\s+(\w+)\s*\((.*?)\)(?:\s*->\s*(.+?))?:\s*(?:\"\"\"(.*?)\"\"\"|\'\'\'(.*?)\'\'\')?'
for match in re.finditer(func_pattern, content, re.MULTILINE | re.DOTALL):
func_name = match.group(1)
params = match.group(2).strip() if match.group(2) else ""
return_type = match.group(3).strip() if match.group(3) else None
docstring = match.group(4) or match.group(5) or ""
self.functions.append({
"name": func_name,
"params": self._parse_params(params),
"return_type": return_type,
"description": docstring.strip(),
"file": filename,
"language": "Python"
})
# 提取类
class_pattern = r'class\s+(\w+)(?:\((.*?)\))?:\s*(?:\"\"\"(.*?)\"\"\"|\'\'\'(.*?)\'\'\')?'
for match in re.finditer(class_pattern, content, re.MULTILINE | re.DOTALL):
class_name = match.group(1)
inheritance = match.group(2).strip() if match.group(2) else None
docstring = match.group(3) or match.group(4) or ""
self.classes.append({
"name": class_name,
"inheritance": inheritance,
"description": docstring.strip(),
"file": filename,
"language": "Python"
})
def _parse_js_file(self, content: str, filename: str):
"""解析 JavaScript/TypeScript 文件"""
# 提取导入
import_pattern = r'import\s+.*?from\s+[\'"]([^\'"]+)[\'"]'
for match in re.finditer(import_pattern, content):
self.imports.append(match.group(1))
# 提取函数
func_patterns = [
r'function\s+(\w+)\s*\((.*?)\)\s*(?::\s*(.+?))?\s*\{',
r'(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\((.*?)\)\s*(?::\s*(.+?))?\s*=>',
r'export\s+(?:async\s+)?function\s+(\w+)\s*\((.*?)\)\s*(?::\s*(.+?))?\s*\{'
]
for pattern in func_patterns:
for match in re.finditer(pattern, content, re.MULTILINE):
func_name = match.group(1)
params = match.group(2).strip() if match.group(2) else ""
return_type = match.group(3).strip() if match.group(3) else None
jsdoc = self._extract_jsdoc(content, match.start())
self.functions.append({
"name": func_name,
"params": self._parse_js_params(params),
"return_type": return_type,
"description": jsdoc.get("description", ""),
"file": filename,
"language": "JavaScript/TypeScript"
})
# 提取类
class_pattern = r'class\s+(\w+)(?:\s+extends\s+(\w+))?\s*\{'
for match in re.finditer(class_pattern, content):
class_name = match.group(1)
inheritance = match.group(2) if match.group(2) else None
self.classes.append({
"name": class_name,
"inheritance": inheritance,
"description": "",
"file": filename,
"language": "JavaScript/TypeScript"
})
def _parse_params(self, params_str: str) -> List[Dict]:
"""解析函数参数"""
if not params_str.strip():
return []
params = []
for param in params_str.split(','):
param = param.strip()
if not param:
continue
if ':' in param:
name, type_hint = param.split(':', 1)
name = name.strip()
type_hint = type_hint.strip()
else:
name = param
type_hint = None
default_value = None
if '=' in name:
name, default_value = name.split('=', 1)
name = name.strip()
default_value = default_value.strip()
params.append({
"name": name,
"type": type_hint,
"default": default_value
})
return params
def _parse_js_params(self, params_str: str) -> List[Dict]:
"""解析 JavaScript 参数"""
params = []
for param in params_str.split(','):
param = param.strip()
if not param:
continue
if '{' in param or '[' in param:
params.append({"name": param, "type": None, "default": None})
continue
if ':' in param:
name, type_hint = param.split(':', 1)
name = name.strip()
type_hint = type_hint.strip()
else:
name = param
type_hint = None
default_value = None
if '=' in name:
name, default_value = name.split('=', 1)
name = name.strip()
default_value = default_value.strip()
params.append({
"name": name,
"type": type_hint,
"default": default_value
})
return params
def _extract_jsdoc(self, content: str, func_start: int) -> Dict:
"""提取 JSDoc 注释"""
jsdoc = {"description": "", "params": [], "returns": None}
lines_before = content[:func_start].split('\n')
comment_lines = []
for line in reversed(lines_before):
stripped = line.strip()
if stripped.startswith('*') or stripped.startswith('/**') or stripped.startswith('*/'):
comment_lines.insert(0, stripped)
else:
break
if comment_lines:
comment_text = '\n'.join(comment_lines)
desc_match = re.search(r'/\*\*\s*(.*?)\s*(?:@|\*/)', comment_text, re.DOTALL)
if desc_match:
jsdoc["description"] = desc_match.group(1).replace('*', '').strip()
param_matches = re.findall(r'@param\s+(?:\{(.+?)\}\s+)?(\w+)\s*-?\s*(.+)', comment_text)
for type_hint, name, desc in param_matches:
jsdoc["params"].append({"name": name, "type": type_hint, "description": desc.strip()})
return_match = re.search(r'@returns?\s+(?:\{(.+?)\}\s+)?(.+)', comment_text)
if return_match:
jsdoc["returns"] = {"type": return_match.group(1), "description": return_match.group(2).strip()}
return jsdoc
def _generate_api_doc(self) -> str:
"""生成 API 文档"""
output_file = os.path.join(self.output_dir, "API.md")
content = "# API 文档\n\n"
content += f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
if self.functions:
content += "## 函数列表\n\n"
for func in self.functions:
content += f"### `{func['name']}`\n\n"
content += f"**文件**: {func['file']}\n\n"
content += f"**语言**: {func['language']}\n\n"
if func['description']:
content += f"**描述**: {func['description']}\n\n"
if func['params']:
content += "**参数**:\n\n"
content += "| 参数名 | 类型 | 默认值 | 说明 |\n"
content += "|--------|------|--------|------|\n"
for param in func['params']:
param_type = param.get('type') or '-'
default = param.get('default') or '-'
content += f"| {param['name']} | {param_type} | {default} | - |\n"
content += "\n"
if func['return_type']:
content += f"**返回值**: `{func['return_type']}`\n\n"
content += "---\n\n"
if self.classes:
content += "## 类列表\n\n"
for cls in self.classes:
content += f"### `{cls['name']}`\n\n"
content += f"**文件**: {cls['file']}\n\n"
content += f"**语言**: {cls['language']}\n\n"
if cls['inheritance']:
content += f"**继承自**: `{cls['inheritance']}`\n\n"
if cls['description']:
content += f"**描述**: {cls['description']}\n\n"
content += "---\n\n"
with open(output_file, 'w', encoding='utf-8') as f:
f.write(content)
return output_file
def _generate_readme(self) -> str:
"""生成 README 文档"""
output_file = os.path.join(self.output_dir, "README.md")
project_name = os.path.basename(self.source_path) if os.path.isdir(self.source_path) else os.path.basename(self.source_path).rsplit('.', 1)[0]
content = f"# {project_name}\n\n"
content += "## 项目简介\n\n"
content += "(此处添加项目简介)\n\n"
content += "## 项目统计\n\n"
content += f"- **函数数量**: {len(self.functions)}\n"
content += f"- **类数量**: {len(self.classes)}\n"
content += f"- **依赖包**: {len(set(self.imports))}\n\n"
if self.imports:
content += "## 主要依赖\n\n"
unique_imports = sorted(set(self.imports))
for imp in unique_imports[:10]:
content += f"- {imp}\n"
if len(unique_imports) > 10:
content += f"- ... 还有 {len(unique_imports) - 10} 个依赖\n"
content += "\n"
content += "## 快速开始\n\n"
content += "### 安装\n\n"
content += "```bash\n"
content += "# 安装依赖\n"
content += "pip install -r requirements.txt\n"
content += "```\n\n"
content += "### 使用方法\n\n"
content += "```python\n"
if self.functions:
first_func = self.functions[0]
content += f"# 示例: 调用 {first_func['name']} 函数\n"
content += f"result = {first_func['name']}()\n"
content += "print(result)\n"
else:
content += "# 添加使用示例\n"
content += "```\n\n"
content += "## 文档\n\n"
content += "- [API 文档](./API.md)\n"
content += "- [更新日志](./CHANGELOG.md)\n\n"
content += "## 许可证\n\n"
content += "MIT License\n"
with open(output_file, 'w', encoding='utf-8') as f:
f.write(content)
return output_file
def _generate_changelog(self) -> str:
"""生成更新日志"""
output_file = os.path.join(self.output_dir, "CHANGELOG.md")
content = "# 更新日志\n\n"
content += "所有重要的更改都将记录在此文件中。\n\n"
today = datetime.now().strftime('%Y-%m-%d')
content += f"## [{today}] - 初始版本\n\n"
content += "### 新增\n"
content += "- 初始项目结构\n"
if self.functions:
content += f"- 添加 {len(self.functions)} 个函数\n"
if self.classes:
content += f"- 添加 {len(self.classes)} 个类\n"
content += "\n"
with open(output_file, 'w', encoding='utf-8') as f:
f.write(content)
return output_file
def execute(**kwargs):
"""Skill 执行入口函数"""
source_path = kwargs.get("source_path")
doc_type = kwargs.get("doc_type", "all")
output_dir = kwargs.get("output_dir", "./docs")
language = kwargs.get("language", "zh-CN")
generator = DocGenerator(source_path, doc_type, output_dir, language)
result = generator.generate()
if "error" in result:
return {
"status": "error",
"message": result["error"]
}
output = f"""
========================================
文档生成完成
========================================
源代码路径: {source_path}
输出目录: {output_dir}
生成的文档:
"""
for file_path in result["generated_files"]:
output += f" - {file_path}\n"
output += f"""
统计信息:
- 函数: {len(generator.functions)} 个
- 类: {len(generator.classes)} 个
- 依赖: {len(set(generator.imports))} 个
========================================
"""
return {
"status": "success",
"output": output,
"data": result
}
if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
result = execute(source_path=sys.argv[1])
print(result["output"])
README.md
# 文档生成 Skill
自动分析代码结构,生成规范的文档。支持 Python 和 JavaScript/TypeScript。
## 快速开始
\`\`\`bash
/gendoc ./src
\`\`\`
## 支持的文档类型
- API.md - API 文档
- README.md - 项目说明
- CHANGELOG.md - 更新日志
使用建议
- 代码要有注释 - 没有注释的代码生成出来的文档也会很空洞
- 写好类型注解 - TypeScript 或 Python 的类型注解会让文档更详细
- 定期重新生成 - 代码更新后记得重新跑一次
- 人工润色 - 生成的文档是框架,还需要你补充项目背景等信息
最后说两句
这个 Skill 让我省了很多写文档的时间,但不是万能的。好的文档还是需要:
- 清晰的项目说明
- 实际的使用示例
- 常见问题解答
它帮你搞定重复劳动,你负责填充灵魂。
如果觉得有用,欢迎评论区告诉我你还有什么文档相关的痛点!
#TRAE #文档自动化 #效率工具 #懒人必备