diff --git a/docs/superpowers/specs/code-reader/spec.md b/docs/superpowers/specs/code-reader/spec.md index 4d4fb4e..a8d7b07 100644 --- a/docs/superpowers/specs/code-reader/spec.md +++ b/docs/superpowers/specs/code-reader/spec.md @@ -148,7 +148,7 @@ | copy 复制 | `copy` | setting 设置 | `sliders-horizontal` | label 标签 | `tag` | | health 健康检查 | `activity` | start 启动 | `play` | | | - 库规则的种类用规则给的图标(`KindBadge.icon`),跟在语言的种类图标后面:SQLAlchemy 数据表 `database`、数据类 `package`、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`、React 组件 `atom`、副作用 `zap`、事件处理 `mouse-pointer-click`。数据类写了 `frozen=True` 再加一把锁(`lock`,悬停「不可修改」)。 + 库规则的种类用规则给的图标(`KindBadge.icon`),跟在语言的种类图标后面:SQLAlchemy 数据表 `database`、数据类 `package`、Pydantic 数据模型 `braces`(和类型块同一个图标,颜色不同)、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`、React 组件 `atom`、副作用 `zap`、事件处理 `mouse-pointer-click`。数据类写了 `frozen=True` 再加一把锁(`lock`,悬停「不可修改」)。 这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 26 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置共用一个,字段、标签共用一个——加库规则用的 9 个(React 组件的原子、副作用的闪电、事件处理的点击手势也在里面),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 能走到,回车 / 空格切换。 @@ -1118,7 +1118,7 @@ export interface CommandRule { ### 启用、认领、优先 -- 启用:宿主收集这份源码里各层导入块的 `ImportFacts.sources`(去掉引号),有一个等于规则的某个库、或以「库.」「库/」开头(子模块),规则才对这份源码启用。规则不用自己读导入语句。`libraries` 是空数组的规则不看导入,`languageIds` 里这几种语言的源码都启用(React:新的 JSX 写法不用导入 react)。 +- 启用:宿主收集这份源码里各层导入块的 `ImportFacts.sources`(去掉引号),有一个等于规则的某个库、或以「库.」「库/」开头(子模块),规则才对这份源码启用。规则不用自己读导入语句。`libraries` 是空数组的规则不看导入,`languageIds` 里这几种语言的源码都启用(React:新的 JSX 写法不用导入 react。Pydantic:子类文件常常只导入父模型、不再导入 pydantic,见「内置:Pydantic 数据模型」)。 - 认领:宿主从顶层往下每块问一遍启用的规则,按注册顺序,第一个认领的算(先注册的优先);被认领的块留下的子块照样问。 - 出错:规则抛错、或替身不合约定(种类 id 没带规则 id 前缀、并进来的不是这块的子块、属性表里的引用标的不是自己 / 规则没给 `resolve` / 引用的段没带下标、放进卡片里的块不是留下的子块 / 放了几处 / 没有那一行或子行、要略去的块还有子块)时,宿主记一条警告日志(输出面板「Superpowers」),当它没认、接着问下一个规则;都没认这块照常显示。预览不会因为规则出错而失败。 - 并进来的子块:不再单独成块,由规则挑(`absorbs`),宿主不替规则多并。没并进来的子块(方法、规则不讲的注释)不管写在哪(字段中间也算),都照源码顺序是替身的子块,预览里照常看得见。替身的编辑范围同别的块,是原块整条语句(从头部开始,到原块连同各子块的最后一行,类就是整个类),盖住留下的子块的行:替身编辑时预览不画这些子块,同一行不会同时在两处能改(见「预览(代码块树)」)。内置规则并成员时连同紧挨在它前面的注释块一起并(讲的是这个成员),讲方法的注释、最后面的注释照常留下。 @@ -1365,6 +1365,68 @@ export interface CommandRule { `__post_init__` 照常是卡片的子块。 +### 内置:Pydantic 数据模型(Python) + +`vscode/src/codeReader/rules/pydantic.ts`,`libraries: []`(子类文件常常只 `from … import 父模型`,不再导入 pydantic,不能靠导入来决定看不看)。类主体里带注解的赋值和数据类共用 `rules/children.ts` 的 `annotatedAssignments`。种类 `pydantic.model`,标签「数据模型」(暗 #c678dd / 亮 #8b3db5),图标 `braces`(和类型块同一个,颜色不同;悬停提示是 `badge.label`,走 `data-tooltip`),和类块一样默认收起子块。 + +- 认哪些类:模块顶层的类,父类链上有 pydantic 的 `BaseModel`。`from pydantic import BaseModel`、`BaseModel as Model`、`import pydantic` 的 `pydantic.BaseModel`、`from pydantic.main import BaseModel` 都算,别名按 `context.imports` 换回。同一文件里继承另一个已经是模型的类也算(`class Child(AIGCMetadata)`,父类写在后面也认)。不认 `TypedDict`、`NamedTuple`、普通类、没导入 pydantic 时同名的 `BaseModel`,也不认 pydantic 的 `@dataclass`(那不是这张卡片,数据类规则也不认它)。 +- 别的文件里的父类:父类是一个名字、又不是本文件里的类时,问索引 `pydantic.models`(`context.lookup`,见「规则认领时查别的文件里的定义」)。索引只扫源码里写了 `BaseModel` 的 `**/*.py`,记下本文件里能确定的模型(父类是 BaseModel,或本文件里另一个模型),值是 `"model"`。查到是模型就换成这张卡片;还没查到(或确定不是)就先按普通类画,查到后再重排。再往下跨一层、中间那个类自己的文件里写不出 `BaseModel` 的,索引不收,认不出来。 +- 字段:类主体里带注解的赋值(`x: str`、`x: str | None = None`、`x: str = Field(...)`),照源码顺序。`ClassVar[...]` 不是字段。`model_config = ConfigDict(...)`(写成带注解的赋值也算)不是字段,是「选项」。 +- 并进卡片的子块:各个字段连同紧挨在它前面的注释块、`model_config` 那一行;方法、`@field_validator`、`ClassVar`、没注解的类属性(`model_config` 除外)、讲它们的注释照常是子块。字段并进来之后不再单独画成变量块。 + +属性表(有才显示;一个字段都没有时不画「字段」这一行): + +| 行 | 内容 | +| --- | --- | +| 名称 | 类名(type 色,带下标) | +| 说明 | 类的 docstring;Markdown | +| 访问级别 | 公共 / 私有 / 受保护(keyword 色) | +| 继承 | 父类照写法(翻译插件翻) | +| 选项 | `model_config = ConfigDict(...)` 里写了的项,照写的顺序用 ` · ` 隔开(storage 色)。没写的不讲 | +| 字段 N 个 | 表格:字段 \| 类型 \| 默认值 \| 说明,和数据类同一套列,每个字段一行,讲的定义是字段名 | + +选项(`ConfigDict` 的参数;不是这个调用就整段照写法翻): + +| 写法 | 显示 | +| --- | --- | +| `strict=True` / `strict=False` | 严格 / 不严格 | +| `extra="forbid"` / `"allow"` / `"ignore"` | 禁止多余字段 / 允许多余字段 / 忽略多余字段 | +| `from_attributes=True` / `False` | 可从对象读取 / 不从对象读取 | +| `frozen=True` / `False` | 不可修改 / 可修改 | +| `populate_by_name=True` | 可用字段名填 | +| `validate_assignment=True` | 赋值时也校验 | +| `arbitrary_types_allowed=True` | 允许任意类型 | +| `use_enum_values=True` | 用枚举的值 | +| `str_strip_whitespace=True` | 去掉首尾空白 | +| `validate_default=True` | 也校验默认值 | +| 别的 | 照写 `名=值`(值翻译插件翻),`**x` 照写 | + +字段表的格子: + +- 字段:字段名(variable 色,带下标)。类型:注解照写法翻(翻译插件翻,`str` → 字符串,`str \| None` → 字符串 \| 空);类型里的名字带源码下标,能点。 +- 默认值:`= x` 写 x(`None` → 空);`Field(default=x)` 或第一个位置参数写 x(`Field(...)` 的 `...` 是必填,不算默认值);`Field(default_factory=f)` 写「每次新建 f()」,和数据类同一个说法。后面接 `Field(...)` 的约束,照写的顺序用 ` · ` 隔开:`min_length=1` 至少 1 个字符、`max_length=255` 最长 255、`ge=0` ≥ 0、`le` ≤、`gt` >、`lt` <、`multiple_of=3` 3 的倍数、`pattern` 匹配、`min_items` / `max_items` 至少 / 最多 N 项、`max_digits` 最多 N 位数字、`decimal_places` 小数 N 位。`default`、`default_factory`、`description` 不在这里重复。不认得的照写 `名=值`,不丢掉。 +- 说明:`Field(description="…")` 的字符串优先(不是字符串就照写法翻);没有就用字段上方的注释、这一行行尾的注释、紧跟在后面的属性 docstring,原文用空格连起来,comment 色。 + +样例(`fixtures/aigcMetadata.py` 的 `AIGCMetadata`,从 luffy `model/aigc.py` 摘的,测试里逐字核对): + +``` +名称: AIGCMetadata +说明: 生成内容来源标识。 +访问级别: 公共 +继承: BaseModel +选项: 严格 · 禁止多余字段 +字段: 7 个 + Label | 字符串 | | + ContentProducer | 字符串 | | + ProduceID | 字符串 | | + ReservedCode1 | 字符串 | | + ContentPropagator | 字符串 | | + PropagateID | 字符串 | | + ReservedCode2 | 字符串 | | +``` + +`TextTrace` 里 `reasoning_content: str | None = None` 的默认值是「空」。`fixtures/createShareRequest.py`(luffy `share/schemas.py` 的 `CreateShareRequest`)的 `session_id` 是「至少 1 个字符」,说明是 `Field(description=…)` 的原文;`@field_validator` 的方法仍是子块。 + ### 内置:FastAPI 接口(Python) `vscode/src/codeReader/rules/fastapi.ts`,`libraries: ['fastapi']`。原型:`code.js` 的「库规则:FastAPI 接口」一节(`ROUTER_SAMPLE`、`routerCard`、`ROUTE_SAMPLE`、`routeCard`),画板「库规则 · FastAPI 接口」;样例是 luffy 的 `user/routes.py`(`fixtures/routes.py`)、`skill_hub/routes.py`(`fixtures/skillHubRoutes.py`)、`user/console/routes.py`(节选在 `fixtures/consoleRoutes.py`)。路由器和挂在它上面的接口是两回事,各一张卡片: @@ -1968,6 +2030,7 @@ vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vit - Celery 规则:`fixtures/celeryApp.py` 的应用卡片和定时任务卡片逐字同上面的样例(列名、「12 条,另有展开 1 处」、行尾注释、条目里单独成行的注释、跨行的 `10⏎ * 60`、`每 get_settings().… 秒`、展开一行);任务名是引用、下标指回源码原文;应用卡片的位置参数按参数顺序认、别的参数各一行、展开、属性 docstring、别名;没导入 celery、不是 `Celery(...)` 的不认;频率换算(秒数算式、`timedelta`、`crontab` 各种写法、`schedule`、换算不了的「每 X 秒」和照写法);说明取哪些注释;条目名和任务名不同、没写任务名、任务名不是字符串、值不是字典、选项一列有才有;`conf.update(beat_schedule=…)`、不带 `beat_schedule` 的 `conf.update` 和 `autodiscover_tasks` 照常。任务名索引:显式 `name=`、默认「模块路径.函数名」(包、`src` 下的命名空间包、没有包)、`@shared_task` / `@shared_task()` / `@app.task` 不带括号 / 老写法 `@task`、`async def`、函数里的只认显式名字、`name=` 不是字符串的不认、同名几处都给、文件改了和新建 `__init__.py` 后跟着变、没有任务装饰器的文件不解析。 - 枚举(`rules/enum.test.ts`、`languages/typescript.test.ts`):`fixtures/enumModels.py` 的 `CommissionStatus` 逐字同上面的样例,`SecretScope` 的 docstring 当说明、没有说明列;种类 enum、叫「枚举」、访问级别同原来的类、没有标签(badge),编辑范围是整个类;Python 成员规则(dunder、sunder、私有名、`_ignore_`、lambda、`property`、`nonmember`、只写注解的不是成员,`auto()` 写「自动」,`member(x)` 取 x,说明取上面的注释块、行尾注释、属性 docstring,装饰器);方法、嵌套类、不是成员的属性照源码顺序是子块,成员前面的注释并进来、最后面的留下,「编辑」写回整个类;类名、成员名的定义范围和带下标的词指回源码原文;没导入 enum、SQLAlchemy 的 `Enum`、不是枚举类型的父类不认,`import enum as e` 的 `e.IntEnum` 认。TypeScript:`enums.ts` 里 export / const / declare / 函数里的 enum 都是枚举块、JSDoc 进头部,写的值、自增算出的值(接不上数字就没有)、说明(上面的注释和 JSDoc、行尾注释,同一行的 JSDoc 讲下一个),访问级别;自增的值和「自动」按 hint。TypeScript 和 Python 同样的成员得到一样的种类、属性表行、列名和格子着色(除了 Python 多的「继承」)。宿主:规则认成现成属性表块时照语言插件报的这种块显示、并进来的不是子块时照常显示;webview 枚举块默认折叠。 - 数据类(`rules/dataclass.test.ts`):`fixtures/dataclassModels.py` 的 `SandboxAccess` 逐字同上面的样例,`AdminUserRow`、`RedirectPayResult`(`field(init=False, default=…)`)、`SmsSent`(`default_factory=dict`)的行;种类 `dataclasses.dataclass`、叫「数据类」、颜色、访问级别同原来的类;编辑范围是整个类、写回原样,`__post_init__` 是子块;类名、字段名的定义范围和带下标的词指回源码原文;装饰器的写法(别名、`dataclasses.dataclass`、`dc.dataclass`)认,pydantic 的、`other.dataclass` 不认,没导入 dataclasses 不认;每个选项的译法、默认值不讲、别的照写、`**x`、别的装饰器一行;字段规则(`ClassVar`、`typing.ClassVar` 留作子块,`InitVar` 的类型和「仅初始化时使用」,`KW_ONLY` 记号并进来、后面的字段「只能按名字传参」、单独写 `kw_only=False` 的「可按位置传参」),`field(...)` 的默认值、`default_factory`(名字、lambda)、各参数、`metadata` 照写、`doc=`,说明取上面的注释块、行尾注释、属性 docstring;没注解的类属性、字段中间的方法照源码顺序是子块,「编辑」写回整个类。 +- Pydantic 数据模型(`rules/pydantic.test.ts`):`fixtures/aigcMetadata.py` 的 `AIGCMetadata` 逐字同上面的样例(7 个无默认值字段、选项「严格 · 禁止多余字段」、`model_config` 不单成一块),`TextTrace` 的可空字段默认值是「空」;`fixtures/createShareRequest.py` 的 `CreateShareRequest` 逐字核对字段、说明、长度约束,`@field_validator` 仍是子块;种类 `pydantic.model`、叫「数据模型」、颜色、图标 `braces`;同一文件里继承模型的也算,`TypedDict`、`NamedTuple`、普通类、没导入 pydantic 的不是;别名、`Field` 的默认值 / `default_factory` / 约束、说明优先于注释,`ClassVar` 和没注解的类属性留下;类型里的名字带源码下标;索引只收本文件里能确定的模型、没写 `BaseModel` 的文件不解析,别的文件里的父类查不到先当普通类、查到再换成卡片;差异:加一个字段标新增,改一个默认值标那一格。 - FastAPI 接口(`rules/fastapi.test.ts`):`fixtures/routes.py` 的 `update_user` 逐字同上面的样例(查到 `Forbidden` 是 403 时四条消息写在一行,没查到时不列),名字、颜色、图标、列名、筛选属性、定义范围、编辑范围是整个函数、函数体是子块;着色(URL 的 `{参数}` / 路径,花括号里的参数名 `refers` 到函数的参数,方法的 Swagger 颜色、成功的状态码绿色、自动带的 422 淡色,带下标的词指回源码原文);`fixtures/skillHubRoutes.py` 的 `list_skills`(Annotated 里的 Query、`_` 的依赖)、`get_skill`(HTTPException 的 status_code / detail)、`install`(Request 不算、`-> None` 没有内容);`fixtures/consoleRoutes.py` 的 `create_user`(`status_code=201`、Pydantic 模型、调别的函数间接抛的不算)、`list_users`(查到的类型别名 `FilterQuery` 是依赖、Literal 是简单类型);参数放在哪儿的各条(路径里的名字含 `{x:path}`、各种标记两种写法、上传文件、枚举和类型别名查到的、Pydantic 模型、`Security`、`Depends()`);标签、依赖、`responses=` 叠上路由对象的,`status.HTTP_…` 常量、`response_model=None`、`response_description`、`raise … from`、只写 `raise`、嵌套函数里的;`FastAPI()` 上的接口、别的装饰器一行;不认的(没导入、路由对象在别的文件、不是路由对象、`websocket`、不带括号)。跨文件(工作区索引 + 假的语言扩展):`update_user` 的 `Forbidden` 按定义处查到 `core/exceptions/schemas.py` 的 403,同名的别处的类不算;索引认的类上的 `status_code`(带注解、常量)、枚举类、`Annotated` 类型别名,普通类不认。路由器卡片:`fixtures/routes.py` 的 `router` 逐字同上面的样例,名字、颜色、图标、列名、编辑范围、定义范围;接口表的函数名带下标、路径里的 `{user_id}` 各 `refers` 到各自函数的参数、方法的颜色;接口卡片的「路由器」行排在说明后面、名字带下标指回路由器;写了依赖、默认响应才有这两行,`FastAPI()` 没有前缀,属性 docstring 是说明,没挂接口时「0 个」、不画表格。 - React 组件(`rules/reactTree.test.ts`、`rules/react.test.ts`,luffy 的七个真实组件):认组件(七个文件里的组件、小写工具函数和不渲染 JSX 的不认;memo / forwardRef 包着的、默认导出、文件末尾 export 的、null 和 JSX 混着返回的);属性(行内类型字面量、同文件接口带 JSDoc、回调、children、默认值、改名、其余属性、方法签名、`FC`、`forwardRef`、导入的类型查不到和查到了);组件体归到哪儿、名字用在哪(副作用、渲染时重置、挂在 `
` 的 ref、处理函数绑在每个按钮上);渲染大纲(原生标签、组件、类、条件、三元、列表、片段、注释、几处返回)和 className 的写法。卡片:`fixtures/desktopTitlebar.tsx.txt` 逐字同上面的样例、没有留下的子块,`"use client"` 和 JSDoc 不单独成块;LegalDocPage 的各分区逐行、副作用 / 处理函数子卡片和它们放在哪一行、样式小标点开的全文;ShareButton 的 useCallback、只在挂载时的副作用;PosterDialog 没绑在 JSX 上的处理函数;差异:加一个属性、改一个副作用的依赖,组件卡片属性表那一行新加、副作用「时机」逐截标。宿主:`libraries` 空的规则都启用;略去的块不出块、有子块的不能略去;放进卡片里的子块记下行 / 子行,不合约定的记下来。webview:放进卡片里的子块画在那一行下面(`BlockTree.test.tsx`),点子行才展开的默认收着、名称是按钮、展开时那一行垫上种类色;默认折叠、跳到定义 / 上一处下一处 / 只看改动展开的是那一行(`viewState`、`reveal`、`diffView` 的测试);小标签、条件小框、类名小标的样子(`SegmentText.test.tsx`)。 - 规则查别的文件里的定义(`ruleLookups.test.ts`):第一遍当没查到、记下问了什么(同一索引同名的只记一处);查到后叫重排,交出定义处正是的那一项;同一版本不再查;索引里没有同名的不问语言扩展,定义处不是索引里的项算没有,没变不叫重排;新版本先用上一版的、再查、变了才叫重排;语言扩展没答时已知的不动、隔一会儿再查。