📝 docs(code-reader): React 钩子卡片按变量讲、属性表操作列、调用树查所有引用的规格

This commit is contained in:
cruldra committed 2026-10-16 22:16:13 +11:00
1 parent 86beaac371
commit fe6e2ce134
1 file changed
+121 -49
+121 -49
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`、钩子和自定义钩子 `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` 换成卡片,左边因此有两个「实现」「特质」图标。
库规则的种类用规则给的图标(`KindBadge.icon`),跟在语言的种类图标后面:SQLAlchemy 数据表 `database`、数据类 `package`、Pydantic 数据模型 `braces`(和类型块同一个图标,颜色不同)、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`、React 组件 `atom`、自定义钩子和钩子返回值 / 钩子调用 `fishing-hook`、状态 `toggle-right`、引用 `anchor`、记忆值和记忆函数 `sigma`、上下文值 `share-2`、过渡 `hourglass`、副作用 `zap`、事件处理函数 `mouse-pointer-click`、渲染输出 `layout-template`、serde 可序列化 `braces`、thiserror 错误 `triangle-alert`、axum 接口 `globe`、sqlx 查询 `database`、tauri 桌面命令 `square-function`。数据类写了 `frozen=True` 再加一把锁(`lock`,悬停「不可修改」);没有自己那种卡片的 React 内置钩子(useId、useDeferredValue……)讲成钩子返回值时再加一个作用图标(状态、副作用、引用、性能、上下文、并发,外部数据源 `database`)。规则卡片可以让语言的种类图标照另一种内置种类画(`RuleCard.languageKind`):React 的记忆函数(`const submit = useCallback(…)`,语言报的是变量)第一个图标是函数。种类图标悬停的字、图标颜色可以和筛选栏里的名字、描边色不一样(`KindBadge.tooltip`、`iconColor`):状态悬停「React 状态」,记忆函数描边是函数色、sigma 图标是记忆值的绿。实现块、特质块再由规则 `rust.impl`、`rust.trait` 换成卡片,左边因此有两个「实现」「特质」图标。
这组图标由插件自己定义,是一个封闭的集合:契约里的 `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 配上图标,没有按任意名字找图标的路。
这组图标由插件自己定义,是一个封闭的集合:契约里的 `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` 挂到元素上,读屏当描述念。
- 种类图标和底色按种类区分(颜色见「颜色」);文件开头的说明(种类「注释」)虚线框。
@@ -257,6 +257,8 @@
输入参数的表格(原型 `paramRows`):往右缩进 5px,第一行是表头「名称 | 类型 | 默认值」,下面每个参数一行。列宽按整列的内容定,webview 不认是哪种表格(库规则的字段表同样排,见 `tableTemplate`):整张表格一个网格、各行借用它的列,前几列宽裕时放到整列不换行,最后一列(默认值、说明)分剩下的、至少留 `min(20%, 8em)`;放不下时先让最后一列换行,编辑区再窄前几列平分剩下的宽度、在格子里换行(长名字从中间断开),不撑出卡片、不藏掉哪一列。格子左右留 8px、上下 2px,长类型、长默认值在格子里换行,不挤掉别的列。外框 1px、圆角 4px,格子之间、行之间 1px 分隔线,颜色都是主题的表格分隔色(`--vscode-tree-tableColumnsBorder`,主题没给时退到 `--vscode-editorWidget-border`);表头垫主题的表头底色(`--vscode-keybindingTable-headerBackground`,比编辑区深一档或浅一档),列名界面字体 12px、`descriptionForeground` 色。没有默认值的格子留空。格子可以是 Markdown(参数的「说明」、「异常」表的两列、对不上的参数的说明):按「注释的 Markdown 排版」显示(界面字体、注释色),着色的词照旧排在等宽字体的段落里,差异逐词标。属性行跟着的说明(`PropertyRow.note`,输出参数的 `@return`)接在值后面,同一行放得下就并排,剩下的地方不到 12 个字宽时换到下一行。折叠「输入参数」那一行时整张表收起;子行 id、跳到定义闪「它那个参数」、高亮名字的各处出现都和列表一样,闪的是表格里那一行(行边框和相邻行共用,改在行里面描一圈聚焦色)。哪些子行排成表格由翻译插件定:给了列名(`PropertyRow.columns`)的排成表格,没给的(导入项、字典 / 对象的各项)照旧是列表,webview 只看有没有列名。
表格的行、属性行可以带操作(`PropertyItem.actions`、`PropertyRow.actions`,现在只有看调用树一种,见「内置:React 组件」的钩子卡片):表里有行带操作时,表最后多一列「操作」(列模板在 `tableTemplate` 后面接 `max-content`,宽度按按钮最多的那一行,表头「操作」),各行的按钮在这一格里从左往右排,没有操作的行空着;属性行带操作时(单个名字的「名称」一行)按钮排在这一行最右边。按钮同块右上角的调用树按钮(`BlockTools.tsx` 的 `CallTreeButton`):`git-branch` 图标,次数大于 0 时带小圆标,0 只有图标;悬停、读屏写操作自己的说法(「谁用了这个状态」),弹框开着时带按下的底色。宿主排视图数据时给卡片上的操作挨个起键(`actionKey`:块的记号加序号,顺序是属性表从上往下、一行里先行自己再子行,`blockView.ts` 的 `mapActions`),页面按键对次数、点开调用树(`webview-ui/src/codeReader/rowActions.ts` 的 `RowActionsContext`,每张卡片自己给,子卡片不沿用);定义卡片浮窗、删掉的块的旧内容不给,不出「操作」一列。
| 种类 | 行 |
| --- | --- |
| 函数 | 名称(函数色;匿名函数写「匿名」)/ 说明(有正文才显示:JSDoc 去掉 `/**`、`*/` 和每行开头的 `*`,docstring 去掉引号;再去掉共同缩进和首尾空行,拆掉块标签(见「注释的 Markdown 排版」),剩下的正文按 Markdown 渲染)/ 对不上的参数(说明里有 `@param` 的名字在输入参数里没有时才显示:`N 个`,展开成表格「名称 \| 说明」,名称是 hint 色、悬停「没有这个参数」)/ 访问级别 / 异步(是 / 否)/ 装饰器(有才显示,源码原样照抄、按源码着色,多个换行)/ 输入参数(`N 个` 或「无」,展开成表格,列名 名称 \| 类型 \| 默认值,每个参数一行:名称 \| 类型(没标注写「未标注」)\| 默认值(译文,没有留空);任何一个参数对上了 `@param` 时多一列「说明」(Markdown,没对上的格子留空),按名字对,`*args` / `**kwargs` / `...rest` 去掉前缀对)/ 输出参数(返回类型的译文,没标注写「未标注」;写了 `@return` 时类型后面跟着这段说明,Markdown、注释色)/ 异常(写了 `@throws` 才显示:`N 个`,展开成表格「异常 \| 触发条件」,一条一行,都是 Markdown,异常名能点,见「注释的 Markdown 排版」的引用) |
@@ -1126,6 +1128,24 @@ export interface PropertyItem extends Defines {
cells: PreviewText[]
items?: PropertyItem[]
table?: PreviewTable // 最后一格的值里那次调用的实参表:表格的行排在最后一格里、文字下面,排成列表的子行排在它下面;总是展开,表里的行又能带 items、table
actions?: RowAction[] // 这一行能做的操作:表格的行排在最后多出来的「操作」一列,从左往右;列表的子行没有这一列
}
/** 表格一行、属性一行上能做的一个操作,页面画成一个按钮。现在只有看调用树一种。 */
export type RowAction = CallTreeAction
/**
* 看调用树:查源码里一个名字被谁用、被谁调用,按钮上带次数(0 次只画图标),点开是调用树弹框。
* calls 查谁调用了它(编辑器的调用关系,认不出这个名字时改查引用、只留调用处),次数是落在几棵调用树里;
* references 查所有用到它的地方,按所在的函数、组件分组,次数是用到几处(名字自己的定义处、落在 except 里的不算)。
*/
export interface CallTreeAction {
kind: 'callTree'
name: SourceRange // 查的那个名字
mode: 'calls' | 'references'
label: string // 按钮的悬停文字:「谁用了这个状态」「谁改了这个状态」
except?: SourceRange[] // 不算的地方(React 钩子的依赖数组里写的那一次)
key?: string // 宿主排视图数据时填的键(actionKey(块的记号, 序号)),页面按它对次数、点开调用树;规则不填
}
/**
@@ -1134,7 +1154,7 @@ export interface PropertyItem extends Defines {
* note 是跟在值后面的一段说明(Markdown,注释色):输出参数那一行,返回类型后面是说明里的 `@return`。
* chain 是值(链的起点)后面接着的链式调用各步,排成竖轨道(见「链式调用卡片」)。
*/
export interface PropertyRow extends Defines { label: string, value: PreviewText, note?: Markdown, items?: PropertyItem[], columns?: string[], chain?: ChainStep[] }
export interface PropertyRow extends Defines { label: string, value: PreviewText, note?: Markdown, items?: PropertyItem[], columns?: string[], chain?: ChainStep[], actions?: RowAction[] /* 按钮排在这一行最右边 */ }
/** 一张带表头和边框的表:列名,每行几列对齐的格子,同 PropertyRow 给了 columns 的子行。 */
export interface PreviewTable { columns: string[], items: PropertyItem[] }
@@ -1192,6 +1212,11 @@ export interface LanguageAdapter {
displayNames?: DisplayNameReader
/** 这块除了自己的种类,还认得出的概念(异步、抽象)。认到才返回;不填就只有种类那一个。种类由宿主补在最前面,这里不要再报。 */
concepts?: (block: CodeBlock) => BlockConcept[]
/**
* 一个函数值(箭头函数、函数表达式)的主体分成的块:语句块是各句,直接返回表达式的是一个返回块(节点是那个表达式,翻译插件在译文前补「返回」,见 ZhTable.returnWritten)。
* 库规则要把实参里函数的主体排成卡片的子块时用(RuleCard.expands);不填就排不出来。
*/
functionBody?: (fn: Node, languageId: string) => CodeBlock[]
}
/** 代码里给一个定义起的中文名:name 是定义处那个名字,text 是中文名。 */
@@ -1265,8 +1290,8 @@ export const KIND_ICONS: Record<BlockKind, KindIconName>
/** 一块上认出的一个概念。颜色不填的是语言的种类(按 id 用 CSS 的种类色);规则的概念填颜色,只给这个图标上色。 */
export interface BlockConcept { id: string, label: string, icon: KindIconName, color?: ThemeColor }
/** 库规则的种类:筛选栏里叫 label,卡片边框用 color,icon 进概念列表;icon 只能从 KindIconName 里挑。 */
export interface KindBadge { label: string, color: ThemeColor, icon: KindIconName }
/** 库规则的种类:筛选栏里叫 label,卡片边框用 color,icon 进概念列表;icon 只能从 KindIconName 里挑。tooltip、iconColor 是种类图标悬停的字、图标的颜色,不填同 label、color。 */
export interface KindBadge { label: string, color: ThemeColor, icon: KindIconName, tooltip?: string, iconColor?: ThemeColor }
/** 库规则认领一块后给出的替身,下面挂着原块留下的子块:规则自己的卡片,或认成的现成属性表块;这块讲在了别的卡片里时是略去它。 */
export type RuleBlock = RuleCard | RuleFacts | RuleOmit
@@ -1279,9 +1304,10 @@ 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 就是它
languageKind?: BlockKind // 左边第一个图标(语言的种类)照这种内置种类画,不填是原块自己的种类(React 记忆函数的变量块画成函数)
tint?: ThemeColor // 卡片淡底的颜色,不填用种类色;边框仍是种类色(React 钩子返回值描边是钩子紫,淡底按来源分)
calls?: true // 能看调用树,查卡片自己的名字(defines)被谁调用,按钮在卡片右上角(React 自定义钩子);属性表里各行自己查的名字见 RowAction
expands?: Node[] // 原块里这几个函数值(实参里的箭头函数)的主体也排成卡片的子块(LanguageAdapter.functionBody),语言插件已经排成子块的不重复排
rows: PropertyRow[]
defines?: SourceRange // 块自己的名字(类名):跳到的定义是它时闪整块
attributes?: BlockAttributes // 规则自己的筛选属性,叠在宿主从原块推出的上面;kind 填了不算;名字由 LibraryRule.attributeNames 给
@@ -1842,7 +1868,7 @@ export interface CommandRule {
### 内置:React 组件(TypeScript / JavaScript)
`vscode/src/codeReader/rules/react.ts`(读语法树的办法在 `rules/reactTree.ts`,都是纯函数),`languageIds: ['typescriptreact', 'javascriptreact', 'typescript', 'javascript']`,`libraries: ['react']`,`alwaysIn: ['typescriptreact', 'javascriptreact']`:新的 JSX 写法不用导入 react,TSX / JSX 文件都启用;自定义钩子常写在 .ts / .js 文件里,这两种导入了 react(或它的子模块)才启用,免得把 zustand 的 `useXxx` store 这类当成钩子。.ts / .js 里没有 JSX,认不出组件、渲染输出,只出自定义钩子卡片、钩子卡片(样例 `fixtures/useShellEvents.ts.txt`,luffy 的 `lib/desktop/use-shell-events.ts` 精简版)。原型:`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/`。
`vscode/src/codeReader/rules/react.ts`(读语法树的办法在 `rules/reactTree.ts`,都是纯函数),`languageIds: ['typescriptreact', 'javascriptreact', 'typescript', 'javascript']`,`libraries: ['react']`,`alwaysIn: ['typescriptreact', 'javascriptreact']`:新的 JSX 写法不用导入 react,TSX / JSX 文件都启用;自定义钩子常写在 .ts / .js 文件里,这两种导入了 react(或它的子模块)才启用,免得把 zustand 的 `useXxx` store 这类当成钩子。.ts / .js 里没有 JSX,认不出组件、渲染输出,只出自定义钩子卡片和钩子体里的钩子卡片(样例 `fixtures/useShellEvents.ts.txt`,luffy 的 `lib/desktop/use-shell-events.ts` 精简版)。原型:`docs/prototype/code-reader/openpencil/react.js` 的三张画板「编辑器全貌」「卡片种类」「对照」(导出图 `exports/react-main.png`、`react-kinds.png`、`react-compare.png`),钩子卡片按变量讲,照 `react-state.js` 的三张画板「登录页」「其他写法」「搜索面板」(导出图 `exports/react-state-login.png`、`react-state-variants.png`、`react-state-hooks.png`,样例源码同 `fixtures/loginPage.tsx.txt`(luffy 的 `app/desktop/login/page.tsx`)、`fixtures/stateVariants.tsx.txt`(三行 useState 包进一个组件)、`fixtures/searchPanel.tsx.txt`),自定义钩子卡片照 `react-hooks.js`(导出图 `exports/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、尝试……)照语言默认的卡片,注释照常挂在它讲的卡片上。组件卡片、自定义钩子卡片不把函数体并进来:语句照源码顺序是它的子块,照常交给各规则看。表格的格子里只写名字和位置,函数体只出现在子卡片里。
@@ -1851,12 +1877,20 @@ TSX 和 Python 一样,从上往下一块语法一张卡片,嵌套的块排
| 种类 id | 名字 | 图标 | 颜色 | 认哪一块 | 行 |
| --- | --- | --- | --- | --- | --- |
| `react.component` | React 组件 | `atom` | #61dafb / #0a7ea4 | 组件的声明 | 名称 / 说明 / 构造参数 |
| `react.hook` | 钩子 | `fishing-hook` | #b180d7 / #7f3fa6 | 直接写在组件体、自定义钩子体里的钩子调用:`const x = useX(…)`、`const [a, b] = useX(…)`、整句的 `useEffect(…)` | 钩子 / 类型参数 / 参数 / 返回 |
| `react.state` | 状态(图标悬停「React 状态」) | `toggle-right` | #9cdcfe / #001080 | `useState`、`useReducer` | 名称 / 来自 / 状态类型 / 默认值;useReducer 是 名称 / 来自 / 处理函数 / 状态类型 / 初始值 / 初始化函数 |
| `react.ref` | 引用 | `anchor` | #4fc1ff / #0070c1 | `useRef` | 名称 / 来自 / 指向类型 / 初始值 |
| `react.memo` | 记忆值 | `sigma` | #b5cea8 / #098658 | `useMemo` | 名称 / 来自 / 计算 / 重算时机 |
| `react.memoFn` | 记忆函数 | `sigma`(图标 #b5cea8 / #098658) | #dcdcaa / #795e26 | `useCallback` | 名称 / 来自 / 参数 / 函数体 / 重建时机 |
| `react.context` | 上下文值 | `share-2` | #4ec9b0 / #267f99 | `useContext` | 名称 / 来自 / 上下文 |
| `react.transition` | 过渡 | `hourglass` | #ce9178 / #a31515 | `useTransition` | 名称 / 来自 |
| `react.effect` | 副作用 | `zap` | #e5c07b / #9a6a00 | `useEffect`、`useLayoutEffect`、`useInsertionEffect` | 触发时机 / 来自 / 执行 |
| `react.hookValue` | 钩子返回值 | `fishing-hook` | #b180d7 / #7f3fa6 | 别的钩子(第三方、自己写的、没有自己那种卡片的内置钩子)接住了返回值 | 名称 / 来自 / 说明 / 参数 |
| `react.hookCall` | 钩子调用 | `fishing-hook` | #b180d7 / #7f3fa6 | 别的钩子整句只是调用、没接返回值 | 来自 / 参数 |
| `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 结构大纲 |
组件色同时用在渲染大纲里的组件、「客户端组件」标签、React 自带钩子的淡底上;钩子卡片、自定义钩子卡片描边都是钩子紫,淡底按来源分(见下);事件处理函数同「函数」块,渲染输出同「返回」块的绿。筛选栏的「种类」按这几个 id 筛,叫上表的名字。
组件色同时用在渲染大纲里的组件、「客户端组件」标签、React 内置钩子讲成钩子返回值时的淡底上;React 的几种概念借语法色(状态同变量、引用同常量、记忆值同数字、上下文同类型、过渡同字符串、副作用同「选项」块的黄),记忆函数描边同「函数」块;钩子返回值、钩子调用、自定义钩子卡片描边都是钩子紫,淡底按来源分(见下);事件处理函数同「函数」块,渲染输出同「返回」块的绿。筛选栏的「种类」按这几个 id 筛,叫上表的名字。
认哪些是组件:名字大写开头;是函数声明、值是函数的变量(箭头函数、函数表达式,直接返回 JSX 的也算),或被 `memo(…)` / `forwardRef(…)`(`React.memo` 也算,可以套几层)包着的函数;函数自己的 return(嵌套函数里的不算)里有一处返回 JSX(元素、片段,三元、`&&` 的某一支是 JSX 也算;和 `null` 混着返回也算)。小写开头的工具函数、不返回 JSX 的大写函数照常是函数块。组件里又定义的组件自己也是一张组件卡片,不算事件处理函数。
@@ -1864,10 +1898,10 @@ TSX 和 Python 一样,从上往下一块语法一张卡片,嵌套的块排
- 组件块(函数块、只声明一个变量的常量 / 变量块)是组件时换成组件卡片。文件开头的 `"use client"`(文件里有组件时)略去(`RuleOmit`),讲成卡片上的「客户端组件」标签;它挂着注释时不略去(注释没地方放),照常是一张语句卡片。组件上面的 JSDoc 是组件块自己的说明(TypeScript 的 JSDoc 能当常量、变量块的说明,`memo(…)`、直接返回 JSX 的常量块也是),讲成卡片的「说明」。
- 自定义钩子的声明(函数块、只声明一个变量的常量 / 变量块;名字是 `use` 后面接大写字母或数字,只叫 `use` 的不算,`customHookAt`)换成自定义钩子卡片。
- 组件体、钩子体里的块:语法树往上找,块的语句直接是某个组件函数或自定义钩子主体(语句块)的子语句才认(`enclosingComponent` / `enclosingCustomHook`:所在的函数找回声明它的语句,再用 `componentAt` / `customHookAt` 核对)。只声明一个变量、值是钩子调用(`React.useX` 也算),或整句就是一次钩子调用的(`hookCallAt`),认成钩子卡片;组件体里定义的函数认成事件处理函数(`bodyOf`)。值是钩子调用再取属性的(`useX()?.a ?? b`)照常是变量卡片。
- 组件体、钩子体里的块:语法树往上找,块的语句直接是某个组件函数或自定义钩子主体(语句块)的子语句才认(`enclosingComponent` / `enclosingCustomHook`:所在的函数找回声明它的语句,再用 `componentAt` / `customHookAt` 核对)。只声明一个变量、值是钩子调用(`React.useX` 也算),或整句就是一次钩子调用的(`hookCallAt`),按变量讲成钩子卡片(见下);组件体里定义的函数认成事件处理函数(`bodyOf`)。值是钩子调用再取属性、再调用的(`useX()?.a ?? b`、`useSearchParams().get("from")`)接住的不是钩子的返回值,照常是变量卡片。
- `return` 块:所在的最内层函数(`enclosingFunction`)是组件函数、返回的是 JSX 时认成渲染输出;写在 `if` 里的提前返回 JSX 也算,它是如果卡片下面的子块。返回 `null` 这类的照常是返回块。
- 别的照语言默认:组件体里普通的 `const x = 表达式` 是变量卡片,`if (…) return null` 是如果卡片、下面是返回卡片,注释是它讲的那张卡片的「注释」一行。
- 回调、函数里的语句来自 TypeScript 语言插件本来给的子块:调用的实参是带语句块的函数时(任何调用都一样),那句语句、那个变量的子块就是函数里的语句,所以 `useEffect(() => {…})`、`useCallback(() => {…})`、`useMemo(() => {…})` 的语句(连返回清理函数的那句 return)排在钩子卡片下面;事件处理函数、自定义钩子是函数块,函数体照常是子块。
- 回调、函数里的语句来自 TypeScript 语言插件本来给的子块:调用的实参是带语句块的函数时(任何调用都一样),那句语句、那个变量的子块就是函数里的语句,所以 `useEffect(() => {…})`、`useCallback(() => {…})`、`useMemo(() => {…})` 的语句(连返回清理函数的那句 return)排在钩子卡片下面。钩子卡片把实参里写的每个函数交给宿主展开(`RuleCard.expands`):语言插件没排成子块的(直接返回表达式的 `() => results.filter(…)`、`() => loadItems()`,第二个起带语句块的函数)由 TypeScript 语言插件的 `functionBody` 排成子块,直接返回的表达式是一张「返回 …」卡片(源码里没写 return,翻译插件照写了的那样补「返回」,`ZhTable.returnWritten`),并进来的照源码先后排。事件处理函数、自定义钩子是函数块,函数体照常是子块。
组件卡片:
@@ -1887,24 +1921,65 @@ 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 色)。
钩子卡片(所有钩子共用一种,不分析钩子里写了什么;行照钩子签名排,没有的行不显示):
钩子卡片按变量讲:卡片讲接住返回值的那个变量是什么(状态、引用、记忆值、记忆函数、上下文值、过渡,别的钩子是钩子返回值),副作用讲那一句;钩子退到「来自」一行。不分析钩子里写了什么,行照签名表排,没有的行不显示。原型 `react-state.js` 的搜索面板(`fixtures/searchPanel.tsx.txt`)逐张对得上:
```
react.hookValue 11
名称: router
来自: useRouter
说明: 路由器,用于页面跳转
react.context 12
名称: { user, logout } 对象解构
user
logout
来自: useContext
上下文: AuthContext
react.state 13
名称: [query, setQuery] 数组解构
1 | query | 当前值
2 | setQuery | 修改函数
来自: useState
状态类型: 未标注
默认值: initialQuery
react.memo 18
名称: visible
来自: useMemo
计算: ⟦函数 · 1 句⟧
重算时机: results 变化时
return 18: 返回 results.filter((r) => r.hidden 不成立)
react.memoFn 20
名称: submit
来自: useCallback
参数: 1 个
e | FormEvent
函数体: ⟦函数 · 2 句⟧
重建时机: router、query 变化时
react.effect 25
触发时机: 仅挂载时
来自: useEffect
执行: ⟦函数 · 1 句⟧
```
| 行 | 内容 |
| --- | --- |
| 钩子 | 被调的钩子名(function 色,带下标:自己写的能跳到定义);第三方钩子悬停「来自 next/navigation」(`Segment.tooltip`) |
| 类型参数 | 源码写了 `useRef<HTMLDivElement>` 才有,照常翻(`number` 是「数值」),几个「, 」隔开 |
| 参数 | `N 个`(hint),下面「参数 \| 值」表,一个实参一行;一行都没有时不出这一行 |
| 返回 | `N 个`(hint),下面接住返回值的各项一行:React 自带、第三方的钩子是「名称 \| 是什么」表,自己写的只有「名称」一列;没接返回值时不出这一行 |
| 名称 | 只用一个名字接住返回值时是那个名字(带下标,能调用的用 function 色),它也是卡片自己的名字(`defines`);解构时照源码写出解构(各项的名字着色,对象解构改了名的写 `键: 名字`),后面灰字「数组解构」「对象解构」,展开是一张表:数组「第几项 \| 名称 \| 说明」,对象「名称 \| 说明」(改了名时前面多一列「键」),签名表没说是什么的项时不出「说明」一列(对象解构都没有)。没接返回值时(副作用、钩子调用)不出这一行 |
| 来自 | 被调的钩子名(function 色,带下标:自己写的能跳到定义);悬停「React 内置钩子」「next/navigation 提供的钩子」「自定义钩子」(`Segment.tooltip`) |
| 状态类型、指向类型 | 第一个类型实参(`useState<LoginTab>` 的 LoginTab,`useRef<HTMLInputElement>`),照常翻;没写时「未标注」 |
| 默认值、初始值、处理函数、初始化函数、计算、函数体、执行、上下文 | 签名表登记的那个实参,按形参的种类写(见下);没传时照签名表写「没写,未定义」,签名表没给说法的不出这一行(useReducer 只传两个实参时没有「初始化函数」)。上下文的名字用上下文色 |
| 重算时机、重建时机、触发时机 | 依赖项:`[a, b]` 是「a、b 变化时」,`[]` 是「仅挂载时」,写成别的表达式时「deps 变化时」(「变化时」「仅挂载时」keyword 色);没写时记忆值、记忆函数「没写,每次渲染时」,副作用「没写,每次渲染后」 |
| 参数(记忆函数) | `useCallback` 收的函数的参数「名称 \| 类型」表(没标注写「未标注」),这时「函数体」的小框里不再写参数;没有参数不出这一行 |
| 说明(钩子返回值) | 只用一个名字接住时讲它是什么:第三方钩子照词表(next/navigation 的 `usePathname` 当前路径、`useRouter` 路由器,用于页面跳转、`useSearchParams` URL 查询参数、`useParams` 路由动态参数、`useSelectedLayoutSegment(s)` 当前激活的下一级 / 各级布局段,next/router 的 `useRouter`,react-dom 的 `useFormStatus`),没有自己那种卡片的内置钩子照签名表(useId「稳定的唯一标识」、useDeferredValue「延迟更新的值」);自己写的钩子、解构接住的没有 |
| 参数(钩子返回值、钩子调用) | `N 个`(hint),下面「参数 \| 值」表,一个实参一行:内置钩子照签名表的形参名,自己写的照定义处的形参(同文件的从语法树读,别的文件的经工作区索引 `react.customHooks` 查:看 `**/*.{ts,tsx,js,jsx}`,预筛有 `useX` 字样才解析,从文件顶层认出自定义钩子,键是名字、值是各形参写法的 JSON;查法同构造参数的类型,先照位置写、查到了重排),都不知道时照位置写「第 1 个」;一行都没有时不出这一行 |
- 钩子从哪来:写成 `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),都不认得时按文件算,再从调用者往上照常问编辑器。
- 签名表(`rules/reactHooks.ts` 的 `REACT_HOOKS`,每个钩子登记一次,同一语义只这一份):作用分类(状态 / 副作用 / 引用 / 性能 / 上下文 / 并发 / 外部数据源)、讲成哪种卡片和卡片从上往下的各行(`CardSpec`:名称 / 来自 / 第一个类型实参 / 第几个实参 / 第几个实参的参数表)、形参名照 `@types/react`(传三个实参的 useReducer 换成重载的 `reducer, initialArg, init`)和每个形参的种类(普通值 / 可以是函数的初始值 / 函数 / 依赖项 / 引用)、没传时怎么写、返回各项是什么(useState「当前值」「修改函数」,useReducer「当前值」「派发函数」,useTransition「是否正在过渡」「启动过渡的函数」……)和接住它的名字能做什么(值 / 状态 / 修改函数 / 派发函数 / 函数,决定那一项的调用树悬停字)。登记了 react 包的全部钩子:useState、useReducer、useActionState、useOptimistic、useId、useContext、use、useRef、useImperativeHandle、useEffect、useLayoutEffect、useInsertionEffect、useEffectEvent、useDebugValue、useMemo、useCallback、useTransition、useDeferredValue、useSyncExternalStore;有自己那种卡片的是 useState、useReducer、useRef、useMemo、useCallback、useContext、useTransition 和三个副作用钩子,别的讲成钩子返回值(没接返回值的 useImperativeHandle、useDebugValue 是钩子调用)。
- 实参那一格按形参的种类:写在这里的函数都收成 fold 小框「函数 (next: 布尔) · 2 句」(没参数不写括号,直接返回表达式的算 1 句),函数体是卡片的子块(直接返回的表达式是一张「返回 …」卡片),小框能点,和卡片的折叠箭头是同一个开关(webview 的 `ChainFoldContext`,卡片有排在下面的子块时由卡片给);可以是函数的初始值传的函数写「首次渲染时调用」加小框;函数、引用种类传的是名字时(filterReducer、AuthContext)照抄成带源码下标的名字(函数的名字 function 色);别的照常翻(`null` 是「空」,`[]` 是「空数组」)。
- 样子:左边两个图标,先是语言的种类(变量;记忆函数是函数,`RuleCard.languageKind`;副作用、钩子调用是语句),再是 React 的概念(上表的图标,悬停卡片种类的名字;状态悬停「React 状态」,useLayoutEffect 的副作用悬停「副作用 · 浏览器绘制前执行」,useInsertionEffect「副作用 · 布局副作用之前执行」,`KindBadge.tooltip`)。描边、淡底是 React 概念的颜色;记忆函数描边、淡底是函数色,sigma 图标是记忆值的绿(`KindBadge.iconColor`)。钩子返回值、钩子调用描边是钩子紫,淡底按来源(`RuleCard.tint`,webview 的 `.cr-block[data-tint]`):React 内置 #61dafb / #0a7ea4、第三方 #e5c07b / #9a6a00、自己写的 #89d185 / #388a34;内置钩子再加一个作用图标(`RuleCard.concepts`):状态 `toggle-right`、副作用 `zap`、引用 `anchor`、性能 `sigma`、上下文 `share-2`、并发 `hourglass`、外部数据源 `database` #dcdcaa / #795e26。
- 调用树:卡片右上角没有调用树按钮,接住返回值的每一项各一个调用树操作(`CallTreeAction`,见「属性表」的「操作」一列):解构的每一项在表的「操作」一列,只接一个名字时在「名称」一行最右边。悬停按接住它的名字能做什么写:值「谁用了」、状态的当前值「谁用了这个状态」(useState、useReducer 的第一项)、修改函数「谁改了这个状态」、派发函数「谁派发了动作」、别的函数「谁调用了」;签名表说不清的项(第三方、自己写的钩子、对象解构)在组件体、钩子体里被调用过的算函数,否则算值。都查所有引用(`mode: 'references'`,只查调用的话 `onTabChange={setActiveTab}`、`onSubmit={submit}` 这种当值传下去的都漏了),组件体、钩子体里 React 内置钩子依赖数组的那几段不算(`except`:依赖项只告诉 React 什么时候重跑,不是在用它)。按钮上的数是用到几处:搜索面板里 query 3、inputRef 2、别的各 1,同原型。
- 查所有引用(调用树服务的 references 查法,`callTree/query.ts` 的 `loadTargetCalls` / `loadCallEdges`):起点按名字起点自己写一个(变量);`vscode.executeReferenceProvider` 查名字的各处引用,去掉名字自己的定义处和起点落在 `except` 里的(`callTree/referenceCalls.ts` 的 `countedSites`),每处在文件大纲(`vscode.executeDocumentSymbolProvider`)里从里往外找第一个调用层级认得的符号当调用者(写在 `useCallback` 回调里的,调用者是组件 SearchPanel),都不认得时按文件算,同一个调用者的几处并成一项,再从调用者往上照常问编辑器。次数是直接连着起点的调用边条数(一处一条,`session.ts` 的 `countOf`)。查调用(calls,函数、类、自定义钩子卡片右上角的按钮)照旧:编辑器的调用层级认不出名字时(数组解构出来的、`const x = useCallback(…)` 的,`vscode.prepareCallHierarchy` 给空的)同样改查引用,但只留调用处(名字后面紧跟实参的括号,中间可以隔着 `?.`、泛型实参),次数是落在几棵调用树里。
- 调用树服务按「调用树的键」数、查(`callTree/session.ts` 的 `callTargets`):块自己的调用树键是块的记号,卡片上的操作键是操作的键(`actionKey`);`callTreeCounts` 消息按键发次数,页面点开发 `openCallTree { version, key }`,回 `callTree { key, … }`。同一版文档重发时问法(名字起点、查法、接口、不算的范围)没变的不再问。
自定义钩子卡片:名称(function 色,带下标,后面跟「具名导出」「默认导出」小标签,同组件卡片)/ 说明(JSDoc 的正文)/ 对不上的参数 / 参数(「名称 \| 类型」表,有参数带默认值时多一列默认值,说明里有参数对上了 `@param` 时多一列说明;没有参数写「无」)/ 返回(只列名称:从最后那句 return 读,对象写的取各个键(简写的、改了名的),数组取各项,标识符是它自己,直接返回表达式的箭头函数读那个表达式;读不出、说明里也没写 `@return` 时不出这一行)/ 异常。样子同钩子卡片(钩子图标、钩子紫描边、自己写的淡底),能看调用树(查这个钩子在哪被调用);钩子体里的钩子调用是钩子卡片。
自定义钩子卡片:名称(function 色,带下标,后面跟「具名导出」「默认导出」小标签,同组件卡片)/ 说明(JSDoc 的正文)/ 对不上的参数 / 参数(「名称 \| 类型」表,有参数带默认值时多一列默认值,说明里有参数对上了 `@param` 时多一列说明;没有参数写「无」)/ 返回(只列名称:从最后那句 return 读,对象写的取各个键(简写的、改了名的),数组取各项,标识符是它自己,直接返回表达式的箭头函数读那个表达式;读不出、说明里也没写 `@return` 时不出这一行)/ 异常。左边是函数、钩子两个图标,钩子紫描边、自己写的淡底,能看调用树(`RuleCard.calls`:右上角的按钮,查这个钩子在哪被调用);钩子体里的钩子调用同组件体里的一样按变量讲。
事件处理函数:名称是函数名(function 色,带下标);参数照常翻(`e: KeyboardEvent<HTMLDivElement>`),几个「, 」隔开,没有写「无」;绑定事件从 JSX 属性反查:「<div>(修改排队消息) 的 onKeyDown」,在属性值里调用时写传入的实参「(传入 h.id)」,在列表里一项项渲染的写「每个 <button>」,几处「、」隔开;没绑在 JSX 上时写由谁调用「由 download、share 调用」(在钩子调用里时写接住返回值的变量名,没用一个名字接住的写钩子名,同一个钩子这样写了几处时后面跟第几处、钩子紫),都没有「—」。函数里的语句是子块(如果、返回、调用、变量、尝试……),注释挂在它讲的那块上。
@@ -1916,7 +1991,7 @@ TSX 和 Python 一样,从上往下一块语法一张卡片,嵌套的块排
- 子节点只有文字、插值时写在同一行:只有一段文字时带引号,插值写成 `{…}`(花括号 hint 色),和文字混着时文字不带引号。
- `cond && <X/>` 前面加「当 … 时」小框(`condition`,条件照常翻);三元一行「如果 …」,下面第一行各带「是」「否」小框(`branch`),一支是 null 时写「不渲染」;`items.map((x) => …)` 一行「对 items 的每一项 x」(`iteration` 小框)后面「key …」,回调里先算的常量写「其中 isActive = …」,再下面是每一项渲染的;JSX 注释写成一行「// …」(comment 色)。
每张卡片「编辑」是它那条语句的完整代码,组件卡片是整个组件(盖住它的子块);组件体里的卡片各自能编辑、问 AI。代码差异在视图数据这一层比:组件卡片的属性表逐行(属性表按名称配对,加的属性标新加的一行),组件体里的卡片是块,照常配对、逐行标(改了依赖,钩子卡片参数表 deps 那一格逐截标)。
每张卡片「编辑」是它那条语句的完整代码,组件卡片是整个组件(盖住它的子块);组件体里的卡片各自能编辑、问 AI。代码差异在视图数据这一层比:组件卡片的属性表逐行(属性表按名称配对,加的属性标新加的一行),组件体里的卡片是块,照常配对、逐行标(改了依赖,副作用卡片的「触发时机」那一行逐截标)。属性表里的操作(调用树按钮)不参与比较。
样例(`fixtures/planChecklist.tsx.txt` 的 `PlanChecklist`,测试里逐字核对;`⟦…⟧` 是小标签、小框,子块多缩进两格,说明只写第一行):
@@ -1927,30 +2002,27 @@ react.component 23
构造参数: 2 个 · 类型 PlanChecklistProps
todos | PlanTodo[] | — | 是 | 执行期计划清单 (覆盖式刷新); 空数组时不渲染.
active | 布尔 | — | 是 | 本轮是否仍在流式执行中. ``in_progress`` 仅在 ``active`` 时转圈; …
react.hook 35
钩子: useState
参数: 1 个
initialState | 假
返回: 2 个
collapsed | 当前值
setCollapsed | 修改函数
react.hook 37
钩子: useEffect
参数: 2 个
effect | ⟦函数 · 1 句⟧
deps | 仅挂载时
react.state 35
名称: [collapsed, setCollapsed] 数组解构
1 | collapsed | 当前值
2 | setCollapsed | 修改函数
来自: useState
状态类型: 未标注
默认值: 假
react.effect 37
触发时机: 仅挂载时
来自: useEffect
执行: ⟦函数 · 1 句⟧
try 38: 尝试
if 39: 如果 (localStorage.getItem(PLAN_CHECKLIST_COLLAPSED_KEY) 全等 "1")
statement 40: queueMicrotask(() => setCollapsed(真));
catch 42: 捕获
注释: localStorage 不可用 (隐私模式等) 时静默用默认
react.hook 47
钩子: useCallback
参数: 2 个
callback | ⟦函数 · 3 句⟧
deps | collapsed 变化时
返回: 1 个
toggle | 记住的函数
react.memoFn 47
名称: toggle
来自: useCallback
函数体: ⟦函数 · 3 句⟧
重建时机: collapsed 变化时
variable 48
名称: next
初始值: collapsed 不成立
@@ -1973,7 +2045,7 @@ react.component 23
…
```
和原型不一样的地方:接口 `PlanChecklistProps` 照 TypeScript 的接口块(一行译文,字段是子块,注释挂在讲的字段上),原型「对照」里画成的字段表(名称 / 类型 / 必填 / 说明)这次没做;渲染输出的大纲挂在「JSX 结构大纲」这一行下面,不是直接画在卡片里;原生标签(`<textarea>`)不能跳,组件标签能跳到组件定义。钩子原型(`react-hooks.js`)里:自定义钩子卡片的返回表画了「是什么」一列(「状态 messages」),这次只列名称;`createContext` 的「上下文」卡片没做,照常是常量卡片;参数里的函数小框画成能点开,这次只当摘要,展开收起用卡片的折叠箭头。
和原型不一样的地方:接口 `PlanChecklistProps` 照 TypeScript 的接口块(一行译文,字段是子块,注释挂在讲的字段上),原型「对照」里画成的字段表(名称 / 类型 / 必填 / 说明)这次没做;渲染输出的大纲挂在「JSX 结构大纲」这一行下面,不是直接画在卡片里;原生标签(`<textarea>`)不能跳,组件标签能跳到组件定义。钩子原型(`react-hooks.js`)里:自定义钩子卡片的返回表画了「是什么」一列(「状态 messages」),这次只列名称;`createContext` 的「上下文」卡片没做,照常是常量卡片。变量视角的原型(`react-state.js`)里:登录页画板上的 `router`、`from` 还是旧的钩子卡片画法,这次 `router` 同搜索面板讲成钩子返回值,`from`(`useSearchParams().get("from")`)接住的不是钩子的返回值,照常是变量卡片;只接一个名字时「名称」一行的按钮靠这一行最右边,没和别的卡片表格里「操作」一列逐像素对齐;原型没画的几种由规则补上:没有自己那种卡片的内置钩子讲成钩子返回值(React 色淡底,多一个作用图标),没接返回值的别的钩子讲成「钩子调用」卡片(语句图标 + 钩子图标,来自 / 参数)。
### 内置:Rust 的库
@@ -2370,8 +2442,8 @@ 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/reactHooks.ts React 自带钩子的签名表:作用分类、形参名和种类、没传时怎么写、返回各项的说法、哪几项是函数
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 命令规则共用的:认选项(连写拆开、带值的、长选项、--)、拼译文(动作、括号里的意思、照抄不认识的选项)
@@ -2524,7 +2596,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>`、导入的类型查不到和查到了);组件体的归类、钩子调用、自定义钩子(认哪些、钩子体里的语句、返回的各项)、名字用在哪(钩子调用、处理函数绑在每个按钮上);渲染大纲(原生标签、组件、类、条件、三元、列表、片段、注释、几处返回)和 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` 空的规则都启用,`alwaysIn` 里的语言不看导入、别的语言照导入(导入了 react 的 .ts 出自定义钩子卡片和钩子卡片,没导入的不启用);略去的块不出块、有子块的不能略去。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` 是如果卡片;按变量讲的钩子卡片(原型 `react-state.js` 的三张画板:`fixtures/loginPage.tsx.txt` 的状态卡片、两个图标和悬停、各项的调用树操作,`fixtures/stateVariants.tsx.txt` 的未标注类型、函数形式的默认值和它的返回卡片子块、只取当前值,`fixtures/searchPanel.tsx.txt` 每种卡片的各行、样子、各项操作的悬停字和不算的依赖数组、宿主数的调用树键、函数参数的小框和子块;`fixtures/hooksPanel.tsx.txt` 的没传参数的说法、useLayoutEffect 的悬停、记忆函数的参数表、惰性初始值),签名表的重载和没传的参数,三种来源的淡底,没有自己那种卡片的内置钩子讲成钩子返回值、带作用图标,别的文件里自己写的钩子经索引查形参名;自定义钩子卡片(函数、钩子两个图标,右上角调用树)和钩子体里的钩子,没接返回值的钩子调用;没绑在 JSX 上的处理函数写由谁调用;直接返回 JSX 的组件把大纲写在卡片里;九个文件认领下来都合约定;差异:加一个属性、改一个副作用的依赖,组件卡片属性表那一行新加、副作用卡片的触发时机那一行逐截标。调用树改查引用(`callTree/referenceCalls.test.ts`:只留调用处、往外找第一个认得的调用者、文件顶层按文件算;查所有引用时不看名字后面、去掉定义处和依赖数组里的),调用树服务按键数(`callTree/session.test.ts`:卡片上的操作各数各的,查引用的次数是用到几处),发给页面的操作下标从块的开头算、键原样带着(`editor/pageBlocks.test.ts`)。宿主:`libraries` 空的规则都启用,`alwaysIn` 里的语言不看导入、别的语言照导入(导入了 react 的 .ts 出自定义钩子卡片和钩子卡片,没导入的不启用);略去的块不出块、有子块的不能略去。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`)。