Add TSF conversion and shared Markdown recognition for mixed function, class, and unit declarations. Extend generation, linting, indexing, examples, and tests around the unified declaration model.
367 lines
12 KiB
Markdown
367 lines
12 KiB
Markdown
# TSL Codegen Toolkit
|
||
|
||
本工具把一个或多个 tsf function、独立 class、完整 unit 转换为统一
|
||
`declarations` json/yaml 录入文件,再生成 TSL API skill 使用的 markdown 文档,
|
||
并根据 markdown 重建统一 API 索引 `function_index.tsv`。
|
||
|
||
## 目录结构
|
||
|
||
```text
|
||
tools/tsl-codegen/
|
||
├─ README.md 使用说明
|
||
├─ STANDARD.md API 文档与录入格式标准
|
||
├─ examples/
|
||
│ ├─ example.yaml yaml 录入例子
|
||
│ └─ example.json json 录入例子
|
||
├─ scripts/
|
||
│ ├─ convert_tsf.py tsf → json/yaml
|
||
│ ├─ generate.py yaml/json → markdown
|
||
│ ├─ lint.py markdown 格式校验
|
||
│ ├─ api_markdown.py lint/index 共用标题模型
|
||
│ └─ build_index.py 重建 13 列 function_index.tsv
|
||
└─ tests/ 工具测试
|
||
```
|
||
|
||
## 使用顺序
|
||
|
||
### 1. 进入仓库根目录
|
||
|
||
先切换到包含 `tools/` 和 `skills/` 的仓库根目录。后续命令都从该目录运行:
|
||
|
||
```bash
|
||
cd /path/to/playbook
|
||
```
|
||
|
||
json 使用 Python 标准库,不需要额外安装解析包。yaml 需要安装 `pyyaml`:
|
||
|
||
```bash
|
||
python -m pip install pyyaml
|
||
```
|
||
|
||
markdown 生成器会强制使用仓库锁定版本的 Prettier。首次使用前安装 Node.js,并在
|
||
仓库根目录安装依赖:
|
||
|
||
```bash
|
||
npm install
|
||
```
|
||
|
||
### 2. 阅读标准
|
||
|
||
先阅读 [`STANDARD.md`](STANDARD.md)。其中定义:
|
||
|
||
- function、class、unit 同级 H2 顶级声明和混合页面的固定层级
|
||
- tsf function、独立 class 和完整 unit 文档块格式
|
||
- yaml/json 统一 `declarations` 判别联合与成员字段
|
||
- 13 列统一 API 索引和完全限定名称
|
||
- 参数表、返回类型和示例代码规则
|
||
|
||
录入文件和生成的 markdown 都必须符合该标准
|
||
|
||
### 3. 准备自己的 yaml 或 json
|
||
|
||
#### 从 tsf 转换
|
||
|
||
转换器接受任意混合输入:function、独立 class 和完整 unit TSF 可以在同一条命令中
|
||
出现。每个文件贡献一个对外 declaration,生成的 `declarations` 严格保持命令行
|
||
输入顺序。页面级 `module` 和 `path` 通过参数统一提供,`--format` 明确选择
|
||
json 或 yaml:
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/convert_tsf.py \
|
||
src/OpenXmlAttribute.tsf \
|
||
src/ParseOpenXml.tsf \
|
||
src/OpenXmlRuntime.tsf \
|
||
--format json \
|
||
--module "OfficeXml / OpenXml" \
|
||
--path officexml/openxml/elements \
|
||
--output tmp/openxml-elements.json
|
||
```
|
||
|
||
生成 yaml 时,把 `--format json` 改为 `--format yaml`,并把输出文件后缀改为
|
||
`.yaml`。混合输入规则不变。
|
||
|
||
查看中文帮助:
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/convert_tsf.py --help
|
||
```
|
||
|
||
转换规则:
|
||
|
||
- function TSF 只转换第一个独立顶层 `function`
|
||
- 独立 class 只公开第一个与文件名一致的 class;后续内部 class 和 private 成员忽略
|
||
- class 开头默认 public;public/protected 进入草稿;类方法只接受 `class function`
|
||
- 类方法没有 `///` 描述时,使用最终声明或修饰符分号后的同一行 `//` 注释作为描述;
|
||
正式 `///` 描述优先,类外实现行和函数体内注释不读取
|
||
- property 类型可选;源码未声明类型时草稿中的 `type` 为空字符串,访问方式仍然必填
|
||
- 完整 unit 只收录 interface 的 function/var/const/class;implementation 全部忽略
|
||
- function、class、unit 可以任意混合;重复 function signature、重复 class/unit
|
||
名称会报错,不同 signature 的 function 重载和跨 kind 同名允许
|
||
- 公开 API 中的 `procedure` 当前不支持
|
||
- 输入支持 utf-8(含 BOM)和 gb18030;输出统一为 utf-8
|
||
- 参数类型和返回类型都是可选的 TSL 注解;转换器从函数签名和 `///` 文档块中尽量
|
||
解析,无法取得时保留空字段,生成待手工完善的录入稿
|
||
- `@param: name {类型} 参数说明` 可以为无类型签名补充参数类型;花括号内的全部内容
|
||
都属于类型;`@returns:` 可以补充返回类型;签名和 `///` 同时提供类型时必须一致
|
||
- 描述、参数类型或说明等编辑性缺口不会阻止转换;类型冲突、文档结构或声明冲突会按
|
||
“文件:行号”报错;全部输入成功后才写输出文件
|
||
- json/yaml 输出补齐各 kind 的固定维护字段:function/method 固定包含
|
||
`desc`、`params`、`returns`,property 固定包含 `desc`、`type`、`params`、`access`,
|
||
field/variable 固定包含 `desc`、`type`,constant 固定包含 `desc`、`type`、`value`;
|
||
参数固定包含 `name`、`type`、`desc`,class/unit 固定包含 `desc`、`members`;缺失值
|
||
使用空字符串或空数组,条件字段只在源码确实存在时输出
|
||
- 生成器把 method `returns`、property/constant `type` 的空字符串按可选字段未填写处理;
|
||
顶级/unit function 返回类型和 field/variable 类型等必填内容仍须补全
|
||
- 输出文件不得与任一输入 tsf 相同
|
||
|
||
#### 手工准备
|
||
|
||
从以下例子选择一种格式:
|
||
|
||
- [`examples/example.yaml`](examples/example.yaml)
|
||
- [`examples/example.json`](examples/example.json)
|
||
|
||
两份完整例子深度等价,同时包含顶级 function、完整 class、class function、
|
||
typed/untyped property、字段、常量、完整 unit 和 interface class 成员
|
||
|
||
例子中的 API 是格式示例,不是真实 TSL API。复制例子到自己的工作目录,再修改
|
||
`module`、`path` 和非空有序 `declarations`。录入根只允许这三个字段;旧
|
||
`functions`、`class`、`unit` 根不兼容,生成器会直接拒绝。混合页面的最小
|
||
完整外形:
|
||
|
||
```json
|
||
{
|
||
"module": "OfficeXml / OpenXml",
|
||
"path": "officexml/openxml/elements",
|
||
"declarations": [
|
||
{
|
||
"kind": "class",
|
||
"name": "OpenXmlAttribute",
|
||
"desc": "表示 OpenXml 属性",
|
||
"members": []
|
||
},
|
||
{
|
||
"kind": "function",
|
||
"name": "ParseOpenXml",
|
||
"signature": "ParseOpenXml(xml)",
|
||
"desc": "解析 OpenXml 文本",
|
||
"params": [
|
||
{"name": "xml", "type": "string", "desc": "OpenXml 文本"}
|
||
],
|
||
"returns": "OpenXmlElement"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
生成的 markdown 使用纯 API 标题和独立声明行。class function 的参数类型进入参数表,
|
||
返回类型写在返回行:
|
||
|
||
```markdown
|
||
# OfficeXml / OpenXml
|
||
|
||
## `OpenXmlAttribute`
|
||
|
||
声明:class
|
||
|
||
表示 OpenXml 属性
|
||
|
||
### `CreateVirtual(position, row_index, _story)`
|
||
|
||
声明:class function
|
||
|
||
创建虚段落
|
||
|
||
可见性:`public`
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
| ----------- | --------- | -------- |
|
||
| `position` | integer | 段落位置 |
|
||
| `row_index` | integer | 行索引 |
|
||
| `_story` | StoryNode | 故事节点 |
|
||
|
||
返回:ParagraphSegment
|
||
```
|
||
|
||
录入文件可放在自己的临时目录,例如:
|
||
|
||
```text
|
||
tmp/my-api.yaml
|
||
tmp/my-api.json
|
||
```
|
||
|
||
一个录入文件对应一个 markdown 叶子页。录入文件不放入 skill;是否长期保留由
|
||
维护者自行决定
|
||
|
||
### 4. 选择 Skill 中的目标位置
|
||
|
||
TSL API skill 的相关目录如下:
|
||
|
||
```text
|
||
skills/tsl-api-reference/
|
||
├─ SKILL.md
|
||
├─ data/
|
||
│ └─ function_index.tsv
|
||
├─ references/
|
||
│ └─ codegen/
|
||
│ ├─ builtin/ playbook 维护
|
||
│ ├─ dotnet/ playbook 维护
|
||
│ └─ project/ 用户项目文档的默认 scope
|
||
│ └─ <module-dir>/<page>.md
|
||
└─ scripts/
|
||
└─ lookup.py
|
||
```
|
||
|
||
markdown 目标路径固定为:
|
||
|
||
```text
|
||
skills/tsl-api-reference/references/codegen/<scope>/<module-dir>/<page>.md
|
||
```
|
||
|
||
- `<scope>`:用户文档默认使用 `project`,也可以自定义单级目录名。`builtin` 和
|
||
`dotnet` 由 playbook 项目维护,不应用于存放用户自己的 API 文档
|
||
- `<module-dir>`:功能分类目录,例如 `base`、`runtime`、`document`
|
||
- `<page>.md`:相关 API 的叶子文档,例如 `array.md`、`elements.md`
|
||
|
||
录入文件的 `module` 是 markdown 一级标题,不是目录名。例如:
|
||
|
||
```text
|
||
module: 我的项目 / 数组
|
||
path: base/array
|
||
目标文件: project/base/array.md
|
||
```
|
||
|
||
查找项目中的现有页面:
|
||
|
||
```bash
|
||
rg --files skills/tsl-api-reference/references/codegen/project
|
||
```
|
||
|
||
生成后可以直接打开目标 markdown 手动阅读。例如:
|
||
|
||
```text
|
||
skills/tsl-api-reference/references/codegen/project/base/array.md
|
||
```
|
||
|
||
### 5. 生成 markdown
|
||
|
||
#### 新建叶子页
|
||
|
||
目标文件不存在时,可以直接生成到 skill。例如:
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/generate.py tmp/my-api.yaml
|
||
```
|
||
|
||
json 使用相同命令:
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/generate.py tmp/my-api.json
|
||
```
|
||
|
||
生成器读取录入文件中的 `path`,默认写入
|
||
`skills/tsl-api-reference/references/codegen/project/<path>.md`。不指定
|
||
`--scope` 时,scope 就是 `project`
|
||
|
||
写入前会自动使用仓库的 `.prettierrc.json` 格式化 markdown,使新页面与现有
|
||
builtin 页面保持一致。未安装 Prettier 或格式化失败时,生成器会停止且不写目标文件
|
||
|
||
需要使用自定义 scope 时,通过 `--scope` 指定单级目录名:
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/generate.py tmp/my-api.json --scope my-project
|
||
```
|
||
|
||
#### 修改现有叶子页
|
||
|
||
生成器会整体覆盖 `path` 对应的页面。只有录入文件包含该页面的全部 API 时才运行
|
||
生成器。只修改现有页面中的少量条目时,应按照 `STANDARD.md` 直接编辑 markdown
|
||
|
||
### 6. 手动检查并校验 markdown
|
||
|
||
以下命令以新建页面
|
||
`skills/tsl-api-reference/references/codegen/project/base/my_api.md` 为例
|
||
|
||
先打开文件,检查页面标题、API 名称、成员层级、参数、返回类型和示例
|
||
|
||
#### 6.1 格式说明
|
||
|
||
通过 `generate.py` 生成的页面已经完成 Prettier 格式化,不需要再次处理。直接手工
|
||
编辑 markdown 后,可以单独格式化目标文件:
|
||
|
||
```bash
|
||
npx --no-install prettier --write skills/tsl-api-reference/references/codegen/project/base/my_api.md
|
||
```
|
||
|
||
#### 6.2 校验
|
||
|
||
校验目标文件:
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/lint.py --file skills/tsl-api-reference/references/codegen/project/base/my_api.md
|
||
```
|
||
|
||
校验整个项目目录:
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/lint.py --dir skills/tsl-api-reference/references/codegen/project
|
||
```
|
||
|
||
使用 `--strict` 时,警告也会导致校验失败
|
||
|
||
校验器将以下问题视为错误:
|
||
|
||
- 缺少描述
|
||
- 顶层/unit function 缺少返回类型
|
||
- 有参数但没有参数表
|
||
- 无参数但存在参数表
|
||
- 参数表不是固定三列,或参数名称/顺序与签名不一致
|
||
- 参数表数据行缺少参数名、类型或说明
|
||
- class member 缺少 visibility
|
||
- property 缺少访问方式,field/variable 缺少类型,constant 缺少值
|
||
- class/unit 成员标题层级、声明形式或子标题错误,或出现 private API
|
||
|
||
以下问题默认作为警告:
|
||
|
||
- 可选参数说明未以 `可选。` 开头
|
||
- tags 行为空
|
||
|
||
### 7. 更新并验证统一 API 索引
|
||
|
||
markdown 确认无误后,重建 TSV:
|
||
|
||
索引固定为 13 列:保留原 8 列并追加 kind、binding、visibility、owner、
|
||
qualified_name。关键词检索会同时匹配原有字段和这些扩展字段。
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/build_index.py --skill-dir skills/tsl-api-reference
|
||
```
|
||
|
||
检查 TSV 是否与 markdown 一致:
|
||
|
||
```bash
|
||
python tools/tsl-codegen/scripts/build_index.py --skill-dir skills/tsl-api-reference --check
|
||
```
|
||
|
||
默认从 `<skill-dir>/references/codegen` 读取 markdown,并写入
|
||
`<skill-dir>/data/function_index.tsv`
|
||
|
||
最后使用简单名称或完全限定名称验证 skill 可以检索到新条目:
|
||
|
||
```bash
|
||
python skills/tsl-api-reference/scripts/lookup.py --name myFunction
|
||
python skills/tsl-api-reference/scripts/lookup.py --name DemoUnit.Document.Save
|
||
```
|
||
|
||
查询简单成员名会返回所有 owner 下的同名 API;`Unit.Class.Member` 等完全限定名称
|
||
用于缩小范围。重载共享完全限定名称,查询会返回全部重载。关键词候选显示
|
||
`class function`、`static field` 等实际声明术语。
|
||
|
||
最终提交:
|
||
|
||
- 新增或修改的 markdown 叶子页
|
||
- `skills/tsl-api-reference/data/function_index.tsv`
|
||
|
||
TSL API skill 只需要 markdown 和 TSV。录入用的 yaml/json 可以由维护者在自己的
|
||
版本库中管理
|