📝 docs(code-reader): 规格补上 interface 的属性表卡片

This commit is contained in:
cruldra committed 2026-10-16 14:27:28 +11:00
1 parent 6b1e6f3487
commit f2f647553d
1 file changed
+75 -10
+75 -10
View File
@@ -41,6 +41,7 @@
| 输入参数 | 排成带表头和边框的表格:名称 \| 类型 \| 默认值,表头写了「默认值」,格子里不再写「默认 」 |
| 库规则 | 第四种插件:按库认出一种写法,把那块整个换成专门的卡片(内置 SQLAlchemy 规则:模型类 → 「数据表」卡片;内置 Celery 规则:`Celery(...)` → 「Celery 应用」卡片、`beat_schedule` → 「定时任务」卡片;内置 dataclasses 规则:数据类 → 「数据类」卡片;内置 FastAPI 规则:挂在路由对象上的函数 → 「接口」卡片,请求、响应各一张表),或把那块认成契约里现成的一种属性表块(内置 enum 规则:Python 自带 enum 库的类 → 枚举块)。和语言插件、翻译插件一样写代码注册、只经契约拿信息;文件导入了规则关心的库才启用,由宿主按语言插件给的导入判断;卡片的用词归规则自己,认成的现成块照常由翻译插件排,webview 只画结构。见「库规则」 |
| 枚举 | 枚举是一种块(种类「枚举」,紫色 #b180d7 / 亮色 #652d90),属性表:名称 / 说明 / 访问级别 / 装饰器 / 继承(有才显示)/ 成员 N 个,成员排成表格「成员 \| 值 \| 说明」。TypeScript 的 `enum` 是语言自己的写法,由语言插件报;Python 没有枚举语法,自带 enum 库的写法由库规则认成同一种块。两边同一种块、同一张表、同一个筛选种类,webview 只有一种画法 |
| 接口 | TypeScript 的 `interface`(含 `declare interface`)是一种块(种类「接口」,图标、颜色同类型块),属性表:名称 / 说明 / 访问级别 / 类型参数 / 继承(有才显示)/ 属性 N 个,属性排成表格「属性 \| 类型 \| 说明」,索引签名 `[key: string]: T` 也是一行。方法签名照常是子块(函数卡片),调用签名、构造签名照旧是一行译文。见「接口」 |
| 异常 | 异常类是一种块(种类「异常」,红色 #f14c4c / 亮色 #cd3131,和「捕获」同一色系),属性表:名称 / 说明 / 访问级别 / 装饰器 / 继承 / 属性 N 个(表格「属性 \| 类型 \| 值 \| 说明」)/ 构造参数 N 个(表格同函数的输入参数),有才显示。异常是语言自己的概念,Python、TypeScript 都由语言插件认:父类是语言自带的异常类、同一文件里认出来的异常类,或名字按命名习惯以 Error / Exception(Python 还有 Warning)结尾。方法(含构造函数)照常是子块、默认收起。见「异常」 |
| 规则自己查定义 | 规则可以把自己给的名字标成「引用」(Celery 任务表里的任务名),⌘ / Ctrl 悬停、点击这样的名字时由规则查定义(Celery 在整个工作区里找任务函数),其余名字照旧问语言扩展;webview 只有一条链接 / 跳转的路,样子、闪、挑选框、在预览里打开都一样。规则要在全工作区找东西时只描述要找什么(`WorkspaceIndex`),找文件、解析、缓存、跟着改动更新归宿主。见「规则自己查定义」 |
| 中文名 | 代码里给定义起的中文名(`Annotated[T, Comment("当前用户")]`、`@Comment("…")`、行尾 `# @Comment 当前用户`,没有时取参数在 docstring / JSDoc 里的说明):预览里这个定义和用到它的各处都显示中文名,悬停看原名,定义处本身在中文名后面淡淡写原名。语言插件认写法,用到它的各处由语言扩展按语义认(同一文件问 document highlights,别的文件定义的先查工作区索引、再问定义提供者),webview 只按源码下标换字。见「中文名」 |
@@ -153,7 +154,7 @@
### 预览(代码块树)
- 每一句是一块:导入、函数、类、枚举、异常、类型、字段、常量、变量、条件、否则、循环、尝试、捕获、最终、返回、语句、文件开头的说明(种类「注释」);Shell 脚本另有解释器、选项、分情况、命令、管道(见「Shell 脚本」);Dockerfile 另有解析指令、阶段、执行、复制、设置、标签、健康检查、启动(见「Dockerfile」)。
- 每一句是一块:导入、函数、类、接口、枚举、异常、类型、字段、常量、变量、条件、否则、循环、尝试、捕获、最终、返回、语句、文件开头的说明(种类「注释」);Shell 脚本另有解释器、选项、分情况、命令、管道(见「Shell 脚本」);Dockerfile 另有解析指令、阶段、执行、复制、设置、标签、健康检查、启动(见「Dockerfile」)。
- 嵌套的块放在父块下面、往右缩进;每一层左边一条竖线(颜色 `--vscode-editorIndentGuide-background`)。
- 块的组成(从左到右):折叠箭头(只有有子块的块才显示,其余占位对齐)、概念图标、内容、右上角一排图标按钮。
- 概念图标(原型 `kindBadge`、画板「Prototype / 块种类图标」):块左边横排这块认出来的概念,各一个图标、不带字,和一行字等高(20px 的框,图形 16px,lucide 的线宽 2.2),间距紧。每个图标悬停自己的名字(`data-tooltip`,见下「悬停提示」);整排是一个 `role="img"`,读屏把名字用顿号连起来念。和折叠箭头、内容同一行顶对齐。webview 不自己用种类查图标:图标名由宿主放进预览数据的概念列表(`BlockView.concepts`),webview 只按名字画(`kindIcons.tsx` 的 `KIND_ICON_COMPONENTS`)。
@@ -175,7 +176,7 @@
| enum 枚举 | `list-ordered` | else 否则 | `corner-down-right` | statement 语句 | `code` |
| exception 异常 | `triangle-alert` | loop 循环 | `repeat` | comment 注释 | `message-square-text` |
| interpreter 解释器 | `terminal` | options 选项 | `sliders-horizontal` | case 分情况 | `signpost` |
| command 命令 | `square-terminal` | pipeline 管道 | `square-terminal` | | |
| command 命令 | `square-terminal` | pipeline 管道 | `square-terminal` | interface 接口 | `braces` |
| directive 解析指令 | `terminal` | stage 阶段 | `layers` | run 执行 | `square-terminal` |
| copy 复制 | `copy` | setting 设置 | `sliders-horizontal` | label 标签 | `tag` |
| health 健康检查 | `activity` | start 启动 | `play` | trait 特质 | `network` |
@@ -183,13 +184,13 @@
库规则的种类用规则给的图标(`KindBadge.icon`),跟在语言的种类图标后面:SQLAlchemy 数据表 `database`、数据类 `package`、Pydantic 数据模型 `braces`(和类型块同一个图标,颜色不同)、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`、React 组件 `atom`、状态 `toggle-right`、引用 `anchor`、数据源 `download`、派生值 `sigma`、副作用 `zap`、事件处理函数 `mouse-pointer-click`、渲染输出 `layout-template`、serde 可序列化 `braces`、thiserror 错误 `triangle-alert`、axum 接口 `globe`、sqlx 查询 `database`、tauri 桌面命令 `square-function`。数据类写了 `frozen=True` 再加一把锁(`lock`,悬停「不可修改」)。实现块、特质块再由规则 `rust.impl`、`rust.trait` 换成卡片,左边因此有两个「实现」「特质」图标。
这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 26 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置、编译属性共用一个,字段、标签共用一个——加库规则用的 14 个(React 的组件、状态、引用、数据源、派生值、副作用、事件处理函数、渲染输出的 8 个也在里面),lucide 的 kebab-case 名字),类型 `KindIconName` 是它们的联合,概念的 `icon` 只能写其中一个,写别的是类型错误。内置种类 → 图标名在 `contract.ts` 的 `KIND_ICONS`(宿主写进概念列表)。webview 给每个名字配一个逐个 import 的 lucide 图标组件(`kindIcons.tsx` 的 `KIND_ICON_COMPONENTS`,打包只带这些,预览脚本约 157 kB);webview 的 `KindIconName` 照契约手抄,`kindIconNames.test.ts` 核对两边一样。规则要新图标就把名字加进这组、在 webview 配上图标,没有按任意名字找图标的路。
这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 26 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置、编译属性共用一个,字段、标签共用一个,类型、接口共用一个——加库规则用的 14 个(React 的组件、状态、引用、数据源、派生值、副作用、事件处理函数、渲染输出的 8 个也在里面),lucide 的 kebab-case 名字),类型 `KindIconName` 是它们的联合,概念的 `icon` 只能写其中一个,写别的是类型错误。内置种类 → 图标名在 `contract.ts` 的 `KIND_ICONS`(宿主写进概念列表)。webview 给每个名字配一个逐个 import 的 lucide 图标组件(`kindIcons.tsx` 的 `KIND_ICON_COMPONENTS`,打包只带这些,预览脚本约 157 kB);webview 的 `KindIconName` 照契约手抄,`kindIconNames.test.ts` 核对两边一样。规则要新图标就把名字加进这组、在 webview 配上图标,没有按任意名字找图标的路。
- 右上角的图标按钮(原型组件 `Reader / BlockTools`,普通块、属性表块、库规则的卡片共用):只有图标、不带字,名字写在悬停提示和 `aria-label` 里;整排是 `role="group"`(`aria-label="块操作"`)。最前面是「按注释规范重写」(注释图标,块还没有注释时叫「按注释规范写注释」,重写过、记录收着时叫「展开重写记录」;不能写注释的块没有,见「重写注释」),然后是「问 AI」(sparkles 图标,点开 / 收起块下面的问答区,展开时带按下的底色、`aria-expanded`;文件不在工作区文件夹里时没有,见「问 AI」),再是「编辑 / 预览」切换:预览时铅笔「编辑」,编辑时眼睛「预览」、带按下的底色(`--vscode-toolbar-activeBackground`)。按钮宽 24px、高 20px(和种类图标同高),图标 14px、`descriptionForeground` 色;悬停 `--vscode-toolbar-hoverBackground`,键盘聚焦描 1px `--vscode-focusBorder`;Tab 能走到,回车 / 空格切换。
- 悬停提示(整个插件的网页只走这一条路,预览和看板共用同一个组件):带提示的元素只写 `data-tooltip="提示"`(名字照旧写在 `aria-label` 里,只有图标的按钮必须有 `aria-label`),页面根上挂一个 `TooltipHost`(`webview-ui/src/components/ui/tooltip.tsx`)统一弹,不用原生 `title`;提示里的换行照样换行(看板摘修复弹框里「迁移」列出的几个文件一行一个);注释、回答里 Markdown 链接的标题(`[文字](网址 "标题")`)也写成 `data-tooltip`。网页的 TSX 里写原生 `title` 属性是 lint 错误(`vscode/eslint.config.mjs`),只有 `iframe` 这种必须带标题的元素例外,组件自己叫 `title` 的属性不算。原因:macOS 上 VS Code 带的 Electron(1.139 是 Electron 43)只在鼠标压着带 `title` 的元素自身边缘时才弹原生提示,停在它里面的图标、文字上不弹(electron/electron#49843,Electron 38 起),种类图标 20px 的框几乎被 16px 的图形占满,基本弹不出来;样子也是系统的。样子照 VS Code 的悬停框:`--vscode-editorHoverWidget-background` 底、`--vscode-editorHoverWidget-border` 边框、`--vscode-editorHoverWidget-foreground` 字、`0 2px 8px --vscode-widget-shadow` 阴影,11px 字,圆角 4px,最宽 22rem;优先在元素上方,上方放不下翻到下方,水平对着元素中间,贴边留 8px,不接鼠标。鼠标停 500ms 才弹,在同一个元素里移动不重新计时,已经弹着时移到另一个带提示的元素上立刻换;移开、移出页面、按下鼠标、按任意键(含 Esc)、滚动就收,收了之后鼠标不离开这个元素不再弹。键盘把焦点移到带提示的元素上(`:focus-visible`)立刻弹,焦点离开就收;提示不是元素自己的名字(`aria-label` 或它的字)时用 `aria-describedby` 挂到元素上,读屏当描述念。
- 种类图标和底色按种类区分(颜色见「颜色」);文件开头的说明(种类「注释」)虚线框。
- 「注释」一行:讲这块的普通注释不单独成块,是这张卡片上的一行(见「注释挂到它讲的块上」)。属性表卡片(语言的、翻译插件排的、库规则的卡片都一样,宿主统一插,规则、翻译插件不用管)插在第一行(名称这类讲它是谁的一行)后面,有说明(值是 Markdown 的那一行)时插在说明后面,所以是「名称、说明、注释、别的」;一行译文的卡片(条件、循环、语句、调用、返回……)排在译文、摊开实参的表、说明的下面,同属性表的一行(左边「注释」、右边 Markdown)。名字是翻译插件给种类 comment 起的名字(中文直译「注释」)。几段注释(中间隔着空行、或前后两处)各是一段 Markdown,照源码顺序,空行隔开。按「注释的 Markdown 排版」显示(仓库路径链接、不带路径的引用、仓库图片都照常能点),标差异时同属性表的行逐词标。这一行上不放按钮,「按注释规范重写」排在卡片右上角那排按钮里(见「重写注释」)。
- 内容两种状态,每块单独切:
- 预览:函数、类、枚举、异常、导入、常量、变量、解释器、选项、解析指令、阶段、健康检查、启动块是属性表(见下节),文件开头的说明是 Markdown(见「注释的 Markdown 排版」),其余块是一行译文;按词着色,等宽字体(中文部分用系统中文字体回退)。一行译文里有跨几行写的字典 / 列表时,拆开的各项排在译文下面(见「字典 / 对象字面量、跨几行写的列表」),有跨几行写的调用时,摊开实参的表排在译文下面(见「调用与构造」);块带说明(Python 字段的属性 docstring)时,说明按 Markdown 再排在下面;块挂着注释时「注释」一行排在最下面。
- 预览:函数、类、接口、枚举、异常、导入、常量、变量、解释器、选项、解析指令、阶段、健康检查、启动块是属性表(见下节),文件开头的说明是 Markdown(见「注释的 Markdown 排版」),其余块是一行译文;按词着色,等宽字体(中文部分用系统中文字体回退)。一行译文里有跨几行写的字典 / 列表时,拆开的各项排在译文下面(见「字典 / 对象字面量、跨几行写的列表」),有跨几行写的调用时,摊开实参的表排在译文下面(见「调用与构造」);块带说明(Python 字段的属性 docstring)时,说明按 Markdown 再排在下面;块挂着注释时「注释」一行排在最下面。
- 编辑:这一块在源码里的完整代码(按语法树着色):挂在它上面的注释、头部连同各子孙块、收尾括号,可以直接改,改完按这整段写回;蓝色边框。按钮换成眼睛「预览」;Esc 也回到预览。
- 复合语句(函数、条件、循环……)块的预览只讲它自己的头部那几行(主体交给子块);收尾括号、空行不成块。挂在块上的注释算在块的完整范围里(写在块上面的从注释开头算起,挂在这一层最后一块上的、子块交上来的到注释结尾):差异、跳过去、钉住卡片、总览条、「只看改动」、按行找块都按它,注释那几行改了标的是它挂着的那块。函数、类的说明(JSDoc / docstring)也算在函数、类块的头部里;Python 常量、变量、字段的属性 docstring 算在它那一块里(中间隔着的空行也是)。
- 编辑范围是块的完整范围(`blockView.ts` 的 `blockSpans`,和语言无关):从头部和挂在它上面的注释里最靠前的开头,到头部、整条语句、挂在它上面的注释、各子孙块(连同它们的注释)里最靠后的那个字符。else、elif、catch 这类展平成兄弟块的子句,整条 if / try 按各块的开头(连同挂在它上面的注释)切开:每块到下一块开头之前,最后一块到语句结尾(Shell 的 `fi` 归它);`} else {` 写在一行时两块都含这一行。
@@ -250,7 +251,7 @@
- 文档刚改、差异还没重算时:块按记号认,没动过的块照样带色条;卡片里的标记按下标认,只在页面上正是差异算的那一版时画。
- 颜色:取 VS Code 编辑器行号旁的改动色带 `--vscode-editorGutter-addedBackground` / `modifiedBackground` / `deletedBackground`(主题没给时用 Dark Modern 的默认值),行内增删的字用 `--vscode-diffEditor-insertedTextBackground` / `removedTextBackground`;整行底色是改动色的 14%,新增卡片底色 10%。
### 属性表(函数、类、枚举、异常、导入、常量、变量块)
### 属性表(函数、类、接口、枚举、异常、导入、常量、变量块)
卡片外观(折叠箭头、种类图标、底色、悬停高亮、「编辑 / 预览」切换)和其他块一样,只是内容换成一张属性表:左列属性名(界面字体、`descriptionForeground` 色,宽 76px,这张表里有更长的属性名时整列按最长的那个放宽,值照样对齐在同一处,属性名不折行:shell 的「管道里任一步失败都算失败」),右列是值(等宽字体,按词性着色,长了换行),两列间距 12px,行高 20px。「输入参数」「导入项」和值是字典 / 对象字面量、跨几行的列表的「值」「初始值」下面展开子行,属性名前带折叠箭头(图标、大小、颜色同块的折叠箭头;点箭头或属性名切换,默认展开,折叠后值「N 个」「字典 N 项」照常显示,只收起子行);子行往右缩进 5px,左边一条 1px 竖线(`editorIndentGuide-background1`);输入参数例外,是一张表格(见下)。字典 / 对象的子行分键 / 值两列,键列宽 150px(编辑区窄时按 28% 收),值列占剩下的。子行下面还能再展开一层(嵌套的字典 / 对象、列表):带下一层的子行第一列前面有折叠箭头(点箭头或第一列切换,默认展开),同一层里其余子行在同样的位置留空、第一列对齐;下一层同样往右缩进 5px、左边一条竖线,竖线对着上一层的箭头,每层自己的两列对齐。同一层里没有带下一层的子行时不留箭头的位置(和导入项的子行一样)。列表的一项带实参表(整项是调用)时,表排在这一项下面、同样往右缩一层、左边一条竖线,总是展开、没有折叠箭头。表格的行(实参表)的值拆成列表时,各项排在最后一格里、文字下面,同上面的子行(项下面的下一层照样能折叠),表格的行自己不折叠。
@@ -260,6 +261,7 @@
| --- | --- |
| 函数 | 名称(函数色;匿名函数写「匿名」)/ 说明(有正文才显示:JSDoc 去掉 `/**`、`*/` 和每行开头的 `*`,docstring 去掉引号;再去掉共同缩进和首尾空行,拆掉块标签(见「注释的 Markdown 排版」),剩下的正文按 Markdown 渲染)/ 对不上的参数(说明里有 `@param` 的名字在输入参数里没有时才显示:`N 个`,展开成表格「名称 \| 说明」,名称是 hint 色、悬停「没有这个参数」)/ 访问级别 / 异步(是 / 否)/ 装饰器(有才显示,源码原样照抄、按源码着色,多个换行)/ 输入参数(`N 个` 或「无」,展开成表格,列名 名称 \| 类型 \| 默认值,每个参数一行:名称 \| 类型(没标注写「未标注」)\| 默认值(译文,没有留空);任何一个参数对上了 `@param` 时多一列「说明」(Markdown,没对上的格子留空),按名字对,`*args` / `**kwargs` / `...rest` 去掉前缀对)/ 输出参数(返回类型的译文,没标注写「未标注」;写了 `@return` 时类型后面跟着这段说明,Markdown、注释色)/ 异常(写了 `@throws` 才显示:`N 个`,展开成表格「异常 \| 触发条件」,一条一行,都是 Markdown,异常名能点,见「注释的 Markdown 排版」的引用) |
| 类 | 名称(类型色;匿名类 `export default class {}` 写「匿名」;赋给变量、类字段的类表达式写变量名、字段名)/ 说明(有正文才显示,规则同函数)/ 访问级别 / 抽象(是 / 否,只有 TypeScript / TSX 有,看 `abstract class`,TypeScript 的类都有这一行;JavaScript / JSX 没有)/ 装饰器(同函数)/ 类型参数(有才显示:TypeScript `<K, T extends X = Y>` 每个参数的译文,逗号隔开,如 `K, T 限定为 X = Y`;Python 3.12 的 `class G[T]` 同样)/ 父类(有才显示:TypeScript `extends` 后面的表达式连着泛型实参的译文,如 `Base<字符串>`;Python 类名后面括号里的参数列表照写的顺序逐项翻,关键字参数、`*bases` 也在这一行,如 `Base, metaclass=ABCMeta`;逗号隔开)/ 实现的接口(有才显示:TypeScript `implements` 后面每个类型的译文,逗号隔开)/ 构造参数(说明里写了 `@param` 才显示:类卡片没有构造参数表可对,照写成「名称 \| 说明」表)/ 异常(同函数) |
| 接口 | 名称(类型色)/ 说明(有说明才显示,规则同函数)/ 访问级别(同类)/ 类型参数(有才显示,同类)/ 继承(有才显示:`extends` 后面每一项的译文,泛型实参连在里面,逗号隔开,如 `Base, Store<T>`)/ 属性(有属性才显示:`N 个`,展开成表格,列名 属性 \| 类型 \| 说明,每个属性一行:属性名(variable 色;可省略的后面带 `?`,同输入参数;索引签名写 `[参数名: 类型的译文]`,如 `[key: 字符串]`)\| 类型(类型的译文;`readonly` 的前面写「只读」,storage 色;没标注写「未标注」)\| 说明(同枚举成员的说明:属性上面单独成行的注释、JSDoc 和这一行行尾的注释,原文用空格连起来;一个属性都没有说明时没有这一列);讲的定义是属性名(索引签名是方括号里的参数名),跳到属性名只闪那一行)。见「接口」 |
| 枚举 | 名称(类型色)/ 说明(有说明才显示,规则同函数)/ 访问级别(同类)/ 装饰器(有才显示,同函数)/ 继承(有才显示:Python 类名后面括号里照写的父类,如 `str, enum.Enum`;TypeScript 没有)/ 成员(`N 个` 或「无」,展开成表格,列名 成员 \| 值 \| 说明,每个成员一行:成员名(variable 色)\| 值(写了值是值的译文;没写值时是语言算出来的值,TypeScript 数值枚举自增的 0、1、2……,算不出来和 Python 的 `auto()` 写「自动」,这两种按 hint 着色)\| 说明(成员的注释、JSDoc、属性 docstring 的原文用空格连起来,comment 色;一个成员都没有说明时没有这一列);讲的定义是成员名,跳到成员名只闪那一行)。原型「枚举」一节(`ENUM_SAMPLE`、`enumCard`) |
| 异常 | 名称(类型色;匿名类写「匿名」)/ 说明(有说明才显示,规则同函数)/ 访问级别(同类)/ 装饰器(有才显示,同函数)/ 继承(有才显示:照写的父类,同类的「父类」,如 `Exception`、`errors.HttpException<字符串>`)/ 属性(有类属性才显示:`N 个`,展开成表格,列名 属性 \| 类型 \| 值 \| 说明,每个属性一行:属性名(variable 色)\| 类型(写了标注是它的译文,一个属性都没标注时没有这一列)\| 值(写了值是值的译文,没写留空)\| 说明(同枚举成员的说明,一个属性都没有说明时没有这一列);讲的定义是属性名)/ 构造参数(写了构造函数才显示:表格同函数的「输入参数」,Python 去掉 self,类说明里的 `@param` 同函数对进「说明」列、对不上的排在说明下面;讲的定义是参数名;没写构造函数时同类,说明里的 `@param` 照写成表)/ 异常(同函数)。见「异常」 |
| 导入 | 来源(TypeScript 是字符串字面量原样,如 `'node:fs'`,字符串色;Python 是点分模块名,如 `fastapi`、`.models`,类型色)/ 仅类型(是 / 否,只有 TypeScript / TSX 有,看 `import type`;JavaScript / JSX 没有)/ 导入项(`N 个` 或「无」,每项一行:`name`、`name 作为 alias`;默认导入写「默认导出 作为 x」,`* as x` 写「全部 作为 x」,Python `from m import *` 写「全部」,Python `import a.b as c` 写「整个模块 作为 c」,一条 `import a, b` 导入多个模块时写模块名) |
@@ -307,6 +309,47 @@
- 语言插件给各项(`LanguageAdapter.collection`:节点 → `CollectionFacts`),翻译插件按字面量的节点类型查叫法(`ZhTable.collections`)、定怎么拆;webview 只画子行和表,不认语言。
### 接口
原型没画,照枚举卡片的画法。TypeScript 的 `interface` 由语言插件报成契约里的接口块(`InterfaceBlock`,种类 `interface`,facts 是 `InterfaceFacts`),翻译插件排成属性表;图标 `braces`、颜色同类型块,筛选种类叫「接口」,访问级别同类。
- 认哪些:`interface X {}`、`export interface X {}`、`declare interface X {}`,函数里写的也算(没有访问级别)。`type X = { … }` 照旧是一行译文的类型块。
- 进「属性」表的成员(照源码顺序):属性签名(`readonly id?: string`),和写了参数名的索引签名(`[key: string]: T`、`readonly [i: number]: T`)。说明的收法同枚举成员:从上一个成员之后起单独成行的注释、JSDoc,和写在它这一行行尾的注释(JSDoc 写在上一个成员同一行时讲的是下一个)。
- 留作子块的成员:方法签名(`close(reason?: string): void`、`get size(): number`)是函数卡片,访问级别是公共,紧贴的 JSDoc 是它的说明;调用签名(`(a: string): number`)、构造签名(`new (…)`)照旧是一行译文。讲它们的注释挂在它们上面;最后一个成员后面的注释有子块时挂到最后一个子块上,没有子块时是接口卡片的「注释」一行。
- 接口块的头部(编辑范围)是整个接口:从紧贴的 JSDoc 开始到右花括号,「编辑」时整段一起改,盖住留下的子块(同异常块)。接口卡片默认展开(方法签名的卡片照常露着),同类型块原来的样子。
- 跳到定义:接口名是块自己的名字(闪整块),属性名只闪「属性」表里那一行;「了解一块代码」照旧按 `接口名.属性名` 找得到并进属性表的属性。中文名(行尾 `// @Comment …`)照常换属性格子里的名字,行尾记号不进说明。
样例:
```ts
/** 会话。 */
export interface Session<T extends Base = Base> extends Base, Store<T> {
/** 编号 */
readonly id: string
// 标题
title?: string // 可以不填
[key: string]: unknown
count
close(reason?: string): void
}
```
```
名称: Session
说明: 会话。
访问级别: 公共
类型参数: T 限定为 Base = Base
继承: Base, Store<T>
属性: 4 个
属性 | 类型 | 说明
id | 只读 字符串 | 编号
title? | 字符串 | 标题 可以不填
[key: 字符串] | 未知 |
count | 未标注 |
```
`close` 是接口卡片下面的一张函数卡片。
### 异常
原型没画,照枚举卡片的画法。异常是语言自己的概念,不是某个库的写法,所以和 TypeScript 的 enum 一样由语言插件认,报成契约里的异常块(`ExceptionBlock`,种类 `exception`,facts 是 `ExceptionFacts`);Python、TypeScript 同一种块、同一张属性表、同一个筛选种类,用词归翻译插件,webview 只按种类上色、默认收起。
@@ -690,7 +733,7 @@
按写注释的技能(`/ecc:writing-code-comments`)给一块代码重写它的注释。没有说明、但块前面有普通注释时换掉那段;都没有就按这门语言的说明新写一段。像问 AI 一样留会话、能接着说:块下面记着重写过的每一轮,在输入框里说修改意见,模型接着那次会话改,交回新正文就写回文件。
- 按钮(`MessageSquareText` 图标,只有图标,悬停 / 读屏「按注释规范重写」,块还没有说明、也没有前面的普通注释时是「按注释规范写注释」):每块代码都有,不管卡片有没有「注释」一行都排在右上角那排按钮的最前面,依次是它、「问 AI」、「编辑」;文件开头的说明卡片也一样。定义卡片浮窗里、删掉的块没有;嵌着的代码里的块(Dockerfile RUN 里的 shell)、语言既没有说明的写法(`LanguageAdapter.freshDoc`)也没给行注释记号(`LanguageAdapter.lineComment`)、又还没有可换的注释的块没有(`BlockView.commentTarget` 没有);文件不在工作区文件夹里时重写记录没地方存,也没有。点了按技能命令重写(开新会话)、展开这块的重写记录;展开着又正在写时不能再点。重写过、记录收着时它叫「展开重写记录」,点了只展开(好接着说),展开着再点才重写。
- 改哪儿(`BlockView.commentTarget`,扩展端 `blockView.ts` 算):块已经有说明(Python 函数 / 类主体里第一条语句的 docstring、紧贴声明的 JSDoc、Rust 的 `///` / `//!`,以及别的语言记在块上的说明)时换掉这段说明的那几行,正文填回原来的定界符(`"""`、`/** */`、`///`),不在声明上面再插一行行注释;说明和块前面的普通注释都有时只换说明,普通注释留着。没有说明、块前面挂着普通注释时换掉它们(从第一段开头到最后一段结尾,几段中间隔着的空行一起换掉,写成一段);文件开头的说明卡片换掉它自己。都没有时新写:Python 的函数或类在主体里插一段 docstring(缩进同主体,作为第一条语句;声明和主体写在同一行时从主体那一列拆开);JavaScript / TypeScript 的函数、类、枚举、常量、变量在声明紧上面插 `/** */`;Rust 的项在紧上面插 `///`;别的(语句、Python 的变量,只有行注释的 shell、Dockerfile)在块的第一行上面插一行行注释。挂在块后面、子块交上来的注释不动。技能照这条规矩从范围本身判断写哪种:写说明时带 `@param` 等标签,前端组件的说明画原型图、嵌进正文。
- 改哪儿(`BlockView.commentTarget`,扩展端 `blockView.ts` 算):块已经有说明(Python 函数 / 类主体里第一条语句的 docstring、紧贴声明的 JSDoc、Rust 的 `///` / `//!`,以及别的语言记在块上的说明)时换掉这段说明的那几行,正文填回原来的定界符(`"""`、`/** */`、`///`),不在声明上面再插一行行注释;说明和块前面的普通注释都有时只换说明,普通注释留着。没有说明、块前面挂着普通注释时换掉它们(从第一段开头到最后一段结尾,几段中间隔着的空行一起换掉,写成一段);文件开头的说明卡片换掉它自己。都没有时新写:Python 的函数或类在主体里插一段 docstring(缩进同主体,作为第一条语句;声明和主体写在同一行时从主体那一列拆开);JavaScript / TypeScript 的函数、类、接口、枚举、常量、变量在声明紧上面插 `/** */`;Rust 的项在紧上面插 `///`;别的(语句、Python 的变量,只有行注释的 shell、Dockerfile)在块的第一行上面插一行行注释。挂在块后面、子块交上来的注释不动。技能照这条规矩从范围本身判断写哪种:写说明时带 `@param` 等标签,前端组件的说明画原型图、嵌进正文。
- 问模型:同「问 AI」用 Claude Code 命令行、在这份文件所在的工作区文件夹里跑、用输入框旁边选的 profile,按 JSON Schema 交结果(`--json-schema '{"type":"object","properties":{"body":{"type":"string"}},"required":["body"],"additionalProperties":false}'`:模型调 `StructuredOutput` 交,result 行的 `structured_output.body` 是不带注释符号的正文;只回了一段话、`body` 是空的都算没交回)。文件有没保存的修改时不问(模型读的是磁盘上的文件),直接失败「先保存再重写注释」。
- 第一轮(点按钮)从 stdin 发的只有一句 `/ecc:writing-code-comments <路径>:<起>-<止>`:路径相对 claude 的工作目录、用 `/` 分隔,行号 1 起,是这块代码的完整范围(连同挂在它上面的注释;文件开头的说明卡片是它自己那几行),开新会话(会话 id 由 Claude Code 起,结果里带回来)。
- 输入框里说的话:`--resume` 这块最近一次重写的会话(和问答的会话分开,各接各的),发「块现在在哪(`<路径>:<起>-<止>`,写回注释后行号会变,按记号从最新的块树里找)、空一行、用户的话」,带上贴的图。会话找不到了(过期、被清理)就开新会话,发技能命令后面空一行跟着这句话。
@@ -708,7 +751,7 @@
```ts
export type BlockKind =
| 'import' | 'function' | 'class' | 'enum' | 'exception' | 'type' | 'field' | 'const' | 'variable'
| 'import' | 'function' | 'class' | 'interface' | 'enum' | 'exception' | 'type' | 'field' | 'const' | 'variable' // interface 是 TS 的接口(属性表),type 是别的类型声明(一行译文)
| 'trait' | 'impl' | 'module' | 'attribute' // Rust:特质、实现、模块、编译属性(属性表)
| 'if' | 'else' | 'loop' | 'try' | 'catch' | 'finally' | 'return' | 'statement' | 'comment'
| 'interpreter' | 'options' | 'case' | 'command' | 'pipeline' // shell:解释器、选项(属性表),分情况、命令、管道(一行译文)
@@ -811,6 +854,26 @@ export interface EnumFacts {
members: EnumMemberFacts[]
}
/** 接口的一个属性:属性签名,写了参数名的索引签名 `[key: string]: T` 也算一个。 */
export interface InterfacePropertyFacts {
name: Node // 属性名;索引签名是方括号里的参数名(key)
index?: Node // 索引签名方括号里参数的类型(string);普通属性没有
type?: Node // 没标注时没有
optional: boolean // x?
readonly: boolean // readonly x
docs: Node[] // 同 EnumMemberFacts.docs(没有属性 docstring)
}
/** 接口:TS 的 interface(含 declare interface)由语言插件报(见「接口」)。 */
export interface InterfaceFacts {
name: Node
doc?: Node // 同 ClassFacts.doc
access?: Access // 同 ClassFacts.access
typeParameters: Node[] // 同 ClassFacts.typeParameters
extends: Node[] // extends A, B<T>:每项一个节点,泛型实参连在里面
properties: InterfacePropertyFacts[] // 照源码顺序
}
/** 异常类的一个类属性:Python 类主体里给一个名字赋值的语句,TS 的类字段(含 static)。 */
export interface FieldFacts {
name: Node
@@ -958,6 +1021,7 @@ export interface BlockShape {
/** 预览成属性表的块:种类和 facts 绑在一起,语言插件漏给 facts 编译不过。 */
export interface FunctionBlock extends BlockShape { kind: 'function', facts: FunctionFacts }
export interface ClassBlock extends BlockShape { kind: 'class', facts: ClassFacts }
export interface InterfaceBlock extends BlockShape { kind: 'interface', facts: InterfaceFacts } // 头部是整个接口,方法签名等别的成员是子块
export interface EnumBlock extends BlockShape { kind: 'enum', facts: EnumFacts } // TS:头部是整条声明,没有子块;库规则认出来的:整个类,留下的方法等是子块
export interface ExceptionBlock extends BlockShape { kind: 'exception', facts: ExceptionFacts } // 头部是整个类,留下的方法(含构造函数)等是子块
export interface ImportBlock extends BlockShape { kind: 'import', facts: ImportFacts }
@@ -965,10 +1029,10 @@ export interface VariableBlock extends BlockShape { kind: 'const' | 'variable',
/** Rust 的内部属性 `#![…]`:生效的各个属性(cfg_attr 拆开)、cfg_attr 的生效条件(CfgPredicate:not / all / any / 配置项 / 照原文)、写在哪一层(文件顶层、mod、impl)。 */
export interface AttributeBlock extends BlockShape { kind: 'attribute', facts: AttributeFacts }
export type FactBlock
= | FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | ImportBlock | VariableBlock | AttributeBlock | InterpreterBlock | OptionsBlock
= | FunctionBlock | ClassBlock | InterfaceBlock | EnumBlock | ExceptionBlock | ImportBlock | VariableBlock | AttributeBlock | InterpreterBlock | OptionsBlock
| DirectiveBlock | StageBlock | HealthBlock | StartBlock // 各带自己的 facts:InterpreterFacts、OptionsFacts、DirectiveFacts、StageFacts、HealthFacts、StartFacts
/** 带说明(facts.doc)的块。 */
export type DocumentedBlock = FunctionBlock | ClassBlock | EnumBlock | ExceptionBlock | VariableBlock
export type DocumentedBlock = FunctionBlock | ClassBlock | InterfaceBlock | EnumBlock | ExceptionBlock | VariableBlock
/** 属性表块的种类和 facts,不带块的形状:库规则把一块认成现成的一种属性表块时给它。 */
export type BlockFacts = FactBlock extends infer B ? B extends FactBlock ? Pick<B, 'kind' | 'facts'> : never : never
@@ -2136,7 +2200,6 @@ Python 内置类型(仅注解里):str 字符串、int 整数、float 浮
| `if (!folder)` | `如果 (folder 不成立)` |
| `return []` | `返回 空数组` |
| `for (const f of files) {` | `遍历 (常量 f 取自 files)` |
| `failed: { path: string, reason: string }[]` | `failed: { path: 字符串, reason: 字符串 }[]` |
| `return {⏎ locked: true,⏎ … }`(issueLock.ts 20–24 行) | `返回 对象 3 项`,下面各项 `locked \| 真`、`prerequisiteNumber \| prereq.number`、`prerequisiteColumn \| prereq.column` |
| `err instanceof Error ? err.message : String(err)` | `如果 err 属于 Error 则 err.message 否则 String(err)` |
@@ -2155,6 +2218,8 @@ Python 内置类型(仅注解里):str 字符串、int 整数、float 浮
| `_default_port = 5680 if _role == "beat" else 5679` | 名称 _default_port / 访问级别 私有 / 初始值 `如果 _role 等于 "beat" 则 5680 否则 5679` |
| `_CONTEST_STATUS_KEYS = {⏎ "published": "contests:publish", "ended": "contests:end", "draft": "contests:update" }` | 名称 _CONTEST_STATUS_KEYS / 访问级别 私有 / 初始值 字典 3 项:`"published" \| "contests:publish"`、`"ended" \| "contests:end"`、`"draft" \| "contests:update"` |
| `CONFIG = {"db": {"host": host, "opts": {}}, **defaults, 1: await load()}` | 名称 CONFIG / 访问级别 公共 / 值 字典 3 项:`"db" \| 字典 2 项`(下一层 `"host" \| host`、`"opts" \| 空字典`)、`展开 \| defaults`、`1 \| 等待 load()` |
| `export interface EnvLockResult {⏎ total: number⏎ ok: string[]⏎ failed: { path: string, reason: string }[]⏎ }`(envLock.ts 32–36 行) | 名称 EnvLockResult / 访问级别 公共 / 属性 3 个:`total \| 数值`、`ok \| 字符串[]`、`failed \| { path: 字符串, reason: 字符串 }[]` |
| `export interface LockedReason {⏎ locked: boolean⏎ prerequisiteNumber?: number⏎ prerequisiteColumn?: string⏎ }`(issueLock.ts 3–7 行) | 名称 LockedReason / 访问级别 公共 / 属性 3 个:`locked \| 布尔`、`prerequisiteNumber? \| 数值`、`prerequisiteColumn? \| 字符串` |
| `const result: EnvLockResult = { total: files.length, ok: [], failed: [] }`(envLock.ts 40 行) | 名称 result / 类型 EnvLockResult / 初始值 对象 3 项:`total \| files.length`、`ok \| 空数组`、`failed \| 空数组` |
| `export const handlers = { total: files.length, 'x-y': await load(), [key]: !ok, files, ...rest, async load(id: string): Promise<void> { … }, nested: { a: [], b: { c: 1 } } }` | 名称 handlers / 访问级别 公共 / 值 对象 7 项:`total \| files.length`、`'x-y' \| 等待 load()`、`[key] \| ok 不成立`、`files \| files`、`展开 \| rest`、`load \| 异步 load(id: 字符串): Promise<无>`、`nested \| 对象 2 项`(下一层 `a \| 空数组`、`b \| 对象 1 项`,再下一层 `c \| 1`) |
| `export abstract class Repository<K, T extends Entity = Entity> extends Base<string> implements Loader<T>, Disposable {`(classes.ts 3–9 行,连同前面的 JSDoc 和 `@Injectable()`) | 名称 Repository / 说明 `仓库基类。…` / 访问级别 公共 / 抽象 是 / 装饰器 `@Injectable()` / 类型参数 `K, T 限定为 Entity = Entity` / 父类 `Base<字符串>` / 实现的接口 `Loader<T>, Disposable` |