Files

TSL Codegen Toolkit

本工具把一个或多个 tsf function、独立 class、完整 unit 转换为统一 declarations json/yaml 录入文件,再生成 TSL API skill 使用的 markdown 文档, 并根据 markdown 重建统一 API 索引 function_index.tsv

目录结构

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/ 的仓库根目录。后续命令都从该目录运行:

cd /path/to/playbook

json 使用 Python 标准库,不需要额外安装解析包。yaml 需要安装 pyyaml

python -m pip install pyyaml

markdown 生成器会强制使用仓库锁定版本的 Prettier。首次使用前安装 Node.js,并在 仓库根目录安装依赖:

npm install

2. 阅读标准

先阅读 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 严格保持命令行 输入顺序。页面级 modulepath 通过参数统一提供,--format 明确选择 json 或 yaml

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。混合输入规则不变。

查看中文帮助:

python tools/tsl-codegen/scripts/convert_tsf.py --help

转换规则:

  • function TSF 只转换第一个独立顶层 function
  • 独立 class 只公开第一个与文件名一致的 class;后续内部 class 和 private 成员忽略
  • class 开头默认 publicpublic/protected 进入草稿;类方法只接受 class function
  • 类方法没有 /// 描述时,使用最终声明或修饰符分号后的同一行 // 注释作为描述; 正式 /// 描述优先,类外实现行和函数体内注释不读取
  • property 类型可选;源码未声明类型时草稿中的 type 为空字符串,访问方式仍然必填
  • 完整 unit 只收录 interface 的 function/var/const/classimplementation 全部忽略
  • 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 固定包含 descparamsreturnsproperty 固定包含 desctypeparamsaccess field/variable 固定包含 desctypeconstant 固定包含 desctypevalue 参数固定包含 nametypedescclass/unit 固定包含 descmembers;缺失值 使用空字符串或空数组,条件字段只在源码确实存在时输出
  • 生成器把 method returns、property/constant type 的空字符串按可选字段未填写处理; 顶级/unit function 返回类型和 field/variable 类型等必填内容仍须补全
  • 输出文件不得与任一输入 tsf 相同

手工准备

从以下例子选择一种格式:

两份完整例子深度等价,同时包含顶级 function、完整 class、class function、 typed/untyped property、字段、常量、完整 unit 和 interface class 成员

例子中的 API 是格式示例,不是真实 TSL API。复制例子到自己的工作目录,再修改 modulepath 和非空有序 declarations。录入根只允许这三个字段;旧 functionsclassunit 根不兼容,生成器会直接拒绝。混合页面的最小 完整外形:

{
  "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 的参数类型进入参数表, 返回类型写在返回行:

# OfficeXml / OpenXml

## `OpenXmlAttribute`

声明:class

表示 OpenXml 属性

### `CreateVirtual(position, row_index, _story)`

声明:class function

创建虚段落

可见性:`public`

| 参数        | 类型      | 说明     |
| ----------- | --------- | -------- |
| `position`  | integer   | 段落位置 |
| `row_index` | integer   | 行索引   |
| `_story`    | StoryNode | 故事节点 |

返回:ParagraphSegment

录入文件可放在自己的临时目录,例如:

tmp/my-api.yaml
tmp/my-api.json

一个录入文件对应一个 markdown 叶子页。录入文件不放入 skill;是否长期保留由 维护者自行决定

4. 选择 Skill 中的目标位置

TSL API skill 的相关目录如下:

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 目标路径固定为:

skills/tsl-api-reference/references/codegen/<scope>/<module-dir>/<page>.md
  • <scope>:用户文档默认使用 project,也可以自定义单级目录名。builtindotnet 由 playbook 项目维护,不应用于存放用户自己的 API 文档
  • <module-dir>:功能分类目录,例如 baseruntimedocument
  • <page>.md:相关 API 的叶子文档,例如 array.mdelements.md

录入文件的 module 是 markdown 一级标题,不是目录名。例如:

module: 我的项目 / 数组
path: base/array
目标文件: project/base/array.md

查找项目中的现有页面:

rg --files skills/tsl-api-reference/references/codegen/project

生成后可以直接打开目标 markdown 手动阅读。例如:

skills/tsl-api-reference/references/codegen/project/base/array.md

5. 生成 markdown

新建叶子页

目标文件不存在时,可以直接生成到 skill。例如:

python tools/tsl-codegen/scripts/generate.py --file tmp/my-api.yaml

json 使用相同命令:

python tools/tsl-codegen/scripts/generate.py --file tmp/my-api.json

需要批量生成时,将录入文件放在同一目录并使用 --dir

python tools/tsl-codegen/scripts/generate.py --dir tmp/api-recordings

目录模式只处理目录中的直属文件,不进入子目录。默认处理 .json.yaml.yml--format json 只处理 .json--format yaml 只处理 .yaml.yml。所有录入文件会先完成解析、校验和格式化;预检发现任何错误或输出目标冲突 时,生成器会列出对应输入文件和具体原因,并且不写入任何 markdown。

生成器读取录入文件中的 path,默认写入 skills/tsl-api-reference/references/codegen/project/<path>.md。不指定 --scope 时,scope 就是 project

写入前会自动使用仓库的 .prettierrc.json 格式化 markdown,使新页面与现有 builtin 页面保持一致。未安装 Prettier 或格式化失败时,生成器会停止且不写目标文件

需要使用自定义 scope 时,通过 --scope 指定单级目录名:

python tools/tsl-codegen/scripts/generate.py --file 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 后,可以单独格式化目标文件:

npx --no-install prettier --write skills/tsl-api-reference/references/codegen/project/base/my_api.md

6.2 校验

校验目标文件:

python tools/tsl-codegen/scripts/lint.py --file skills/tsl-api-reference/references/codegen/project/base/my_api.md

校验整个项目目录:

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。关键词检索会同时匹配原有字段和这些扩展字段。

python tools/tsl-codegen/scripts/build_index.py --skill-dir skills/tsl-api-reference

检查 TSV 是否与 markdown 一致:

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 可以检索到新条目:

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 functionstatic field 等实际声明术语。

最终提交:

  • 新增或修改的 markdown 叶子页
  • skills/tsl-api-reference/data/function_index.tsv

TSL API skill 只需要 markdown 和 TSV。录入用的 yaml/json 可以由维护者在自己的 版本库中管理