📝 docs(code-reader): 规格补上 React 组件一节(认组件、名称标签、属性表、状态 / 引用 / 读取的数据 / 派生值 / 副作用 / 事件处理、渲染大纲、和原型不一样的地方),契约补上略去块、把子块放进卡片某一行、不看导入的规则、新的框样式和图标
This commit is contained in:
1 parent
fd8b03a563
commit
6568182709
1 file changed
+111
-14
@@ -142,7 +142,7 @@
|
||||
|
||||
库规则的种类用规则给的图标(`KindBadge.icon`):SQLAlchemy 数据表 `database`、数据类 `package`、Celery 应用 `server-cog`、定时任务 `calendar-clock`、FastAPI 接口 `globe`、FastAPI 路由器 `network`。
|
||||
|
||||
这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 26 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置共用一个,字段、标签共用一个——加库规则用的 6 个,lucide 的 kebab-case 名字),类型 `KindIconName` 是它们的联合,`KindBadge.icon` 只能写其中一个,写别的是类型错误。webview 给每个名字配一个逐个 import 的 lucide 图标组件(`kindIcons.tsx` 的 `KIND_ICON_COMPONENTS`,打包只带这些,预览脚本约 157 kB);webview 的 `KindIconName` 照契约手抄,`kindIconNames.test.ts` 核对两边一样。规则要新图标就把名字加进这组、在 webview 配上图标,没有按任意名字找图标的路。
|
||||
这组图标由插件自己定义,是一个封闭的集合:契约里的 `KIND_ICON_NAMES`(上表 26 个——命令、管道、执行共用一个,解释器、解析指令共用一个,选项、设置共用一个,字段、标签共用一个——加库规则用的 9 个(React 组件的原子、副作用的闪电、事件处理的点击手势也在里面),lucide 的 kebab-case 名字),类型 `KindIconName` 是它们的联合,`KindBadge.icon` 只能写其中一个,写别的是类型错误。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` 挂到元素上,读屏当描述念。
|
||||
- 种类图标和底色按种类区分(颜色见「颜色」);注释块虚线框。
|
||||
@@ -814,11 +814,19 @@ export type TokenRole =
|
||||
*/
|
||||
export interface Segment { text: string, role: TokenRole, offset?: number, reference?: RuleReference, color?: ThemeColor, refers?: SourceRange }
|
||||
|
||||
/** 框的样式,决定框头图标和框的颜色:'iteration' 是写在表达式里的遍历,图标、颜色同「循环」块。 */
|
||||
export type GroupStyle = 'iteration'
|
||||
/**
|
||||
* 框的样式,决定框头图标和框的颜色:iteration 写在表达式里的遍历(渲染大纲的「对 … 的每一项」也是),图标、颜色同「循环」块;
|
||||
* condition 按条件(「当 … 时」「如果 …」),图标、颜色同「条件」块;branch 条件的一支(「是」「否」「否则」),同「条件」块的颜色、不带图标;
|
||||
* skip 这个条件下不渲染,图标、颜色同「返回」块;tag 小标签(「具名导出」「回调」),只有框头的字、颜色是 color;
|
||||
* classes 收起来的一串类名(「样式 N 个类」),中性色,点开、悬停看 detail。
|
||||
*/
|
||||
export type GroupStyle = 'iteration' | 'condition' | 'branch' | 'skip' | 'tag' | 'classes'
|
||||
|
||||
/** 框起来的一组词:预览画成嵌在前后文字里的小框,框头是图标和 label(翻译插件给);框里的词照样带下标,还能再套框。 */
|
||||
export interface SegmentGroup { style: GroupStyle, label: string, segments: Inline[] }
|
||||
/**
|
||||
* 框起来的一组词:预览画成嵌在前后文字里的小框,框头是图标和 label(翻译插件、库规则给;只有图标时是空的);框里的词照样带下标,还能再套框。
|
||||
* color 盖过样式的颜色(tag 按标签的意思上色),detail 是点开、悬停时看的全文(classes 的全部类名,一行一条)。
|
||||
*/
|
||||
export interface SegmentGroup { style: GroupStyle, label: string, segments: Inline[], color?: ThemeColor, detail?: string }
|
||||
|
||||
/** 译文里的一段:着色的词,或框起来的一组词。 */
|
||||
export type Inline = Segment | SegmentGroup
|
||||
@@ -943,14 +951,17 @@ export interface Translator {
|
||||
export interface ThemeColor { dark: string, light: string }
|
||||
|
||||
/** 种类图标:插件自己定义的一组 lucide 图标(kebab-case 名字);前 22 个给内置种类,后面的给库规则。规则要新图标就加进这组,没有按任意名字找图标的路。 */
|
||||
export const KIND_ICON_NAMES = ['import', 'square-function', /* …22 个内置种类的… */ 'database', 'package', 'server-cog', 'calendar-clock', 'globe', 'network'] as const
|
||||
export const KIND_ICON_NAMES = ['import', 'square-function', /* …22 个内置种类的… */ 'database', 'package', 'server-cog', 'calendar-clock', 'globe', 'network', 'atom', 'zap', 'mouse-pointer-click'] as const
|
||||
export type KindIconName = (typeof KIND_ICON_NAMES)[number]
|
||||
|
||||
/** 库规则的种类:块左边只画图标(种类色),label 是悬停提示和读屏念的名字,筛选栏里也叫它;icon 只能从 KindIconName 里挑。 */
|
||||
export interface KindBadge { label: string, color: ThemeColor, icon: KindIconName }
|
||||
|
||||
/** 库规则认领一块后给出的替身,下面挂着原块留下的子块:规则自己的卡片,或认成的现成属性表块。 */
|
||||
export type RuleBlock = RuleCard | RuleFacts
|
||||
/** 库规则认领一块后给出的替身,下面挂着原块留下的子块:规则自己的卡片,或认成的现成属性表块;这块讲在了别的卡片里时是略去它。 */
|
||||
export type RuleBlock = RuleCard | RuleFacts | RuleOmit
|
||||
|
||||
/** 这块讲在了别的卡片里,不再单独成块(React 的 "use client" 讲成组件卡片的「客户端组件」标签);不能有子块。 */
|
||||
export interface RuleOmit { omit: true }
|
||||
|
||||
/** 规则自己的卡片:一张属性表,种类的图标、颜色、名字和每一行都由规则给。 */
|
||||
export interface RuleCard {
|
||||
@@ -960,8 +971,15 @@ export interface RuleCard {
|
||||
defines?: SourceRange // 块自己的名字(类名):跳到的定义是它时闪整块
|
||||
attributes?: BlockAttributes // 规则自己的筛选属性,叠在宿主从原块推出的上面;kind 填了不算;名字由 LibraryRule.attributeNames 给
|
||||
absorbs: CodeBlock[] // 并进来的子块(原块 children 里的对象本身),不再单独成块;没并的照源码顺序留作子块,编辑范围同别的块,是原块整条语句
|
||||
slots?: RuleSlot[] // 画在卡片某一行下面的子块,一块只能放一处
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡片里的一处子块:block(没并进来的子块)不排在卡片后面,画在第 row 行下面当子卡片,照常转成块(翻译、库规则、编辑、问 AI、差异都照常)。
|
||||
* item 是这一行的子行里讲它的那一个:这时它默认收着,点那个子行的第一格才展开、一次只展开一个(React 组件的事件处理表点名称)。
|
||||
*/
|
||||
export interface RuleSlot { block: CodeBlock, row: number, item?: number }
|
||||
|
||||
/** 把这块认成契约里现成的一种属性表块(enum 库的类 → 枚举块):宿主当语言插件报的这种块处理,翻译插件排属性表;头部是原块整条语句。 */
|
||||
export interface RuleFacts {
|
||||
as: BlockFacts
|
||||
@@ -1008,7 +1026,7 @@ export interface LibraryRule {
|
||||
id: string // 'sqlalchemy'
|
||||
label: string // 'SQLAlchemy 数据表'
|
||||
languageIds: string[]
|
||||
libraries: string[] // 关心的库:导入来源等于它或是它的子模块才启用
|
||||
libraries: string[] // 关心的库:导入来源等于它或是它的子模块才启用;空数组是不看导入,languageIds 里这几种语言都启用(React)
|
||||
claim: (block: CodeBlock, context: RuleContext) => RuleBlock | undefined
|
||||
resolve?: (key: string, workspace: RuleWorkspace) => Promise<RuleLocation[]> // 查自己标的引用;没给就不能标引用
|
||||
attributeNames?: (displayLanguage: string | undefined) => AttributeNames // 自己报的筛选属性和值叫什么(同 Translator.attributeNames,用词归规则);翻译插件给了名字的以翻译插件为准
|
||||
@@ -1072,20 +1090,22 @@ export interface CommandRule {
|
||||
|
||||
- 注册:`CodeReaderApi.registerLibraryRule(rule)`,返回 `Disposable`;内置规则走同一个 API。
|
||||
- 必须给:`id`、`label`、`languageIds`、`libraries`(关心的库)、`claim`(看一块,认领就返回替身 `RuleBlock`,不认返回 `undefined`)。
|
||||
- 替身(`RuleBlock`)两种,按有没有 `as` 分:
|
||||
- 替身(`RuleBlock`)三种,按有没有 `omit`、`as` 分:
|
||||
- 略去(`RuleOmit`,`{ omit: true }`):这块讲在了别的卡片里(React 文件开头的 `"use client"` 讲成组件卡片的「客户端组件」标签,组件上面没并进块头部的 JSDoc 讲成「说明」),预览不画它、不占块的位置(后面的块 id 接着往下数)。它不能有子块,有子块时宿主当替身不合约定。
|
||||
- 认成现成的属性表块(`RuleFacts`):`as` 是种类和 facts(`BlockFacts`,例如 `{ kind: 'enum', facts: EnumFacts }`),`absorbs` 同卡片。宿主把原块换成这种块(节点不变,头部是原块整条语句,子块是留下的),此后和语言插件报的这种块完全一样:翻译插件的 `describe` 排属性表、种类名取翻译插件的、颜色取这个种类的、筛选属性从 facts 推、块自己的名字从 facts 取。规则不给用词,也不给标签和颜色。只要检查并进来的是不是这块的子块。
|
||||
- 规则自己的卡片(`RuleCard`),下面几条都说的是它。
|
||||
- 卡片必须给:`kind`(写成「规则 id.名字」,宿主核对前缀;筛选记住的就是它,要稳定)、`badge`(种类的名字、暗 / 亮两种颜色和 lucide 图标名)、`rows`(用现成的 `PropertyRow`:属性名 + 着色的值或 Markdown,表格就是带 `columns` 的子行)、`absorbs`(并进来的子块)。可以给:`defines`(块自己的名字,跳到定义是它时闪整块)、`attributes`(自己的筛选属性);表格子行填 `PropertyItem.defines`(字段名),跳到这个定义时只闪那一行。
|
||||
- 卡片必须给:`kind`(写成「规则 id.名字」,宿主核对前缀;筛选记住的就是它,要稳定)、`badge`(种类的名字、暗 / 亮两种颜色和 lucide 图标名)、`rows`(用现成的 `PropertyRow`:属性名 + 着色的值或 Markdown,表格就是带 `columns` 的子行)、`absorbs`(并进来的子块)。可以给:`defines`(块自己的名字,跳到定义是它时闪整块)、`attributes`(自己的筛选属性)、`slots`(放进卡片某一行下面的子块,见下);表格子行填 `PropertyItem.defines`(字段名),跳到这个定义时只闪那一行。
|
||||
- 放进卡片里的子块(`RuleSlot`):留下的子块(没并进来的)可以不排在卡片后面,画在卡片第 `row` 行下面当子卡片(React 组件的副作用画在「副作用」那一行下面);它照常转成块,照样问各规则(副作用那句也被 React 规则认成自己的卡片),编辑、问 AI、差异、筛选都照常。给了 `item` 的是那一行的子行里讲它的那一个:默认收着,点那个子行的第一格才展开、同一行里别的收起(事件处理表点名称)。一块只能放一处,不能同时并进来;`row` 要有这一行、`item` 要有这个子行。
|
||||
- 只经契约:规则拿到的是块(kind、node、header、children、facts)和 `RuleContext`;源码里的名字照抄时自己带下标(`Segment.offset`),表达式、类型交给 `context.translate`(当前翻译插件的 `expression`,名字带下标,⌘ + 点击、高亮各处出现照常),说明文字交给 `context.doc`(当前翻译插件的 `doc`,和别的「说明」一样按 Markdown 显示),别的文件里定义的名字交给 `context.lookup`(见「规则认领时查别的文件里的定义」);库自己的配色(HTTP 方法)写在段的 `color` 上。规则可以读自己那门语言的语法树(规则声明了 `languageIds`),但不 import 语言插件、翻译插件和宿主模块(`rules/contractOnly.test.ts` 检查 `rules/` 下非测试文件只 import `../contract` 和同目录的文件)。
|
||||
- 用词归规则:属性名、表头、种类名、类型译名都在规则自己的词表里,按 `context.displayLanguage`(翻译插件的 `displayLanguage`)挑,没有这种语言的词表就用规则默认的那份;加一种显示语言就是给规则加一份词表。webview 只画结构,不认识「数据表」这些字。
|
||||
|
||||
### 启用、认领、优先
|
||||
|
||||
- 启用:宿主收集这份源码里各层导入块的 `ImportFacts.sources`(去掉引号),有一个等于规则的某个库、或以「库.」「库/」开头(子模块),规则才对这份源码启用。规则不用自己读导入语句。
|
||||
- 启用:宿主收集这份源码里各层导入块的 `ImportFacts.sources`(去掉引号),有一个等于规则的某个库、或以「库.」「库/」开头(子模块),规则才对这份源码启用。规则不用自己读导入语句。`libraries` 是空数组的规则不看导入,`languageIds` 里这几种语言的源码都启用(React:新的 JSX 写法不用导入 react)。
|
||||
- 认领:宿主从顶层往下每块问一遍启用的规则,按注册顺序,第一个认领的算(先注册的优先);被认领的块留下的子块照样问。
|
||||
- 出错:规则抛错、或替身不合约定(种类 id 没带规则 id 前缀、并进来的不是这块的子块、属性表里的引用标的不是自己 / 规则没给 `resolve` / 引用的段没带下标)时,宿主记一条警告日志(输出面板「Superpowers」),当它没认、接着问下一个规则;都没认这块照常显示。预览不会因为规则出错而失败。
|
||||
- 出错:规则抛错、或替身不合约定(种类 id 没带规则 id 前缀、并进来的不是这块的子块、属性表里的引用标的不是自己 / 规则没给 `resolve` / 引用的段没带下标、放进卡片里的块不是留下的子块 / 放了几处 / 没有那一行或子行、要略去的块还有子块)时,宿主记一条警告日志(输出面板「Superpowers」),当它没认、接着问下一个规则;都没认这块照常显示。预览不会因为规则出错而失败。
|
||||
- 并进来的子块:不再单独成块,由规则挑(`absorbs`),宿主不替规则多并。没并进来的子块(方法、规则不讲的注释)不管写在哪(字段中间也算),都照源码顺序是替身的子块,预览里照常看得见。替身的编辑范围同别的块,是原块整条语句(从头部开始,到原块连同各子块的最后一行,类就是整个类),盖住留下的子块的行:替身编辑时预览不画这些子块,同一行不会同时在两处能改(见「预览(代码块树)」)。内置规则并成员时连同紧挨在它前面的注释块一起并(讲的是这个成员),讲方法的注释、最后面的注释照常留下。
|
||||
- 卡片在预览里:种类图标、颜色取 `badge`(`icon`;颜色暗色、亮色主题各取一个),悬停提示是 `badge.label`,内容是 `rows`;筛选的 `kind` 是替身的种类 id,其余属性从原块推出(原来的类是公共的,数据表也是公共的),叠上规则给的(规则自己的属性叫 `LibraryRule.attributeNames` 给的名字,规则起名时抛错记日志、照写 id);筛选栏里这个种类叫 `badge.label`;和类块一样默认收起子块。
|
||||
- 卡片在预览里:种类图标、颜色取 `badge`(`icon`;颜色暗色、亮色主题各取一个),悬停提示是 `badge.label`,内容是 `rows`,放进卡片的子块画在它那一行下面(这一行有折叠箭头,收起时它们跟着收起;画成一层块,同块树),不算卡片的子块(卡片的折叠箭头只管后面留下的子块);筛选的 `kind` 是替身的种类 id,其余属性从原块推出(原来的类是公共的,数据表也是公共的),叠上规则给的(规则自己的属性叫 `LibraryRule.attributeNames` 给的名字,规则起名时抛错记日志、照写 id);筛选栏里这个种类叫 `badge.label`;和类块一样默认收起子块。
|
||||
|
||||
### 规则自己查定义
|
||||
|
||||
@@ -1438,6 +1458,79 @@ export interface CommandRule {
|
||||
|
||||
配色:名称 function、标签 string、依赖名 function、「(参数名)」hint、表格第一列(项)hint、URL 同上、参数名 variable、类型按翻译插件、异常类名 type、消息和说明 comment、自动带的 422 hint。
|
||||
|
||||
### 内置:React 组件(TypeScript / JavaScript)
|
||||
|
||||
`vscode/src/codeReader/rules/react.ts`(读语法树的办法在 `rules/reactTree.ts`,都是纯函数),`languageIds: ['typescriptreact', 'javascriptreact']`,`libraries: []`(新的 JSX 写法不用导入 react,TSX / JSX 文件都启用)。原型:`code.js` 的「React 组件」一节(`TITLEBAR_CARD`、`LEGAL_CARD`、`reactCard`),插件菜单「React 组件」那一页的四张画板;样例是 luffy 的 `components/desktop/DesktopTitlebar.tsx`(`fixtures/desktopTitlebar.tsx.txt`,简单)、`components/LegalDocPage.tsx`(`fixtures/legalDocPage.tsx.txt`,复杂),说明板的例子 `DeleteAppDialog`、`DesktopSettingsDialog`、`AppVaultSummary`、`ShareButton`、`PosterDialog` 也复制进了 `fixtures/`。三种卡片:
|
||||
|
||||
- 组件:种类 `react.component`,名字「React 组件」,图标 `atom`,颜色暗 #61dafb / 亮 #0a7ea4(组件色,渲染大纲里的组件、「客户端组件」标签同色),和函数块一样默认收起留下的子块。
|
||||
- 副作用:种类 `react.effect`,名字「副作用」,图标 `zap`,颜色暗 #e5c07b / 亮 #9a6a00。
|
||||
- 事件处理:种类 `react.handler`,名字「事件处理」,图标 `mouse-pointer-click`,颜色暗 #dcdcaa / 亮 #795e26。
|
||||
|
||||
认哪些是组件:名字大写开头;是函数声明、值是函数的变量(箭头函数、函数表达式,直接返回 JSX 的也算),或被 `memo(…)` / `forwardRef(…)`(`React.memo` 也算,可以套几层)包着的函数;函数自己的 return(嵌套函数里的不算)里有一处返回 JSX(元素、片段,三元、`&&` 的某一支是 JSX 也算;和 `null` 混着返回也算)。小写开头的工具函数、不返回 JSX 的大写函数照常是函数块。组件里又定义的组件自己也是一张组件卡片,不算处理函数。
|
||||
|
||||
整块换成组件卡片,不再是普通函数卡片的输入参数 / 输出参数。文件开头的 `"use client"`(文件里有组件时)和组件上面没并进块头部的 JSDoc(值是 `memo(…)`、直接返回 JSX 的常量块)略去(`RuleOmit`),讲成卡片上的「客户端组件」标签和「说明」。属性表(有才显示,没有就不画):
|
||||
|
||||
| 行 | 内容 |
|
||||
| --- | --- |
|
||||
| 名称 | 组件名(组件色,带下标);后面跟小标签(`tag` 框):「具名导出」「默认导出」(同 import / export 关键字的颜色;`export default X`、`export { X }` 写在文件别处也算)、「客户端组件」(组件色)、「记忆化」「转发 ref」(同 const 关键字的颜色,memo / forwardRef 包着) |
|
||||
| 说明 | 组件上面的 JSDoc,Markdown |
|
||||
| 属性 | `N 个`(hint);属性类型写的是一个名字(接口、类型别名、导入的类型)时后面写「 · 来自 XxxProps」,名字带下标(⌘ + 点击跳到类型)。下面一张表格「名称 \| 类型 \| 默认值 \| 必填」,有属性带说明时多一列「说明」。没写参数时写「无」 |
|
||||
| 状态 | useState / useReducer:「名称 \| 初始值 \| 改它的」 |
|
||||
| 引用 | useRef:「名称 \| 初始值 \| 用在」 |
|
||||
| 读取的数据 | 别的钩子(`useXxx(…)` 赋给变量):「变量 \| 来自 \| 意思」 |
|
||||
| 派生值 | 组件体里别的常量,和 useMemo / useCallback:「名称 \| 怎么算 \| 什么时候算」 |
|
||||
| 副作用 | `N 个`(hint),下面每个 useEffect / useLayoutEffect / useInsertionEffect 一张副作用子卡片 |
|
||||
| 事件处理 | 组件体里定义的函数(函数声明、值是函数的常量):「名称 \| 绑定在 \| 做什么」,点名称在表下面展开它的子卡片 |
|
||||
| 渲染 | `N 处返回`(hint),下面是返回的 JSX 画成的结构大纲 |
|
||||
|
||||
属性表:属性类型的字段照类型里的顺序一行一个(类型字面量、同文件的接口 / 类型别名,交叉类型各部分接起来,`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);改它的:改状态的函数名(function 色,带下标),后面括号里写在哪儿改,「;」隔开:「副作用」(几个时「副作用 2」)、处理函数名、派生值名、「<input> 的 onChange(传 e.target.value)」(绑在 JSX 属性上;在列表里一项项渲染的前面加「每个」)、「渲染时,当 … 时」(渲染时按条件重置状态的 `if (…) { setX(…) }`:主体都是在调改状态的函数,这句并进卡片)。
|
||||
- 引用的用在:「挂在 <main> 的 ref 上」(元素写成 `<Tag>`:组件用组件色、能跳到定义(`refers` 到标签名),原生标签灰色,带 `aria-label` / `title` 字符串时后面写「(返回)」),别处用到的「副作用 1、handleSectionClick 里用它」。
|
||||
- 读取的数据的来自照常翻;意思:库自带的钩子照词表(next/navigation 的 `usePathname` 当前路径、`useRouter` 路由器,跳转用、`useSearchParams`、`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 是算值的那个表达式,useCallback 写「函数 」加函数;主体是语句块时写「N 句」,那句照常是块、放进这一行对应的子行上,点名称展开(useCallback 的用事件处理子卡片,useMemo 的照常是变量块);什么时候算:普通常量「每次渲染」,useMemo / useCallback 按依赖读(同副作用的时机)、后面写「(useMemo)」。
|
||||
|
||||
副作用子卡片(组件体里那句 `useEffect(…)` 照常是块,被规则认成这张卡片,放进卡片「副作用」那一行下面):时机按依赖数组读,`[a, b]` 是「a、b 变了时」,`[]` 是「只在挂载时」,没写是「每次渲染后」(keyword 色),useLayoutEffect 后面加「,浏览器绘制前」;做什么是紧贴在上面的注释(几行接成一行,hint 色;这段注释并进组件卡片),没写注释时写「N 句」;副作用函数里的语句照常是块,放进「做什么」那一行下面;最后返回清理函数时那句 return 并进来,多一行「清理」:`() => …` 翻它的主体,语句块一句句翻、「;」隔开。
|
||||
|
||||
事件处理:表里名称是函数名(function 色,带下标);绑定在从 JSX 属性反查:「<button>(打印) 的 onClick」,在属性值里调用时写传的实参「(传 h.id)」,在列表里一项项渲染的写「每个 <button>」,几处「、」隔开;没绑在 JSX 上时写被谁调用「被 download、share 调用」,都没有「—」;做什么是紧贴在上面的注释(函数声明的 JSDoc 也算),直接返回表达式的箭头函数照常翻它,别的写「N 句」。处理函数那句照常是块,被规则认成事件处理子卡片(名称 / 参数 `id: 字符串` / 做什么,函数里的语句放进「做什么」下面),放进表格对应的子行上:默认收着,点名称展开、那一行淡淡垫上事件处理的颜色,同一张表里别的收起;再点名称或子卡片左上角的箭头收起。
|
||||
|
||||
渲染大纲(「渲染」一行下面的子行,一行一个元素,下一层缩进;带下一层的行有折叠箭头):
|
||||
|
||||
- 元素:标签名,原生标签灰色(暗 #808080 / 亮 #6f6f6f),组件用组件色、带下标能跳到定义(`a.b` 一节节都能跳);`<></>` 写「<> 片段,不产生元素」。
|
||||
- `className`(`class`)收成「样式 N 个类」小标(`classes` 框):N 是写死的类;按条件切换的(模板字符串里的三元、`cn(…)` / `clsx(…)` 的实参、`cond && "x"`、`{ "x": cond }`)、算出来的表达式在后面写「 · 随 doc 变」(比较的左边、取反的那个,算出来的写它自己);点开在下面弹出全部类名(写死的一行,按条件的「doc 全等 "terms" 时:…」「否则:…」,算出来的「另加 …」),盖住后面的字,再点收起;悬停也能看。
|
||||
- 别的属性(`key` 不写,列表那一行写)写成「名字=值」,两个空格隔开:字符串照写,表达式照常翻,跨几行写的对象只写有哪些键「对象:h2、a」,值是 JSX 元素时写 `<Tag>`,只写了名字的(`aria-hidden`)就是名字,`{...x}` 照写。
|
||||
- 子节点只有文字、插值时写在同一行:只有一段文字时带引号,插值写成 `{…}`(花括号 hint 色),和文字混着时文字不带引号。
|
||||
- `cond && <X/>` 前面加「当 … 时」小框(`condition`,条件照常翻);三元一行「如果 …」,下面第一行各带「是」「否」小框(`branch`),一支是 null 时写「不渲染」;`items.map((x) => …)` 一行「对 items 的每一项 x」(`iteration` 小框)后面「key …」,回调里先算的常量写「其中 isActive = …」,再下面是每一项渲染的;JSX 注释写成一行「// …」(comment 色)。
|
||||
- 几处返回:分几段,提前 `return null`(`if (cond) return null`,主体只有这一句)写「当 … 时」(`skip` 小框)「不渲染」,提前返回 JSX 的「当 … 时」下面是它渲染的,最后一处「否则」(`branch` 小框);只有一处返回时直接是它的大纲。这些 return(和提前返回的 if)并进卡片。
|
||||
|
||||
组件体里归不进上面分区的语句(没讲进分区的注释、别的 if、循环、单独调用的钩子)照常是组件卡片留下的子块。卡片「编辑」是组件的完整代码(整条语句);副作用、处理函数子卡片各自能编辑、问 AI。代码差异在视图数据这一层比:组件卡片的属性表逐行(属性表按名称配对,加的属性标新加的一行),副作用、处理函数子卡片是块,照常配对、逐行标(改了依赖,「时机」那一行逐截标)。
|
||||
|
||||
样例(`fixtures/desktopTitlebar.tsx.txt`,测试里逐字核对;`⟦…⟧` 是小标签、小框):
|
||||
|
||||
```
|
||||
名称: DesktopTitlebar ⟦具名导出⟧ ⟦客户端组件⟧
|
||||
说明: 窗口顶部 40px. 整条连同里面的文字都是拖拽区 (data-tauri-drag-region="deep"), 按钮除外. 侧栏开关只在侧栏顶部.
|
||||
属性: 1 个
|
||||
platform | DesktopShell["platform"] | — | 是
|
||||
读取的数据: 2 个
|
||||
pathname | usePathname() | 当前路径
|
||||
title | useDesktopShell((s) => s.title) | 从 desktop store 读 title
|
||||
派生值: 1 个
|
||||
center | title ?? PAGE_TITLES[pathname] ?? "" | 每次渲染
|
||||
渲染: 1 处返回
|
||||
div ⟦样式 8 个类⟧ data-tauri-drag-region="deep"
|
||||
div ⟦样式 3 个类⟧
|
||||
⟦当 platform 全等 "macos" 时⟧ span ⟦样式 1 个类⟧ data-testid="traffic-light-inset" aria-hidden
|
||||
div ⟦样式 6 个类⟧
|
||||
span ⟦样式 4 个类⟧ {center}
|
||||
⟦当 platform 全等 "windows" 时⟧ WindowControls
|
||||
```
|
||||
|
||||
和原型不一样的地方:原型里「做什么」「意思」、派生值的读法(「title;没有就按路径查 PAGE_TITLES」)是照设计手写的中文,实现里是注释原文、照翻译插件翻的译文和词表;条件照翻译插件读(`===` 是「全等」,原型写「是」);渲染大纲带下一层的行有折叠箭头;副作用子卡片收起时不换成一行摘要,「做什么」那一行的箭头收起它下面的语句。
|
||||
|
||||
## 命令规则
|
||||
|
||||
shell 脚本大半是一条条命令,一条命令读成什么取决于命令本身(`rm -rf` 是「删除(连同里面的东西、不提示)」,`tar -xzf` 是「解压 tar.gz」)。这些既不是语法(语言插件不管),也不是某种语言的措辞(翻译插件不该认识几十个命令)。命令规则是第五种插件:按命令名注册,把一条命令连同它的选项读成一句译文,像库规则按库认写法一样。
|
||||
@@ -1653,6 +1746,7 @@ interface BlockViewBase {
|
||||
defines?: SourceRange // 块自己的名字(函数名、类名、只声明一个变量时的变量名)在源码里的范围,扩展端从 facts 取(库规则的块取 RuleBlock.defines);跳到的定义是它时闪整块
|
||||
attributes: Record<string, string> // 筛选属性(含 kind),见「筛选」
|
||||
badge?: KindBadge // 库规则的卡片才有:种类的名字、颜色、图标;webview 按它画图标、上色、默认折叠
|
||||
slot?: { row: number, item?: number } // 库规则放进父卡片属性表里的子块才有(RuleSlot):画在父卡片第 row 行下面;item 是讲它的子行,点它才展开(它的块 id 记成折叠着就是收着)
|
||||
children: BlockView[]
|
||||
}
|
||||
|
||||
@@ -1736,6 +1830,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):组件卡片(名称标签、属性表、状态 / 引用 / 读取的数据 / 派生值 / 副作用 / 事件处理、渲染大纲),副作用、处理函数的子卡片,导入的属性类型经索引查
|
||||
rules/reactTree.ts React 规则读语法树的办法:认组件、属性、组件体里的钩子和处理函数、名字用在哪、渲染的 JSX 大纲、别的文件里的属性类型(纯函数)
|
||||
commandRules.ts 命令规则的宿主部分:命令名 → 规则(先注册的算),规则出错记下、当它没读
|
||||
commands/reading.ts 命令规则共用的:认选项(连写拆开、带值的、长选项、--)、拼译文(动作、括号里的意思、照抄不认识的选项)
|
||||
commands/systemCommands.ts 内置命令规则:新建用户 / 用户组(useradd、adduser、addgroup / groupadd)、gpg、install、chown,管 '*' 的只问版本的命令
|
||||
@@ -1855,6 +1951,7 @@ vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vit
|
||||
- 枚举(`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;没注解的类属性、字段中间的方法照源码顺序是子块,「编辑」写回整个类。
|
||||
- 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<Props>`、`forwardRef<R, Props>`、导入的类型查不到和查到了);组件体归到哪儿、名字用在哪(副作用、渲染时重置、挂在 `<main>` 的 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`):第一遍当没查到、记下问了什么(同一索引同名的只记一处);查到后叫重排,交出定义处正是的那一项;同一版本不再查;索引里没有同名的不问语言扩展,定义处不是索引里的项算没有,没变不叫重排;新版本先用上一版的、再查、变了才叫重排;语言扩展没答时已知的不动、隔一会儿再查。
|
||||
- 带自己颜色的词(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`)。
|
||||
|
||||
Reference in new issue
Block a user