✨ feat(vscode): 代码阅读库规则卡片不再吞掉字段中间的方法,编辑时收起子块

This commit is contained in:
cruldra committed 2026-09-26 19:58:43 +08:00
1 parent c803fd457f
commit 2251e67ef8
15 files changed
+280 -54

No files matched your search

+12 -9
View File
@@ -77,7 +77,8 @@
- 预览:函数、类、导入、常量、变量块是属性表(见下节),注释块是 Markdown 说明(见「注释的 Markdown 排版」),其余块是一行译文;按词着色,等宽字体(中文部分用系统中文字体回退)。一行译文的块带说明(Python 字段的属性 docstring)时,说明按 Markdown 排在译文下面。
- 编辑:这一块的源码(按语法树着色),可以直接改;蓝色边框。按钮文字「预览」;Esc 也回到预览。
- 复合语句(函数、条件、循环……)块里只放它自己的头部那几行(主体交给子块);收尾括号、空行不成块。函数、类的说明(JSDoc / docstring)也算在函数、类块的头部里;Python 常量、变量、字段的属性 docstring 算在它那一块里(中间隔着的空行也是)。
- 库规则认领的块(数据表卡片等,见「库规则」):种类标签的字和颜色由规则给,内容是规则给的属性表;它的编辑范围是原来的头部加上并进来的成员,「编辑」时一起改。
- 库规则认领的块(数据表卡片等,见「库规则」):种类标签的字和颜色由规则给,内容是规则给的属性表;它的编辑范围是原块整条语句(类就是整个类),「编辑」时整段一起改。规则没并进卡片的子块(方法、别的成员)不管写在类主体哪儿(两个字段中间也算),都照源码顺序是卡片的子块,各自的「编辑」照旧只改自己那几行。
- 同一行不会同时在两处能改(能同时编辑的几段互不重叠):一块在编辑时,编辑范围和它重叠的子孙块不画(卡片编辑时它下面的子块全收起来,一行写完的 `if x: return 1` 编辑时同一行的子块也不画),回到预览就照原样出来;点一块的「编辑」时,编辑范围和它重叠的别的块退出编辑。跳到定义的目标块正被盖住时闪盖住它的那块。
- 折叠:函数、类块和库规则认领的块打开时默认收起子块(属性表照常显示),其余块默认展开;用户点过折叠箭头的块按他点的,记在视图状态里(见「预览数据与消息」)。
- 顶部筛选栏:见「筛选」;翻译下拉框(多于一种翻译时才显示)也在栏里。
- 解析失败:显示错误原因,并提示用「编辑」查看源码。
@@ -426,7 +427,7 @@ export interface RuleBlock {
rows: PropertyRow[]
defines?: SourceRange // 块自己的名字(类名):跳到的定义是它时闪整块
attributes?: BlockAttributes // 规则自己的筛选属性,叠在宿主从原块推出的上面;kind 填了不算
absorbs: CodeBlock[] // 并进来的子块(原块 children 里的对象本身),不再单独成块
absorbs: CodeBlock[] // 并进来的子块(原块 children 里的对象本身),不再单独成块;没并的照源码顺序留作子块,编辑范围是原块整条语句
}
/** 规则看一块时宿主给的。 */
@@ -505,7 +506,7 @@ export interface CodeReaderApi {
- 启用:宿主收集这份源码里各层导入块的 `ImportFacts.sources`(去掉引号),有一个等于规则的某个库、或以「库.」「库/」开头(子模块),规则才对这份源码启用。规则不用自己读导入语句。
- 认领:宿主从顶层往下每块问一遍启用的规则,按注册顺序,第一个认领的算(先注册的优先);被认领的块留下的子块照样问。
- 出错:规则抛错、或替身不合约定(种类 id 没带规则 id 前缀、并进来的不是这块的子块、属性表里的引用标的不是自己 / 规则没给 `resolve` / 引用的段没带下标)时,宿主记一条警告日志(输出面板「Superpowers」),当它没认、接着问下一个规则;都没认这块照常显示。预览不会因为规则出错而失败。
- 并进来的子块:不再单独成块。替身的编辑范围从原块头部开始,到最后一个并进来的子块(整条语句)结束,连成一段;夹在这段中间的子块(注释,或者写在字段中间的方法)也跟着并进来,保证每一行只在一块里能改;这段以后的子块(方法等)照常是替身的子块。
- 并进来的子块:不再单独成块,由规则挑(`absorbs`),宿主不替规则多并。没并进来的子块(方法、规则不讲的注释)不管写在哪(字段中间也算),都照源码顺序是替身的子块,预览里照常看得见。替身的编辑范围是原块整条语句(从头部开始,到原块连同各子块的最后一行,类就是整个类),盖住留下的子块的行:替身编辑时预览不画这些子块,同一行不会同时在两处能改(见「预览(代码块树)」)。内置规则并成员时连同紧挨在它前面的注释块一起并(讲的是这个成员),讲方法的注释、最后面的注释照常留下。
- 替身在预览里:种类标签的字、颜色取 `badge`(暗色、亮色主题各取一个),内容是 `rows`;筛选的 `kind` 是替身的种类 id,其余属性从原块推出(原来的类是公共的,数据表也是公共的),叠上规则给的;筛选栏里这个种类叫 `badge.label`;和类块一样默认收起子块。
### 规则自己查定义
@@ -575,7 +576,7 @@ export interface CodeReaderApi {
说明:`comment=` 的字符串(comment 色);没写 `comment=` 时用字段的属性 docstring(`context.doc` 给的原文,comment 色;它随字段并进卡片)。关系的目标取第一个位置参数(字符串去掉引号),没写就用 `Mapped[...]` 里的类型;说明写「对方字段 X」(`back_populates` / `backref`)、「单个对象」(`uselist=False`)、「中间表 X」(`secondary`),用 ` · ` 隔开。
并进卡片的子块:`__tablename__`、`__table_args__`、每个字段和关系的赋值;`__mapper_args__`、普通属性、方法照常是子块。
并进卡片的子块:`__tablename__`、`__table_args__`、每个字段和关系的赋值,连同紧挨在它们前面的注释块;`__mapper_args__`、普通属性、方法(写在两个字段中间也算)和讲它们的注释照常是子块。
样例(luffy `referral/models.py` 的 `ReferralRelation`,测试里逐字核对):
@@ -803,7 +804,7 @@ Python 内置类型(仅注解里):str 字符串、int 整数、float 浮
interface BlockViewBase {
id: string // 在树里的位置,例如 '6.1.0'
kind: string // BlockKind,或库规则认领的块的种类 id(sqlalchemy.table)
startLine: number // 编辑范围的起止行,0 起:一般是 header,库规则认领的块到最后一个并进来的子块为止
startLine: number // 编辑范围的起止行,0 起:一般是 header,库规则认领的块是原块整条语句(盖住它留下的子块)
endLine: number
code: string // 编辑范围的源码,去掉共同缩进
codeTokens: Segment[] // 源码着色(拼起来等于 code)
@@ -866,13 +867,14 @@ vscode/src/codeReader/
runtime.ts web-tree-sitter 初始化、语法包加载、解析
blockView.ts CodeBlock → BlockView(源码着色、去缩进、调用翻译)
attributes.ts 块的筛选属性(从 facts 推出通用属性 + 语言自己报的)、文件里出现过的属性和块数
libraryRules.ts 库规则的宿主部分:按导入启用、按注册顺序认领、出错退回、并进来的范围、查规则标的引用
libraryRules.ts 库规则的宿主部分:按导入启用、按注册顺序认领、出错退回、编辑范围和留下的子块、查规则标的引用
workspaceIndex.ts 库规则的工作区索引(WorkspaceIndex)的宿主部分:建、缓存、跟着文件改动更新(不碰 VS Code)
workspaceFiles.ts 工作区索引在 VS Code 里怎么找文件、读文件、盯改动(排除规则)
rules/sqlalchemy.ts 内置 SQLAlchemy 库规则(只 import contract 和同目录的共用代码)
rules/celery.ts 内置 Celery 库规则:两张卡片、频率换算、任务名索引
rules/pythonTree.ts Python 库规则共用的读语法树的办法(名字最后一节、字符串内容、调用的参数、别名)
rules/segments.ts 库规则共用的拼属性表文字的办法
rules/children.ts 库规则共用的挑子块的办法(成员连同紧挨在它前面的注释块)
languages/typescript.ts
languages/python.ts (iteration:推导式的结构)
translators/zhTemplate.ts 模板直译引擎(按语言的表驱动,词性、遍历结构从 SourceDocument.language 取;推导式排进框见 iterationGroup)
@@ -892,7 +894,7 @@ vscode/src/codeReader/
editor/definitionAction.ts 查到的定义 → 怎么跳(纯函数)
editor/occurrences.ts 查名字的各处出现(vscode.executeDocumentHighlights)
index.ts registerCodeReader(context): CodeReaderApi
vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vite 第二个入口;属性表在 PropertyGrid.tsx,子行排成表格还是列表在 subRows.ts,着色的词和框在 SegmentText.tsx,Markdown 渲染在 markdown.ts / TextPreview.tsx,筛选栏在 FilterBar.tsx,筛选规则在 filter.ts,高亮各处出现的规则在 occurrences.ts、接到页面上在 useOccurrences.ts)
vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vite 第二个入口;属性表在 PropertyGrid.tsx,子行排成表格还是列表在 subRows.ts,着色的词和框在 SegmentText.tsx,Markdown 渲染在 markdown.ts / TextPreview.tsx,筛选栏在 FilterBar.tsx,筛选规则在 filter.ts,编辑时盖住哪些块、点「编辑」退出哪些块在 editing.ts,高亮各处出现的规则在 occurrences.ts、接到页面上在 useOccurrences.ts)
```
## 测试
@@ -912,8 +914,9 @@ vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vit
- 跳到定义:TS / Python 译文和属性表里的名字、点分模块名每一节、模块字符串带下标且指回源码原文,关键字、运算符、说明词不带(前面有中文注释也对得上);查到的定义 → 不动 / 原地 / 预览 / 普通编辑器 / 挑选框;webview 里按下标找目标块、要展开的祖先块和属性行,滚动位置。闪哪一处(TS、Python 源码走完整的语言插件 + 翻译插件 + 视图数据):参数 → 它那个子行,导入项 → 它那一项,多变量声明里的一个 → 它的名称行,类的泛型参数 → 「类型参数」那一行,函数名 / 类名 / 单个变量名(含解构模式)→ 整块,循环变量 → 所在的块,翻译插件没填 `defines` → 整块(照样展开属性行);翻译插件给参数、导入项、声明的变量、泛型参数标的范围指回源码原文;块自己的名字只有函数、类和单变量声明有。
- 高亮各处出现:段的源码范围和出现重叠才亮、边界相接不算、没下标的不亮,点分模块名每一节各按自己的范围认(真实的 Python 译文和属性表:定义处按写入、用到处按读取);写入 → 强高亮色,读取、按文字 → 普通高亮色,同时碰上按写入;单击名字问、点空白处清、点按钮 / 链接 / 筛选栏不动、名字在按钮里照样问,拖选、双击第二下、带修饰键的点击不动,⌘ / Ctrl + 点击照旧发跳到定义、不问出现处;回答连同名字显示、空回答不亮、换名字时回答到前旧的留着、迟到的回答丢掉、答过「没有」再点照样再问、清掉后才到的回答丢掉、换文档版本或翻译清掉而同版本同翻译重发不清、没有文档时不问。
- 注册表:注册 / 注销后 `languageIds` 与可选翻译正确;同一语言多个翻译时按 `globalState` 选中;库规则按语言过滤、保持注册顺序,注销后不再给。
- 库规则(宿主):导入了库(`from sqlalchemy.orm import …`、`import sqlalchemy as sa`、`import os, sqlalchemy`、函数里的导入)才启用,`sqlalchemy_utils`、相对导入 `.sqlalchemy` 不算;先注册的规则优先;规则抛错、替身不合约定时记下来、接着问下一个、都没认照常显示,document 消息不带 error;编辑范围到最后一个并进来的子块、夹在中间的注释并进来、之后的子块留下,每一行最多在一块里能改;筛选的种类计数、名字取规则的标签;`rules/` 下的非测试文件只 import contract。
- SQLAlchemy 规则:luffy `referral/models.py` 的节选(`fixtures/sqlalchemyModels.py`)里 `ReferralRelation` 的属性表逐字同上面的样例,其余模型覆盖 `Mapped[bool]`、`int | None` 可空、`default=None`、`server_default`、别名的 `PgUUID`、`_enum` 辅助函数、关系、唯一约束、删除时置空 / 级联删除;枚举类、函数照常显示;卡片的编辑范围是整个类、每行只在一块里;类名、字段名的定义范围和带下标的词都指回源码原文;webview 里跳到类名闪整块、跳到字段名只闪字段表里那一行并先展开「字段」那一行,卡片默认折叠;`import sqlalchemy as sa` 的老写法 `Column`、类型全名、只写注解的字段、不认得的类型照写法、docstring 当说明、方法留作子块。
- 库规则(宿主):导入了库(`from sqlalchemy.orm import …`、`import sqlalchemy as sa`、`import os, sqlalchemy`、函数里的导入)才启用,`sqlalchemy_utils`、相对导入 `.sqlalchemy` 不算;先注册的规则优先;规则抛错、替身不合约定时记下来、接着问下一个、都没认照常显示,document 消息不带 error;编辑范围是整个类、没并进来的子块(夹在中间的注释、两个字段中间的方法)照源码顺序留下,卡片「编辑」写回整个类,能编辑的范围只在块和它的子孙之间重叠;筛选的种类计数、名字取规则的标签;`rules/` 下的非测试文件只 import contract。
- 同一行不在两处能改(webview `editing.test.ts`):卡片编辑时盖住它留下的子块(字段中间的方法连同主体),跳到被盖住的块改闪卡片;只编辑头部的块不盖住子块,一行写完的块盖住同一行的子块;点「编辑」时重叠的块退出编辑;任意点法下正在编辑、画出来的块两两不重叠。
- SQLAlchemy 规则:luffy `referral/models.py` 的节选(`fixtures/sqlalchemyModels.py`)里 `ReferralRelation` 的属性表逐字同上面的样例,其余模型覆盖 `Mapped[bool]`、`int | None` 可空、`default=None`、`server_default`、别名的 `PgUUID`、`_enum` 辅助函数、关系、唯一约束、删除时置空 / 级联删除;枚举类、函数照常显示;卡片的编辑范围是整个类、每行只在一块里;两个字段中间写着的方法照常是子块、卡片「编辑」写回整个类、字段前面的注释并进卡片、讲方法的注释留下;类名、字段名的定义范围和带下标的词都指回源码原文;webview 里跳到类名闪整块、跳到字段名只闪字段表里那一行并先展开「字段」那一行,卡片默认折叠;`import sqlalchemy as sa` 的老写法 `Column`、类型全名、只写注解的字段、不认得的类型照写法、docstring 当说明、方法留作子块。
- 库规则的引用:引用标的不是自己、规则没给 `resolve`、段没带下标时当替身不合约定;查引用交给标它的规则,规则不在、没给 `resolve`、出错时记下来、当作没有定义;视图数据里的引用(下标 → 引用)只从规则认领的块的属性表里取,查定义时是引用的问规则、其余问语言扩展,只对记下它的那个文档版本有效(`editor/definitionSources.test.ts`)。
- 工作区索引(内存里的工作区,`MemoryIndexHost`):第一次查才建、同一个 id 只建一份;查到的是名字的行列(列按 UTF-16)、照文件和行排好;预筛不过的不解析;改了只重看那一份、新建加进来、删目录连里面的去掉;新建 / 删掉问过在不在的文件(`__init__.py`)时问过它的文件重看;建索引期间的改动建好后补上;读不了、规则出错的记下来、别的照常;dispose 后不再盯着。
- 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` 后跟着变、没有任务装饰器的文件不解析。