🐛 fix(vscode): 代码阅读的钉住卡片改成每张卡片都算一层,折叠着、没有子块的高卡片滚到一半也钉在顶上

This commit is contained in:
cruldra committed 2026-10-08 07:17:26 +08:00
1 parent a53a5d4504
commit a1fbefd3aa
14 files changed
+45 -34

No files matched your search

+8 -8
View File
@@ -239,7 +239,7 @@
- 标了改动的块默认展开(函数、类本来默认收起子块),用户点过的照他的。
- 筛选栏第二行(高 30px,只有有差异数据时才有):比较图标、「对比:<基准>」、「·」、统计(各按改动色,没有的那种不写,都没有写「没有改动」),右边「只看改动」开关、上一处 / 下一处(↑ ↓,悬停提示、`aria-label`)。基准里没有这个文件时只写基准。
- 只看改动:每一层连着没变的块折成一条「… N 块没变 点开」(虚线框),被筛掉的块不算也不打断,删掉的块在哪儿就在哪儿断开;改过的块照常,它下面没变的子块同样折;点开一段就恢复这一段(筛选一变重新折)。开关按工作区记住(`BlockFilter`,`workspaceState` 的 `superpowers.codeReader.onlyChanges`),所有预览共用;跳到定义落在折起来的块上时临时露出来。
- 上一处 / 下一处:在各处改动(新增的最外面那块、自己改了的修改块、删掉的块,照页面先后,被筛掉的不算)之间跳,展开折住它的块,滚到筛选栏下面(外层还钉着的卡片下面,见「钉住外层卡片」)、闪几下;跳完没滚过页面就接着刚跳到的那处走,自己滚过了按视口顶上那块的行找;到头绕回另一头。
- 上一处 / 下一处:在各处改动(新增的最外面那块、自己改了的修改块、删掉的块,照页面先后,被筛掉的不算)之间跳,展开折住它的块,滚到筛选栏下面(外层还钉着的卡片下面,见「钉住卡片」)、闪几下;跳完没滚过页面就接着刚跳到的那处走,自己滚过了按视口顶上那块的行找;到头绕回另一头。
- 总览条:预览右边缘 14px 宽、从筛选栏下面贴到底,每处改动按它在新文件里的行画一段刻度(修改块只画它自己那几行,删掉的块一条 2px 细线),点刻度跳过去(同上一处 / 下一处)。
- 文档刚改、差异还没重算时:块按记号认,没动过的块照样带色条;卡片里的标记按下标认,只在页面上正是差异算的那一版时画。
- 颜色:取 VS Code 编辑器行号旁的改动色带 `--vscode-editorGutter-addedBackground` / `modifiedBackground` / `deletedBackground`(主题没给时用 Dark Modern 的默认值),行内增删的字用 `--vscode-diffEditor-insertedTextBackground` / `removedTextBackground`;整行底色是改动色的 14%,新增卡片底色 10%。
@@ -506,7 +506,7 @@
2. 定义是属性表里某一行 / 子行讲的东西(参数、导入项、一条语句声明几个变量时的某一个、类的泛型参数、对象字面量一项的键——TypeScript 对象的属性名就是定义,嵌套几层都算)→ 只闪这一行 / 子行。范围是翻译插件从 facts 取的节点(`PropertyRow.defines`、`PropertyItem.defines`;字典 / 对象的项标的是键,展开项不标),定义处落在范围里就算(`*args` 的定义在 `args` 上、解构参数里的名字都对得上)。
3. 其余(定义在一行译文里,例如循环变量;翻译插件没填 `defines`)→ 显示着定义处那个名字的块,名字在属性行的子行里时展开那一行(在嵌套的子行里时连它上面各层一起展开);页面上找不到这个名字就取包含定义那一行的块。
- 同一文件原地跳、跳进另一个文件的预览,规则一样。
- 滚过去:目标所在块的祖先块折叠着就展开,目标在折叠的属性行子行里(参数、导入项、字典 / 对象的项)就展开那一行,在嵌套的子行里就连它上面折叠着的各层子行一起展开。目标已在视口里(筛选栏和钉住的外层卡片下面)不滚,否则滚到视口正中(比视口高就顶部对齐)。然后目标的边框和底色往聚焦色(`--vscode-focusBorder`)闪三下,共约 1.5 秒后复原;同一处再跳一次从头闪;系统开了「减少动态效果」时改成静止高亮 1.5 秒。目标是属性行 / 子行但块正在编辑(页面上没有属性表)时闪整块。
- 滚过去:目标所在块的祖先块折叠着就展开,目标在折叠的属性行子行里(参数、导入项、字典 / 对象的项)就展开那一行,在嵌套的子行里就连它上面折叠着的各层子行一起展开。目标已在视口里(筛选栏和钉住的卡片下面)不滚,否则滚到视口正中(比视口高就顶部对齐)。然后目标的边框和底色往聚焦色(`--vscode-focusBorder`)闪三下,共约 1.5 秒后复原;同一处再跳一次从头闪;系统开了「减少动态效果」时改成静止高亮 1.5 秒。目标是属性行 / 子行但块正在编辑(页面上没有属性表)时闪整块。
- VS Code 里没有这种语言的定义提供者(shell):语言扩展答不上时,同一文件里的名字由语言插件认(`LanguageAdapter.symbols`,见「Shell 脚本」),跳到用到的那处之前最近的定义。
- 不按修饰键单击名字是高亮它的各处出现(见下一节);⌘ / Ctrl + 点击名字不会触发块的编辑切换、折叠,也不动高亮。
@@ -521,10 +521,10 @@
- 卡片只读:同页面上那张卡片(类、函数默认收起子块,能在浮窗里折叠展开,只记在浮窗里),块树不带编辑那部分(`BlockTreeState.edit`),没有编辑、问 AI、重写注释的按钮,不标差异、不筛选。浮窗里的名字下标是那个文件的,页面上管名字的几处都不认它们(浮窗根上标 `data-peek`):不按 ⌘ / Ctrl 变链接、不能跳,单击不高亮也不清页面上的高亮,不画页面上的高亮,不再弹卡片;注释里不带路径的引用不成链接(引用按注释所在的文件找,页面只替自己这份文件问),仓库路径链接照样能点。
- 按住 ⌘ / Ctrl 变链接、⌘ / Ctrl + 点击跳转、单击高亮各处出现都照旧;在浮窗外按下鼠标(单击名字、⌘ + 点击)先收起浮窗。
### 钉住外层卡片(Sticky Scroll)
### 钉住卡片(Sticky Scroll)
- 同 VS Code 编辑器的 Sticky Scroll:滚进一张卡片的子块、卡片自己的开头已经滚到筛选栏后面时,筛选栏下面钉一行这张卡片的头;套着几层就摞几行,外层在上、里层在下。只认页面上画出来的:子块画出来了的卡片才钉,折叠着的、被筛掉的、正在编辑的、「只看改动」折起来的卡片下面没有子块,不钉;挂在子行上、展开着的子块也算它那张卡片的子块。和语言无关(只看块树),标差异的页面(未提交的更改、审查工单)一样。
- 钉哪几张(`stickyScroll.ts` 的 `pinnedLines`,照 VS Code 的 `StickyScrollController.findScrollWidgetState`):每行一样高(24px),卡片那一行在筛选栏下沿往下 n × 行高,n 是它外面套着几张卡片;卡片上沿高过它那一行顶、子块的下沿还没过这一行顶时钉住。子块快滚完、下沿高过这一行的下沿时,这一行跟着被顶上去,从上一行下面滑走(层级低一级),整块跟着变矮;下一张同层卡片接上来时它已经滑走。
- 同 VS Code 编辑器的 Sticky Scroll,一张卡片就是一层:卡片的开头(名称那一行)已经滚到筛选栏后面、卡片还有一部分看得见时,筛选栏下面钉一行这张卡片的头;套着几层就摞几行,外层在上、里层在下。卡片管到哪:卡片自己,连同它画出来的子块(挂在子行上、展开着的子块也算)。折叠着的卡片、没有子块的卡片照样钉(函数默认收着,属性表本身就高);折起来的子块、「只看改动」折起来的块没画出来,不算;被筛掉的不在页面上,正在编辑的卡片不钉;删掉的块那张不钉。和语言无关(只看块树),标差异的页面(未提交的更改、审查工单)一样。
- 钉哪几张(`stickyScroll.ts` 的 `pinnedLines`,照 VS Code 的 `StickyScrollController.findScrollWidgetState`):每行一样高(24px),卡片那一行在筛选栏下沿往下 n × 行高,n 是它外面套着几张卡片;卡片上沿高过它那一行顶、下沿(连同子块)还没过这一行顶时钉住。卡片快滚完、下沿高过这一行的下沿时,这一行跟着被顶上去,从上一行下面滑走(层级低一级),整块跟着变矮;下一张同层卡片接上来时它已经滑走。
- 行数:`editor.stickyScroll.enabled` 关着不钉;最多钉 `editor.stickyScroll.maxLineCount` 行,且不超过筛选栏下面可视区域的四分之一(四舍五入,同 VS Code);套得比这深时留外层的,同 VS Code。设置由扩展端按这份文件读(语言自己的设置优先),`ready` 后发给页面,改了再发,页面不写死;没收到之前不钉。
- 一行的样子(`StickyLines.tsx`):和那张卡片左右对齐(越里层越往右),折叠箭头那格留空、概念图标、标题,位置同卡片上的;标题同卡片上的写法(属性表第一行(名称)的值或一行译文,中文名照换、定义处后面淡淡写原名;Markdown 只取第一行),一行放不下省略。底色 `--vscode-editorStickyScroll-background`、悬停 `--vscode-editorStickyScrollHover-background`、下沿阴影 `--vscode-editorStickyScroll-shadow`,同编辑器(主题没给时用编辑器底色、列表悬停色、控件阴影)。
- 点一行:把那张卡片滚到顶上,正好在外层还钉着的几行下面(外层各占一行,最多同上面的上限),闪几下(同跳到定义)。行里的名字照旧:⌘ / Ctrl + 点击跳到定义、悬停看定义卡片;单击名字高亮它的各处出现,同时滚过去;钉住的行里不画高亮(页面上找当前那一处只认卡片里的)。
@@ -2020,7 +2020,7 @@ type BlockView = BlockViewBase & ({ translation: PreviewText, table?: PreviewTab
- `{ type: 'definitionProbed', id, found }`:回答第 `id` 次 `probeDefinition`。
- `{ type: 'definitionPeeked', id, peek? }`:回答第 `id` 次 `peekDefinition` / `peekLink`。`peek` 是 `{ path, line, block: BlockView, names: ShownName[] }`:定义所在的文件(从工作区文件夹算起)、定义处那一行(0 起)、那块卡片连同子块、卡片上显示成中文名的名字(下标是那个文件的);没有卡片时不带。见「悬停看定义卡片」。
- `{ type: 'hoverDelay', delay, hidingDelay }`:VS Code 的 `editor.hover.delay` / `editor.hover.hidingDelay`(毫秒,按这份文件读),`ready` 后发一次,设置改了再发。
- `{ type: 'stickyScroll', enabled, maxLineCount }`:VS Code 的 `editor.stickyScroll.enabled` / `editor.stickyScroll.maxLineCount`(按这份文件读,语言自己的设置优先),`ready` 后发一次,设置改了再发;见「钉住外层卡片」。
- `{ type: 'stickyScroll', enabled, maxLineCount }`:VS Code 的 `editor.stickyScroll.enabled` / `editor.stickyScroll.maxLineCount`(按这份文件读,语言自己的设置优先),`ready` 后发一次,设置改了再发;见「钉住卡片」。
- `{ type: 'occurrencesFound', id, occurrences: { start, end, kind: 'text' | 'read' | 'write' }[] }`:回答第 `id` 次 `findOccurrences`,`[start, end)` 是源码下标,`kind` 同 `DocumentHighlightKind`;语言扩展没答就是空数组。
- `{ type: 'moveOccurrence', step: 1 | -1 }`:两条命令发来,跳到下一处 / 上一处出现。
- `{ type: 'displayNames', version, names: ShownName[] }`:version 这份文档里显示成中文名的名字,查到一步发一步;页面只收自己眼下这个版本的。
@@ -2187,7 +2187,7 @@ vscode/src/codeReader/
editor/occurrences.ts 查名字的各处出现(vscode.executeDocumentHighlights)
editor/occurrenceNavigation.ts 跳到下一处 / 上一处高亮:命令、上下文键
index.ts registerCodeReader(context): CodeReaderApi
vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vite 第二个入口;块右上角的图标按钮在 BlockTools.tsx,悬停提示在 src/components/ui/tooltip.tsx(和看板共用,页面根上挂一次),块的概念图标(图标名 → 图标组件只在 kindIcons.tsx;内置种类 → 图标名在 contract.ts 的 KIND_ICONS,宿主写进概念列表)在 kindIcons.tsx,属性表在 PropertyGrid.tsx,子行排成表格还是列表在 subRows.ts,着色的词和框在 SegmentText.tsx,中文名换字在 displayNames.ts,Markdown 渲染在 markdown.ts / TextPreview.tsx(不带路径的引用的行内规则也在 markdown.ts),仓库链接和引用的点击、问坏没坏在 repoLink.ts / useRepoLinks.ts,悬停看定义卡片什么时候弹、收、放在哪在 definitionPeek.ts、接到页面上在 useDefinitionPeek.ts、浮窗和只读的卡片在 DefinitionPeekPopup.tsx,筛选栏在 FilterBar.tsx,筛选规则在 filter.ts,编辑时盖住哪些块、点「编辑」退出哪些块在 editing.ts,高亮各处出现的规则在 occurrences.ts、接到页面上在 useOccurrences.ts,问 AI 的问答区在 AskThread.tsx、状态和规则在 qa.ts、贴图拖图的规则在 qaImages.ts、收发在 useQa.ts;代码差异:页面上怎么标(块 id、删掉的块挂哪、只看改动折哪几段、各处改动、上一处 / 下一处跳到哪)在 diffView.ts,筛选栏第二行和新文件横幅在 DiffBar.tsx,卡片里的增删在 DiffText.tsx(Markdown 的增删在 markdown.ts 的 renderMarkdownDiff),总览条在 OverviewRuler.tsx,跳到一处改动在 useChangeNav.ts;钉住外层卡片:钉哪几张、顶上去多少在 stickyScroll.ts,量页面、收设置在 useStickyScroll.ts,钉住的几行在 StickyLines.tsx)
vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vite 第二个入口;块右上角的图标按钮在 BlockTools.tsx,悬停提示在 src/components/ui/tooltip.tsx(和看板共用,页面根上挂一次),块的概念图标(图标名 → 图标组件只在 kindIcons.tsx;内置种类 → 图标名在 contract.ts 的 KIND_ICONS,宿主写进概念列表)在 kindIcons.tsx,属性表在 PropertyGrid.tsx,子行排成表格还是列表在 subRows.ts,着色的词和框在 SegmentText.tsx,中文名换字在 displayNames.ts,Markdown 渲染在 markdown.ts / TextPreview.tsx(不带路径的引用的行内规则也在 markdown.ts),仓库链接和引用的点击、问坏没坏在 repoLink.ts / useRepoLinks.ts,悬停看定义卡片什么时候弹、收、放在哪在 definitionPeek.ts、接到页面上在 useDefinitionPeek.ts、浮窗和只读的卡片在 DefinitionPeekPopup.tsx,筛选栏在 FilterBar.tsx,筛选规则在 filter.ts,编辑时盖住哪些块、点「编辑」退出哪些块在 editing.ts,高亮各处出现的规则在 occurrences.ts、接到页面上在 useOccurrences.ts,问 AI 的问答区在 AskThread.tsx、状态和规则在 qa.ts、贴图拖图的规则在 qaImages.ts、收发在 useQa.ts;代码差异:页面上怎么标(块 id、删掉的块挂哪、只看改动折哪几段、各处改动、上一处 / 下一处跳到哪)在 diffView.ts,筛选栏第二行和新文件横幅在 DiffBar.tsx,卡片里的增删在 DiffText.tsx(Markdown 的增删在 markdown.ts 的 renderMarkdownDiff),总览条在 OverviewRuler.tsx,跳到一处改动在 useChangeNav.ts;钉住卡片:钉哪几张、顶上去多少在 stickyScroll.ts,量页面、收设置在 useStickyScroll.ts,钉住的几行在 StickyLines.tsx)
```
## 测试
@@ -2208,7 +2208,7 @@ vscode/webview-ui/code-reader.html + src/codeReader/ 预览界面(React,Vit
- 行替换:多行块替换后缩进正确。
- 跳到定义:TS / Python 译文和属性表里的名字、点分模块名每一节、模块字符串带下标且指回源码原文,关键字、运算符、说明词不带(前面有中文注释也对得上);查到的定义 → 不动 / 原地 / 别的文件 / 挑选框,别的文件用哪种编辑器(`editor/openTarget.test.ts`:有代码阅读的用预览、带位置时滚到那一行闪定义处,没有的用默认的打开方式选中名字,打不开成文本的用默认的打开方式);webview 里按下标找目标块、要展开的祖先块和属性行,滚动位置。悬停看定义卡片:取哪块(`editor/definitionPeek.test.ts`:Python 源码走完整的语言插件 + 翻译插件,类名 → 类那块连同子块,参数 → 所在的函数,变量 → 变量那块,块外面 → 没有,挂在子行上的子块去掉 `slot`;卡片上的中文名只带画出来的、长度对得上的,别的文件只认定义本身),什么时候弹、收(`definitionPeek.test.ts`:停够延迟才问、没到时间移开不问、没有卡片不弹、迟到的回答丢掉、链接问 `peekLink` 点标题发 `openRepoLink`、没收到延迟不弹、移开过 `hidingDelay` 才收、移进浮窗不收、扫过别的名字不往后推、浮窗里不弹别的、换成新名字的卡片、Esc / 滚动后不再弹、换文档版本收起;指到符号的链接才弹;放在下方 / 上方 / 空间大的那边、钳在视口内),只读的卡片(`DefinitionPeekPopup.test.tsx`:类收起子块、没有编辑 / 问 AI / 重写按钮、中文名用卡片带的、引用不成链接;`markdown.test.ts` 的 `peek`)。闪哪一处(TS、Python 源码走完整的语言插件 + 翻译插件 + 视图数据):参数 → 它那个子行,导入项 → 它那一项,多变量声明里的一个 → 它的名称行,类的泛型参数 → 「类型参数」那一行,函数名 / 类名 / 单个变量名(含解构模式)→ 整块,循环变量 → 所在的块,翻译插件没填 `defines` → 整块(照样展开属性行);翻译插件给参数、导入项、声明的变量、泛型参数标的范围指回源码原文;块自己的名字只有函数、类和单变量声明有。
- 高亮各处出现:段的源码范围和出现重叠才亮、边界相接不算、没下标的不亮,讲别处名字的段(`refers`)按那个名字算、自己照抄的位置不算(`occurrences.test.ts` 的 `sourceOf`),点分模块名每一节各按自己的范围认(真实的 Python 译文和属性表:定义处按写入、用到处按读取);写入 → 强高亮色,读取、按文字 → 普通高亮色,同时碰上按写入;单击名字问、点空白处清、点按钮 / 链接 / 筛选栏不动、名字在按钮里照样问,拖选、双击第二下、带修饰键的点击不动,⌘ / Ctrl + 点击照旧发跳到定义、不问出现处;回答连同名字显示、空回答不亮、换名字时回答到前旧的留着、迟到的回答丢掉、答过「没有」再点照样再问、清掉后才到的回答丢掉、换文档版本或翻译清掉而同版本同翻译重发不清、没有文档时不问。键盘跳(`occurrences.test.ts`、`reveal.test.ts`、`editor/occurrenceNavigation.test.ts`):回答照源码先后排、点中的那处是当前那一处、「2/5」和「N 处」;往后 / 往前、到头接着跳,找不到的跳过、总数照旧、一处都找不到不动,点中的不在回答里从点中的地方数起;只有当前那一处描框;能不能跳(高亮、焦点、输入框)第一次必报、之后变了才报;页面上找第一处显示着它的地方、要展开的块 / 属性行 / 子行、跳过筛掉 / 编辑 / 盖住的块、Markdown 不算;`package.json` 的两条命令、默认键、`when`、命令面板,扩展和页面的代码里不写键、不拦键,上下文键有一个预览能跳就真、变了才设,命令发给能跳的预览、没有就发给活动的;按键那一刻的位置跳转时回一次、隔太久 / 点过鼠标 / 没按过键不回,跳完守住跳到的位置、过了 `KEY_SCROLL_MS` 或又按键、点鼠标不守。
- 钉住外层卡片(`stickyScroll.test.ts`、`StickyLines.test.tsx`、`BlockTree.test.tsx`):上沿刚滚过它那一行才钉、里层按自己那一行算,外层在上里层在下,同层前一张滚完、后一张还没到只钉外层;子块快滚完时最里那行被顶上去、正好到行顶时整行顶到上一行下面,最后一张子块同时是外层的结尾时里层先走、再顶外层;比上限深时留外层的;最多行数按设置和可视区域的四分之一;滚到顶上时上面钉着多高;钉的没变不重画。一行里是概念图标和标题:属性表第一行的值(中文名照换、不带属性名、不带说明)、一行译文、Markdown 只取第一行、空属性表只有图标。画出来的子层记着它是哪张卡片的,收着时没有。
- 钉住卡片(`stickyScroll.test.ts`、`StickyLines.test.tsx`、`BlockTree.test.tsx`):没有子块的高卡片(折叠着的函数)名称那行滚到后面、下面还看得见就钉,滚完时被顶上去;上沿刚滚过它那一行才钉、里层按自己那一行算,外层在上里层在下,同层前一张滚完、后一张还没到只钉外层;子块快滚完时最里那行被顶上去、正好到行顶时整行顶到上一行下面,最后一张子块同时是外层的结尾时里层先走、再顶外层;比上限深时留外层的;最多行数按设置和可视区域的四分之一;滚到顶上时上面钉着多高;钉的没变不重画。一行里是概念图标和标题:属性表第一行的值(中文名照换、不带属性名、不带说明)、一行译文、Markdown 只取第一行、空属性表只有图标。画出来的子层记着它是哪张卡片的,收着时没有。
- 中文名:
- 语言插件认写法(`languages/pythonNames.test.ts`、`languages/typescriptNames.test.ts`,真实解析):Python 注解 `Annotated`(`typing.Annotated`、`t.Comment`,f-string、别的调用不算)、装饰器(函数、类)、行尾记号(多行参数表、`*args`、`self.x`、变量、字段、枚举成员,记号前面有别的字,记号的范围)、单独成行 / 那一行没有定义的不算、函数那一行给参数、docstring 的谷歌写法和 Sphinx 写法(带类型、星号、续行、别的段落不算)、第一小句和 12 个字的限制、明写的记号盖过说明文字、没取的记号照样去掉;TypeScript 装饰器(类、`@d export class`、字段、构造函数和方法的参数、方法隔着别的装饰器、名字最后一节)、行尾 `//` 记号(参数、变量、枚举成员、类字段、接口属性,块注释不算)、JSDoc `@param`(类型、短横、可选、属性不算、export 函数、方法、箭头函数变量、类字段)、JavaScript 默认值参数;预筛和各方言的文件。
- 记号不是说明(`nameResolution.test.ts`,走 `readBlockViews`):Python、TypeScript 枚举成员的行尾记号去掉、前面的字照留、只有记号的没有说明列。