📝 docs(code-reader): 规格里的 React 钩子换成统一的钩子卡片和自定义钩子卡片,调用树认不出的名字改查引用

This commit is contained in:
cruldra committed 2026-10-16 20:21:20 +11:00
1 parent 6e63b7ba6a
commit 8ec48efcd6
1 file changed
+56 -35
+56 -35
View File
@@ -182,9 +182,9 @@
| health 健康检查 | `activity` | start 启动 | `play` | trait 特质 | `network` |
| impl 实现 | `square-function` | module 模块 | `layers` | attribute 编译属性 | `sliders-horizontal` |
库规则的种类用规则给的图标(`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` 换成卡片,左边因此有两个「实现」「特质」图标。
库规则的种类用规则给的图标(`KindBadge.icon`),跟在语言的种类图标后面:SQLAlchemy 数据表 `database`、数据类 `package`、Pydantic 数据模型 `braces`(和类型块同一个图标,颜色不同)、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`、React 组件 `atom`、钩子和自定义钩子 `fishing-hook`、事件处理函数 `mouse-pointer-click`、渲染输出 `layout-template`、serde 可序列化 `braces`、thiserror 错误 `triangle-alert`、axum 接口 `globe`、sqlx 查询 `database`、tauri 桌面命令 `square-function`。数据类写了 `frozen=True` 再加一把锁(`lock`,悬停「不可修改」);React 自带的钩子再加一个作用图标(状态 `toggle-right`、副作用 `zap`、引用 `anchor`、性能 `sigma`、上下文 `share-2`、并发 `hourglass`、外部数据源 `database`)。钩子卡片、自定义钩子卡片不带语言的种类图标,左边第一个就是钩子图标(`RuleCard.languageConcepts: false`)。实现块、特质块再由规则 `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 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置、编译属性共用一个,字段、标签共用一个,类型、接口共用一个——加库规则用的 16 个(React 的组件、钩子、钩子的七种作用、事件处理函数、渲染输出用到其中 11 个),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` 挂到元素上,读屏当描述念。
- 种类图标和底色按种类区分(颜色见「颜色」);文件开头的说明(种类「注释」)虚线框。
@@ -1256,7 +1256,7 @@ export interface Translator {
export interface ThemeColor { dark: string, light: string }
/** 种类图标:插件自己定义的一组 lucide 图标(kebab-case 名字);前 26 个给内置种类,后面的给库规则。规则要新图标就加进这组,没有按任意名字找图标的路。 */
export const KIND_ICON_NAMES = ['import', 'square-function', /* …26 个内置种类的… */ 'database', 'package', 'server-cog', 'calendar-clock', 'globe', 'network', 'atom', 'zap', 'mouse-pointer-click', 'toggle-right', 'anchor', 'download', 'sigma', 'layout-template'] as const
export const KIND_ICON_NAMES = ['import', 'square-function', /* …26 个内置种类的… */ 'database', 'package', 'server-cog', 'calendar-clock', 'globe', 'network', 'atom', 'zap', 'mouse-pointer-click', 'toggle-right', 'anchor', 'sigma', 'layout-template', 'fishing-hook', 'share-2', 'hourglass'] as const
export type KindIconName = (typeof KIND_ICON_NAMES)[number]
/** 内置种类 → 图标名。宿主写进概念列表,webview 不自己查。 */
@@ -1279,6 +1279,9 @@ export interface RuleCard {
kind: string // 种类 id,「规则 id.名字」(sqlalchemy.table):筛选的 kind 值,要稳定
badge: KindBadge // 种类的名字、颜色、图标;筛选栏里这个种类也叫这个名字,卡片边框用这个颜色
concepts?: BlockConcept[] // 额外认出的概念,排在种类后面;语言的概念仍在更前面,同一个 id 只留第一个
languageConcepts?: false // 填 false 时左边不带语言的概念,从种类画起(React 钩子卡片左边第一个是钩子图标)
tint?: ThemeColor // 卡片淡底的颜色,不填用种类色;边框仍是种类色(React 钩子卡片描边都是钩子紫,淡底按来源分)
calls?: SourceRange // 能看调用树,查这个名字被谁调用(钩子卡片查返回的 setOpen,自定义钩子卡片查钩子自己);BlockView.callTree.name 就是它
rows: PropertyRow[]
defines?: SourceRange // 块自己的名字(类名):跳到的定义是它时闪整块
attributes?: BlockAttributes // 规则自己的筛选属性,叠在宿主从原块推出的上面;kind 填了不算;名字由 LibraryRule.attributeNames 给
@@ -1838,34 +1841,32 @@ export interface CommandRule {
### 内置:React 组件(TypeScript / JavaScript)
`vscode/src/codeReader/rules/react.ts`(读语法树的办法在 `rules/reactTree.ts`,都是纯函数),`languageIds: ['typescriptreact', 'javascriptreact']`,`libraries: []`(新的 JSX 写法不用导入 react,TSX / JSX 文件都启用)。原型:`docs/prototype/code-reader/openpencil/react.js` 的三张画板「编辑器全貌」「卡片种类」「对照」(导出图 `exports/react-main.png`、`react-kinds.png`、`react-compare.png`);样例是 luffy 的 `chat/code/bubbles/QueuedBubble.tsx`(`fixtures/queuedBubble.tsx.txt`)和 `chat/code/PlanChecklist.tsx` 第 91–226 行(`fixtures/planChecklist.tsx.txt`,前面带上文件开头的导入),以前的 `DesktopTitlebar`、`LegalDocPage`、`DeleteAppDialog`、`DesktopSettingsDialog`、`AppVaultSummary`、`ShareButton`、`PosterDialog`、`OverviewSection` 也在 `fixtures/`。
`vscode/src/codeReader/rules/react.ts`(读语法树的办法在 `rules/reactTree.ts`,都是纯函数),`languageIds: ['typescriptreact', 'javascriptreact']`,`libraries: []`(新的 JSX 写法不用导入 react,TSX / JSX 文件都启用)。原型:`docs/prototype/code-reader/openpencil/react.js` 的三张画板「编辑器全貌」「卡片种类」「对照」(导出图 `exports/react-main.png`、`react-kinds.png`、`react-compare.png`),钩子卡片照 `react-hooks.js` 的两张画板(导出图 `exports/react-hooks-panel.png`、`react-hooks-custom.png`,样例源码同 `fixtures/hooksPanel.tsx.txt`);样例是 luffy 的 `chat/code/bubbles/QueuedBubble.tsx`(`fixtures/queuedBubble.tsx.txt`)和 `chat/code/PlanChecklist.tsx` 第 91–226 行(`fixtures/planChecklist.tsx.txt`,前面带上文件开头的导入),以前的 `DesktopTitlebar`、`LegalDocPage`、`DeleteAppDialog`、`DesktopSettingsDialog`、`AppVaultSummary`、`ShareButton`、`PosterDialog`、`OverviewSection` 也在 `fixtures/`。
TSX 和 Python 一样,从上往下一块语法一张卡片,嵌套的块排在父卡片下面,缩进、竖线、折叠同函数体。规则只换掉 React 的几种写法,别的块(导入、接口、类型、常量、普通变量、if、尝试……)照语言默认的卡片,注释照常挂在它讲的卡片上。组件卡片不把组件体并进来:组件体的语句照源码顺序是它的子块,照常交给各规则看。表格的格子里只写名字和位置,函数体只出现在子卡片里。
TSX 和 Python 一样,从上往下一块语法一张卡片,嵌套的块排在父卡片下面,缩进、竖线、折叠同函数体。规则只换掉 React 的几种写法,别的块(导入、接口、类型、常量、普通变量、if、尝试……)照语言默认的卡片,注释照常挂在它讲的卡片上。组件卡片、自定义钩子卡片不把函数体并进来:语句照源码顺序是它的子块,照常交给各规则看。表格的格子里只写名字和位置,函数体只出现在子卡片里。
卡片种类(颜色写暗 / 亮):
| 种类 id | 名字 | 图标 | 颜色 | 认哪一块 | 行 |
| --- | --- | --- | --- | --- | --- |
| `react.component` | React 组件 | `atom` | #61dafb / #0a7ea4 | 组件的声明 | 名称 / 说明 / 构造参数 |
| `react.state` | 状态 | `toggle-right` | #9cdcfe / #001080 | `const [v, setV] = useState(…)`、`useReducer(…)` | 名称 / 初始值 / 更新位置 |
| `react.ref` | 引用 | `anchor` | #9cdcfe / #001080 | `useRef(…)` | 名称 / 初始值 / 使用位置 |
| `react.source` | 数据源 | `download` | #9cdcfe / #001080 | 别的钩子赋给变量(`useQuery`、`usePathname`、`useRouter`、自定义 `useXxx`) | 变量 / 钩子 / 说明 |
| `react.derived` | 派生值 | `sigma` | #9cdcfe / #001080 | `useMemo`、`useCallback` | 名称 / 计算表达式 / 依赖项 |
| `react.effect` | 副作用 | `zap` | #e5c07b / #9a6a00 | `useEffect`、`useLayoutEffect`、`useInsertionEffect` | 触发时机 / 清理函数 |
| `react.hook` | 钩子 | `fishing-hook` | #b180d7 / #7f3fa6 | 直接写在组件体、自定义钩子体里的钩子调用:`const x = useX(…)`、`const [a, b] = useX(…)`、整句的 `useEffect(…)` | 钩子 / 类型参数 / 参数 / 返回 |
| `react.customHook` | 自定义钩子 | `fishing-hook` | #b180d7 / #7f3fa6 | 自定义钩子的声明(`function useXxx`、`const useXxx = () => …`) | 名称 / 说明 / 参数 / 返回 |
| `react.handler` | 事件处理函数 | `mouse-pointer-click` | #dcdcaa / #795e26 | 组件体里定义的函数(函数声明、值是函数的常量) | 名称 / 说明 / 参数 / 绑定事件(说明里的块标签另有几行,见下) |
| `react.render` | 渲染输出 | `layout-template` | #89d185 / #388a34 | 组件函数自己返回 JSX 的 return | JSX 结构大纲 |
组件色同时用在渲染大纲里的组件、「客户端组件」标签上;状态、引用、数据源、派生值都是数据,共用变量块的浅蓝,靠图标区分;副作用同「选项」块的黄,事件处理函数同「函数」块,渲染输出同「返回」块的绿。筛选栏的「种类」按这几个 id 筛,叫上表的名字。
组件色同时用在渲染大纲里的组件、「客户端组件」标签、React 自带钩子的淡底上;钩子卡片、自定义钩子卡片描边都是钩子紫,淡底按来源分(见下);事件处理函数同「函数」块,渲染输出同「返回」块的绿。筛选栏的「种类」按这几个 id 筛,叫上表的名字。
认哪些是组件:名字大写开头;是函数声明、值是函数的变量(箭头函数、函数表达式,直接返回 JSX 的也算),或被 `memo(…)` / `forwardRef(…)`(`React.memo` 也算,可以套几层)包着的函数;函数自己的 return(嵌套函数里的不算)里有一处返回 JSX(元素、片段,三元、`&&` 的某一支是 JSX 也算;和 `null` 混着返回也算)。小写开头的工具函数、不返回 JSX 的大写函数照常是函数块。组件里又定义的组件自己也是一张组件卡片,不算事件处理函数。
认领(`LibraryRule.claim` 一块一块看,组件卡片的 `absorbs` 是空的):
- 组件块(函数块、只声明一个变量的常量 / 变量块)是组件时换成组件卡片。文件开头的 `"use client"`(文件里有组件时)略去(`RuleOmit`),讲成卡片上的「客户端组件」标签;它挂着注释时不略去(注释没地方放),照常是一张语句卡片。组件上面的 JSDoc 是组件块自己的说明(TypeScript 的 JSDoc 能当常量、变量块的说明,`memo(…)`、直接返回 JSX 的常量块也是),讲成卡片的「说明」。
- 组件体里的块:语法树往上找,块的语句直接是某个组件函数主体(语句块)的子语句才认(`enclosingComponent`:所在的函数经 `componentOfFunction` 找回它的声明,再用认组件的 `componentAt` 核对),按组件体的归类(`bodyOf`)认成状态、引用、数据源、派生值、副作用、事件处理函数。
- 自定义钩子的声明(函数块、只声明一个变量的常量 / 变量块;名字是 `use` 后面接大写字母或数字,只叫 `use` 的不算,`customHookAt`)换成自定义钩子卡片。
- 组件体、钩子体里的块:语法树往上找,块的语句直接是某个组件函数或自定义钩子主体(语句块)的子语句才认(`enclosingComponent` / `enclosingCustomHook`:所在的函数找回声明它的语句,再用 `componentAt` / `customHookAt` 核对)。只声明一个变量、值是钩子调用(`React.useX` 也算),或整句就是一次钩子调用的(`hookCallAt`),认成钩子卡片;组件体里定义的函数认成事件处理函数(`bodyOf`)。值是钩子调用再取属性的(`useX()?.a ?? b`)照常是变量卡片。
- `return` 块:所在的最内层函数(`enclosingFunction`)是组件函数、返回的是 JSX 时认成渲染输出;写在 `if` 里的提前返回 JSX 也算,它是如果卡片下面的子块。返回 `null` 这类的照常是返回块。
- 别的照语言默认:组件体里普通的 `const x = 表达式` 是变量卡片,`if (…) return null` 是如果卡片、下面是返回卡片,注释是它讲的那张卡片的「注释」一行。
- 回调、函数里的语句来自 TypeScript 语言插件本来给的子块:调用的实参是带语句块的函数时(任何调用都一样),那句语句、那个变量的子块就是函数里的语句,所以 `useEffect(() => {…})`、`useCallback(() => {…})`、`useMemo(() => {…})` 的语句排在副作用、派生值卡片下面;事件处理函数是函数块,函数体照常是子块。
- 回调、函数里的语句来自 TypeScript 语言插件本来给的子块:调用的实参是带语句块的函数时(任何调用都一样),那句语句、那个变量的子块就是函数里的语句,所以 `useEffect(() => {…})`、`useCallback(() => {…})`、`useMemo(() => {…})` 的语句(连返回清理函数的那句 return)排在钩子卡片下面;事件处理函数、自定义钩子是函数块,函数体照常是子块。
组件卡片:
@@ -1885,16 +1886,26 @@ TSX 和 Python 一样,从上往下一块语法一张卡片,嵌套的块排
构造参数:属性类型的字段照类型里的顺序一行一个(类型字面量、同文件的接口 / 类型别名,交叉类型各部分接起来,`Readonly<…>`、`PropsWithChildren<…>` 看里面;没标注参数类型时 `const X: FC<Props>` 的泛型实参、`forwardRef<R, Props>` 的第二个泛型实参),解构里有、类型里没有的接在后面,`...rest` 最后(类型写「其余的属性」)。名称是属性名(定义处带下标:解构出来的是解构那处,没解构的是字段;跳到定义只闪这一行),`children` 后面标「子元素」、函数类型的属性(`(x) => R`、方法签名)标「回调」(组件色的小标签),解构时改了名写「(组件内名为 x)」;类型照翻译插件翻,方法签名写成函数签名;默认值取解构的默认值,没有写「—」;必填看类型的 `?`(是 / 否,storage 色),没类型可看时写了默认值的「否」、别的「—」;说明是字段上面的 JSDoc / 注释、这一行行尾的注释(comment 色,几行接成一行)。类型是导入的名字时经工作区索引查它的字段(索引 `react.propTypes`:看工作区里所有 `**/*.{ts,tsx}`,预筛有 `interface X` / `type X` 才解析,按 typescriptreact 解析;从文件顶层认出接口、类型别名,键是名字、值是各字段的名字、类型写法、可省略、回调、说明的 JSON),查法同「规则认领时查别的文件里的定义」:先照解构的出行、类型「未标注」,查到了按字段重排、重发;别的文件里的类型照写文字(type 色)。
状态、引用、数据源、派生值(名称、变量带下标,解构模式照常翻;跳到定义闪这张卡片):
钩子卡片(所有钩子共用一种,不分析钩子里写了什么;行照钩子签名排,没有的行不显示):
- 状态的初始值:useState 的实参、useReducer 的第二个实参(后面括号里写 reducer)。更新位置:调用改状态函数的各处,只写名字和位置,「、」隔开,写出来一样的只留一处,没有时「—」:「副作用」(几个时「副作用 2」,副作用色)、事件处理函数名 / 派生值名 / 变量名 / 数据源的变量名(带下标,⌘ + 点击跳到那张卡片)、「<textarea>(排队消息内容) 的 onChange」(绑在 JSX 属性上;元素写成 `<Tag>`:组件用组件色、能跳到定义(`refers` 到标签名),原生标签灰色,带 `aria-label` / `title` 字符串时后面写上它;在列表里一项项渲染的前面加「每个」)、「渲染期间,当 … 时」(渲染时按条件重置状态的 `if (…) { setX(…) }`,这句 if 照常是如果卡片)、「渲染输出」(JSX 里别处用到)。
- 引用的使用位置:「<textarea>(排队消息内容) 的 ref 属性」,别处用到的「;」接在后面、几处「、」隔开:「<main> 的 ref 属性;副作用 1、副作用 2、handleSectionClick」。
- 数据源的钩子是这次调用,照常翻;说明:库自带的钩子照词表(next/navigation 的 `usePathname` 当前路径、`useRouter` 路由器,用于页面跳转、`useSearchParams` URL 查询参数、`useParams` 路由动态参数、`useSelectedLayoutSegment(s)` 当前激活的下一级 / 各级布局段,react 的 `useId`、`useTransition`、`useSyncExternalStore`、`useOptimistic`、`useActionState`、`useDeferredValue`,react-dom 的 `useFormStatus`);`useContext(X)` 是「X 上下文的值」;传选择器的 `useXxx((s) => s.a.b)` 是「xxx store 中的 a.b」(store 名:导入路径最后一节是 store 时取上一节,`@/lib/desktop/store` 是 desktop;否则去掉 store、use 字样,再不行取钩子名),选择器不是直接取属性的写「基于 xxx store 计算 …」;自己项目里的(相对路径、`@/` 这类别名、同文件)是「自定义钩子」,别的库的是「xxx 提供的钩子」(hint)。
- 派生值的计算表达式:useMemo 是算值的那个表达式(照常翻),主体是语句块时写「N 条语句」;useCallback 记住的函数主体是语句块时写「函数(N 条语句)」,直接返回表达式的照常翻整个函数。依赖项:依赖数组的各项「、」隔开,后面写「(useMemo)」「(useCallback)」;空数组写「无」,没写依赖数组写「未指定」。主体是语句块时里面的语句是这张卡片的子块。
| 行 | 内容 |
| --- | --- |
| 钩子 | 被调的钩子名(function 色,带下标:自己写的能跳到定义);第三方钩子悬停「来自 next/navigation」(`Segment.tooltip`) |
| 类型参数 | 源码写了 `useRef<HTMLDivElement>` 才有,照常翻(`number` 是「数值」),几个「, 」隔开 |
| 参数 | `N 个`(hint),下面「参数 \| 值」表,一个实参一行;一行都没有时不出这一行 |
| 返回 | `N 个`(hint),下面接住返回值的各项一行:React 自带、第三方的钩子是「名称 \| 是什么」表,自己写的只有「名称」一列;没接返回值时不出这一行 |
副作用:触发时机按依赖数组读,`[a, b]` 是「a、b 变化时」,`[]` 是「仅挂载时」,没写是「每次渲染后」(keyword 色),依赖写成别的表达式时「deps 变化时」;useLayoutEffect 后面加「,浏览器绘制前」。最后返回清理函数时那句 return 并进来,多一行「清理函数」:`() => …` 翻它的主体,语句块一句句翻、「;」隔开。副作用函数里别的语句是子块。
- 钩子从哪来:写成 `React.useX`、从 `'react'` 导入的是 React 自带;从别的包导入的是第三方库;从相对路径、`@/`、`~/`、`#` 开头的别名导入的是自己写的。没导入的:这个文件里定义了这个钩子的是自己写的,签名表里有的是 React 自带(自动导入 React 钩子的项目),别的也算自己写的。
- 签名表(`rules/reactHooks.ts` 的 `REACT_HOOKS`,每个钩子登记一次):作用分类(状态 / 副作用 / 引用 / 性能 / 上下文 / 并发 / 外部数据源)、形参名照 `@types/react`(useEffect 的 `effect, deps`,useReducer 的 `reducer, initialState`,传三个实参时换成重载的 `reducer, initialArg, init`)、每个形参的种类(普通值 / 可以是函数的初始值 / 函数 / 依赖项 / 引用)、没传时怎么写(依赖项「没写,每次渲染后」,useRef 的 initialValue「没写,未定义」;没给说法的参数没传就不出那一行)、返回各项的说法(useState「当前值」「修改函数」,useReducer「当前值」「派发函数」,useRef「引用对象」,useMemo「算出的值」,useCallback「记住的函数」,useContext「上下文的值」,useTransition「是否正在过渡」「启动过渡的函数」……)和哪几项是函数。登记了 react 包的全部钩子:useState、useReducer、useActionState、useOptimistic、useId、useContext、use、useRef、useImperativeHandle、useEffect、useLayoutEffect、useInsertionEffect、useEffectEvent、useDebugValue、useMemo、useCallback、useTransition、useDeferredValue、useSyncExternalStore。
- 参数表的「参数」一格:React 自带的照签名表的形参名,自己写的照定义处的形参(同文件的从语法树读,别的文件的经工作区索引 `react.customHooks` 查:看 `**/*.{ts,tsx,js,jsx}`,预筛有 `useX` 字样才解析,从文件顶层认出自定义钩子,键是名字、值是各形参写法的 JSON;查法同构造参数的类型,先照位置写、查到了重排),都不知道时照位置写「第 1 个」。
- 「值」一格按形参的种类:依赖项 `[a, b]` 是「a、b 变化时」、`[]` 是「仅挂载时」、写成别的表达式时「deps 变化时」(「变化时」「仅挂载时」keyword 色);可以是函数的初始值传的函数直接返回一次调用时写「首次渲染时调用 readOpen(id)」;写在这里的函数主体是语句块时收成 fold 小框「函数 (next: 布尔) · 2 句」(没参数不写括号),函数体照常是卡片的子块,展开收起交给卡片自己的折叠箭头(小框不在链式调用的某一步里,只当摘要);直接返回表达式的函数没有子块可放,照常翻整个函数;函数、引用种类传的是名字时(reducer、ThemeContext)照抄成带源码下标的名字(函数的名字 function 色);别的照常翻(`null` 是「空」,`[]` 是「空数组」)。
- 返回表:变量名接住的是整个返回值(返回元组的钩子「是什么」写全部各项「当前值、修改函数」),数组解构照位置(空着的位置跳过,`...rest` 前面带 `...`),对象解构一个键一项(「是什么」写「—」)。名称带下标(跳到定义只闪这一行),返回里是函数的那项用 function 色。第三方钩子的「是什么」照词表(next/navigation 的 `usePathname` 当前路径、`useRouter` 路由器,用于页面跳转、`useSearchParams` URL 查询参数、`useParams` 路由动态参数、`useSelectedLayoutSegment(s)` 当前激活的下一级 / 各级布局段,next/router 的 `useRouter`,react-dom 的 `useFormStatus`),变量名整个接住时才写,别的「—」。
- 样子:左边第一个是钩子图标 `fishing-hook`(钩子紫,不带它所在那句常量、语句的图标),React 自带的再加一个作用图标(`RuleCard.concepts`,悬停分类名;useLayoutEffect 悬停「副作用 · 浏览器绘制前执行」,useInsertionEffect「副作用 · 布局副作用之前执行」):状态 `toggle-right` #9cdcfe / #001080、副作用 `zap` #e5c07b / #9a6a00、引用 `anchor` #4fc1ff / #0070c1、性能 `sigma` #b5cea8 / #098658、上下文 `share-2` #4ec9b0 / #267f99、并发 `hourglass` #ce9178 / #a31515、外部数据源 `database` #dcdcaa / #795e26。描边是钩子紫,淡底按来源(`RuleCard.tint`,webview 的 `.cr-block[data-tint]`):React 自带 #61dafb / #0a7ea4、第三方 #e5c07b / #9a6a00、自己写的 #89d185 / #388a34。只用一个变量名接住返回值时那个名字是卡片自己的名字(`defines`)。
- 调用树:返回里有函数的钩子卡片带调用树按钮(`RuleCard.calls` → `BlockView.callTree.name`),查接住那个函数的名字:useState 查 setOpen,useReducer 查 dispatch,useTransition 查 startTransition,useCallback 查 toggle。TypeScript 的调用层级认不出这种名字(数组解构出来的、`const x = useCallback(…)` 的,`vscode.prepareCallHierarchy` 给空的),调用树服务这时改查引用(`callTree/referenceCalls.ts`):`vscode.executeReferenceProvider` 查名字的各处引用,只留调用处(名字后面紧跟实参的括号,中间可以隔着 `?.`、泛型实参),每处在文件大纲(`vscode.executeDocumentSymbolProvider`)里从里往外找第一个调用层级认得的符号当调用者(`setOpen(next)` 写在 `useCallback` 的回调里,调用者是组件 Panel;`startTransition(…)` 在 handleClick 里,调用者是 handleClick),都不认得时按文件算,再从调用者往上照常问编辑器。
事件处理函数:名称是函数名(function 色,带下标);参数照常翻(`e: KeyboardEvent<HTMLDivElement>`),几个「, 」隔开,没有写「无」;绑定事件从 JSX 属性反查:「<div>(修改排队消息) 的 onKeyDown」,在属性值里调用时写传入的实参「(传入 h.id)」,在列表里一项项渲染的写「每个 <button>」,几处「、」隔开;没绑在 JSX 上时写由谁调用「由 download、share 调用」,都没有「—」。函数里的语句是子块(如果、返回、调用、变量、尝试……),注释挂在它讲的那块上。
自定义钩子卡片:名称(function 色,带下标,后面跟「具名导出」「默认导出」小标签,同组件卡片)/ 说明(JSDoc 的正文)/ 对不上的参数 / 参数(「名称 \| 类型」表,有参数带默认值时多一列默认值,说明里有参数对上了 `@param` 时多一列说明;没有参数写「无」)/ 返回(只列名称:从最后那句 return 读,对象写的取各个键(简写的、改了名的),数组取各项,标识符是它自己,直接返回表达式的箭头函数读那个表达式;读不出、说明里也没写 `@return` 时不出这一行)/ 异常。样子同钩子卡片(钩子图标、钩子紫描边、自己写的淡底),能看调用树(查这个钩子在哪被调用);钩子体里的钩子调用是钩子卡片。
事件处理函数:名称是函数名(function 色,带下标);参数照常翻(`e: KeyboardEvent<HTMLDivElement>`),几个「, 」隔开,没有写「无」;绑定事件从 JSX 属性反查:「<div>(修改排队消息) 的 onKeyDown」,在属性值里调用时写传入的实参「(传入 h.id)」,在列表里一项项渲染的写「每个 <button>」,几处「、」隔开;没绑在 JSX 上时写由谁调用「由 download、share 调用」(在钩子调用里时写接住返回值的变量名,没用一个名字接住的写钩子名,同一个钩子这样写了几处时后面跟第几处、钩子紫),都没有「—」。函数里的语句是子块(如果、返回、调用、变量、尝试……),注释挂在它讲的那块上。
渲染输出(「JSX 结构大纲」一行下面的子行,一行一个元素,下一层缩进;带下一层的行有折叠箭头):
@@ -1904,7 +1915,7 @@ TSX 和 Python 一样,从上往下一块语法一张卡片,嵌套的块排
- 子节点只有文字、插值时写在同一行:只有一段文字时带引号,插值写成 `{…}`(花括号 hint 色),和文字混着时文字不带引号。
- `cond && <X/>` 前面加「当 … 时」小框(`condition`,条件照常翻);三元一行「如果 …」,下面第一行各带「是」「否」小框(`branch`),一支是 null 时写「不渲染」;`items.map((x) => …)` 一行「对 items 的每一项 x」(`iteration` 小框)后面「key …」,回调里先算的常量写「其中 isActive = …」,再下面是每一项渲染的;JSX 注释写成一行「// …」(comment 色)。
每张卡片「编辑」是它那条语句的完整代码,组件卡片是整个组件(盖住它的子块);组件体里的卡片各自能编辑、问 AI。代码差异在视图数据这一层比:组件卡片的属性表逐行(属性表按名称配对,加的属性标新加的一行),组件体里的卡片是块,照常配对、逐行标(改了依赖,「触发时机」那一行逐截标)。
每张卡片「编辑」是它那条语句的完整代码,组件卡片是整个组件(盖住它的子块);组件体里的卡片各自能编辑、问 AI。代码差异在视图数据这一层比:组件卡片的属性表逐行(属性表按名称配对,加的属性标新加的一行),组件体里的卡片是块,照常配对、逐行标(改了依赖,钩子卡片参数表 deps 那一格逐截标)。
样例(`fixtures/planChecklist.tsx.txt` 的 `PlanChecklist`,测试里逐字核对;`⟦…⟧` 是小标签、小框,子块多缩进两格,说明只写第一行):
@@ -1915,21 +1926,30 @@ react.component 23
构造参数: 2 个 · 类型 PlanChecklistProps
todos | PlanTodo[] | — | 是 | 执行期计划清单 (覆盖式刷新); 空数组时不渲染.
active | 布尔 | — | 是 | 本轮是否仍在流式执行中. ``in_progress`` 仅在 ``active`` 时转圈; …
react.state 35
名称: collapsed
初始值: 假
更新位置: 副作用、toggle
react.effect 37
触发时机: 仅挂载时
react.hook 35
钩子: useState
参数: 1 个
initialState | 假
返回: 2 个
collapsed | 当前值
setCollapsed | 修改函数
react.hook 37
钩子: useEffect
参数: 2 个
effect | ⟦函数 · 1 句⟧
deps | 仅挂载时
try 38: 尝试
if 39: 如果 (localStorage.getItem(PLAN_CHECKLIST_COLLAPSED_KEY) 全等 "1")
statement 40: queueMicrotask(() => setCollapsed(真));
catch 42: 捕获
注释: localStorage 不可用 (隐私模式等) 时静默用默认
react.derived 47
名称: toggle
计算表达式: 函数(3 条语句)
依赖项: collapsed(useCallback)
react.hook 47
钩子: useCallback
参数: 2 个
callback | ⟦函数 · 3 句⟧
deps | collapsed 变化时
返回: 1 个
toggle | 记住的函数
variable 48
名称: next
初始值: collapsed 不成立
@@ -1952,7 +1972,7 @@ react.component 23
…
```
和原型不一样的地方:接口 `PlanChecklistProps` 照 TypeScript 的接口块(一行译文,字段是子块,注释挂在讲的字段上),原型「对照」里画成的字段表(名称 / 类型 / 必填 / 说明)这次没做;渲染输出的大纲挂在「JSX 结构大纲」这一行下面,不是直接画在卡片里;更新位置、使用位置里的「副作用」是副作用色的字,不能跳(副作用没有名字可查定义),原生标签(`<textarea>`)也不能跳,组件标签能跳到组件定义。
和原型不一样的地方:接口 `PlanChecklistProps` 照 TypeScript 的接口块(一行译文,字段是子块,注释挂在讲的字段上),原型「对照」里画成的字段表(名称 / 类型 / 必填 / 说明)这次没做;渲染输出的大纲挂在「JSX 结构大纲」这一行下面,不是直接画在卡片里;原生标签(`<textarea>`)不能跳,组件标签能跳到组件定义。钩子原型(`react-hooks.js`)里:自定义钩子卡片的返回表画了「是什么」一列(「状态 messages」),这次只列名称;`createContext` 的「上下文」卡片没做,照常是常量卡片;参数里的函数小框画成能点开,这次只当摘要,展开收起用卡片的折叠箭头。
### 内置:Rust 的库
@@ -2349,8 +2369,9 @@ vscode/src/codeReader/
rules/enum.ts 内置 enum 库规则(Python):枚举类认成枚举块,成员照 Python 的规则认
rules/dataclass.ts 内置 dataclasses 库规则(Python):数据类卡片,字段照 dataclasses 的规则认
rules/fastapi.ts 内置 FastAPI 库规则(Python):路由器卡片(接口表)、接口卡片(请求表、响应表),参数照 FastAPI 的规则放,异常类的状态码、类型别名、枚举经索引查
rules/react.ts 内置 React 库规则(TSX / JSX):一块一张卡片——组件卡片(名称标签、说明、构造参数),组件体里的状态 / 引用 / 数据源 / 派生值 / 副作用 / 事件处理函数 / 渲染输出卡片,略去 "use client" 和组件的 JSDoc,导入的属性类型经索引查
rules/reactTree.ts React 规则读语法树的办法:认组件、构造参数、一块语句属于哪个组件体(enclosingComponent)、组件体的归类、名字用在哪、渲染的 JSX 大纲、别的文件里的属性类型(纯函数)
rules/react.ts 内置 React 库规则(TSX / JSX):一块一张卡片——组件卡片(名称标签、说明、构造参数)、自定义钩子卡片,组件体和钩子体里的钩子卡片、组件体里的事件处理函数 / 渲染输出卡片,略去 "use client" 和组件的 JSDoc,导入的属性类型、别的文件里自定义钩子的形参经索引查
rules/reactHooks.ts React 自带钩子的签名表:作用分类、形参名和种类、没传时怎么写、返回各项的说法、哪几项是函数
rules/reactTree.ts React 规则读语法树的办法:认组件和自定义钩子、构造参数、一块语句属于哪个组件体 / 钩子体(enclosingComponent / enclosingCustomHook)、钩子调用、组件体的归类、名字用在哪、自定义钩子返回的各项、渲染的 JSX 大纲、别的文件里的属性类型(纯函数)
commandRules.ts 命令规则的宿主部分:命令名 → 规则(先注册的算),规则出错记下、当它没读
commands/reading.ts 命令规则共用的:认选项(连写拆开、带值的、长选项、--)、拼译文(动作、括号里的意思、照抄不认识的选项)
commands/systemCommands.ts 内置命令规则:新建用户 / 用户组(useradd、adduser、addgroup / groupadd)、gpg、install、chown,管 '*' 的只问版本的命令
@@ -2502,7 +2523,7 @@ vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vit
- 数据类(`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 混着返回的;组件体里的语句、提前返回的 if 里的 return 认得出在哪个组件里);构造参数(行内类型字面量、同文件接口带 JSDoc、回调、children、默认值、改名、其余属性、方法签名、`FC<Props>`、`forwardRef<R, Props>`、导入的类型查不到和查到了);组件体的归类、名字用在哪(副作用、渲染时重置、挂在 `<main>` 的 ref、处理函数绑在每个按钮上);渲染大纲(原生标签、组件、类、条件、三元、列表、片段、注释、几处返回)和 className 的写法。卡片:组件卡片只有名称 / 说明 / 构造参数,组件体照源码顺序是子块,`"use client"` 不单独成块(文件里没组件时、它挂着注释时照常成块),JSDoc 是组件块的说明;`fixtures/planChecklist.tsx.txt` 逐字同上面的样例;QueuedBubble 的状态、引用、变量、副作用、两个事件处理函数、渲染输出照源码顺序、函数体是子卡片,提前 `return null` 是如果卡片;状态的更新位置、引用的使用位置只写名字和位置;数据源的几种说明;useMemo / useCallback 的派生值、带清理的副作用、触发时机的几种写法、没绑在 JSX 上的处理函数写由谁调用;直接返回 JSX 的组件把大纲写在卡片里;九个文件认领下来都合约定;差异:加一个属性、改一个副作用的依赖,组件卡片属性表那一行新加、副作用卡片「触发时机」逐截标。宿主:`libraries` 空的规则都启用;略去的块不出块、有子块的不能略去。webview:小标签、条件小框、类名小标的样子(`SegmentText.test.tsx`)。
- React 组件(`rules/reactTree.test.ts`、`rules/react.test.ts`,luffy 的九个真实组件):认组件(七个文件里的组件、小写工具函数和不渲染 JSX 的不认;memo / forwardRef 包着的、默认导出、文件末尾 export 的、null 和 JSX 混着返回的;组件体里的语句、提前返回的 if 里的 return 认得出在哪个组件里);构造参数(行内类型字面量、同文件接口带 JSDoc、回调、children、默认值、改名、其余属性、方法签名、`FC<Props>`、`forwardRef<R, Props>`、导入的类型查不到和查到了);组件体的归类、钩子调用、自定义钩子(认哪些、钩子体里的语句、返回的各项)、名字用在哪(钩子调用、处理函数绑在每个按钮上);渲染大纲(原生标签、组件、类、条件、三元、列表、片段、注释、几处返回)和 className 的写法。卡片:组件卡片只有名称 / 说明 / 构造参数,组件体照源码顺序是子块,`"use client"` 不单独成块(文件里没组件时、它挂着注释时照常成块),JSDoc 是组件块的说明;`fixtures/planChecklist.tsx.txt` 逐字同上面的样例;QueuedBubble 的钩子、变量、两个事件处理函数、渲染输出照源码顺序、函数体是子卡片,提前 `return null` 是如果卡片;钩子卡片(`fixtures/hooksPanel.tsx.txt`,原型 `react-hooks.js` 的样例)的各行、形参名、依赖项 / 惰性初始值 / 函数参数的写法、返回表、三种来源的淡底、作用图标只有内置钩子有、调用树查的名字,签名表的重载和没传的参数,别的文件里自己写的钩子经索引查形参名;自定义钩子卡片和钩子体里的钩子;没绑在 JSX 上的处理函数写由谁调用;直接返回 JSX 的组件把大纲写在卡片里;九个文件认领下来都合约定;差异:加一个属性、改一个副作用的依赖,组件卡片属性表那一行新加、钩子卡片参数表 deps 那一格逐截标。调用树改查引用(`callTree/referenceCalls.test.ts`:只留调用处、往外找第一个认得的调用者、文件顶层按文件算)。宿主:`libraries` 空的规则都启用;略去的块不出块、有子块的不能略去。webview:小标签、条件小框、类名小标的样子(`SegmentText.test.tsx`)。
- 规则查别的文件里的定义(`ruleLookups.test.ts`):第一遍当没查到、记下问了什么(同一索引同名的只记一处);查到后叫重排,交出定义处正是的那一项;同一版本不再查;索引里没有同名的不问语言扩展,定义处不是索引里的项算没有,没变不叫重排;新版本先用上一版的、再查、变了才叫重排;语言扩展没答时已知的不动、隔一会儿再查。
- 带自己颜色的词(webview `SegmentText.test.tsx`):`cr-colored`,暗色、亮色写在 style 上;讲别处名字的段 `data-offset` 记那个名字的下标。
- 异常(`languages/exceptions.test.ts`,真实解析 + 中文直译 + 视图数据):`fixtures/appExceptions.py` 的 `AppException`、`NotFound` 逐字同「异常」的样例(列名随有没有类型、说明变),`GatewayTimeout` 的多段说明,方法是子块;`fixtures/commerceExceptions.py` 16 个类都是异常、只有说明的四行、写了 `__init__` 的构造参数;认哪些类(自带的异常类、`Warning`、不合后缀的自带类 `StopIteration` / `KeyboardInterrupt`、`pkg.errors.AppException`、同一文件里写在后面的父类一层层往下传、泛型和别的父类混写,关键字参数、`Mapping[...]`、`*bases` 不算,函数里的类也认);类属性(带不带注解、只写注解、说明取讲它的注释 / 行尾注释 / 属性 docstring,连着赋值、解构赋值、方法留作子块,讲方法的注释、类主体最后的注释挂在方法、最后一块上),构造参数去掉 self、`*args` 开头的不去;`@dataclass` 的异常类照样是异常卡片;编辑范围是整个类、改卡片写回整个类,类名、属性名、参数名的定义范围和带下标的词指回源码原文;TypeScript `fixtures/exceptions.ts.txt`(`extends Error`、类字段含 static、参数属性、JSDoc、写在后面的父类、类表达式、`ns.X<T>`,父类名不合习惯的照常是类)、JavaScript 的类字段;TypeScript 和 Python 同样的异常得到一样的行和列名;筛选的种类叫「异常」、没有 badge;属性的中文名和去掉行尾记号。webview:异常块默认折叠(`viewState.test.ts`),跳到类名闪整块、属性名 / 构造参数名只闪表里那一行(`reveal.test.ts`)。