✨ feat(vscode): 代码阅读「未提交的更改」点文件行改成打开标准预览(和主栏一样不标差异),文件行上加「打开更改」按钮才打开对比上一次提交的预览,语言没有代码阅读的走 VS Code 自带的差异视图,删掉的文件行不挂这个按钮

This commit is contained in:
cruldra committed 2026-10-14 13:47:59 +11:00
1 parent 4b6e110a44
commit c2a13e5691
18 files changed
+211 -79

No files matched your search

+9 -21
View File
@@ -1,7 +1,6 @@
import type { ExtensionContext, Uri } from 'vscode'
import type { CodeReaderApi, RuleLocation, RuleReference } from './contract'
import type { OpenedFrom } from './diff/diffBase'
import type { TargetSpot } from './editor/openTarget'
import type { FileNode } from './explorer/fileTree'
import type { FeatureRow } from './features/featureViews'
import type { IndexedName } from './nameResolution'
@@ -14,7 +13,7 @@ import { previewDiffBases } from './diff/diffBase'
import { BlockFilter } from './editor/blockFilter'
import { FIND, FIND_NEXT, FIND_PREVIOUS } from './editor/findCommands'
import { NEXT_OCCURRENCE, PREVIOUS_OCCURRENCE } from './editor/occurrenceNavigation'
import { languageOf, openingFor } from './editor/openTarget'
import { languageOf, openFile } from './editor/openTarget'
import { PATH_PREVIEW_SCHEME } from './editor/pathPreview'
import { PreviewEditorProvider } from './editor/previewEditor'
import { EditorSwitch } from './editor/switchEditor'
@@ -73,26 +72,14 @@ const LANGUAGE_IDS_CONTEXT = 'superpowers.codeReader.languageIds'
const FILES_VIEW = 'superpowers.codeReader.files'
const OPEN_COMMAND = 'superpowers.codeReader.open'
/**
* 侧栏点文件、点「了解一块代码」栏里的一条:语言受支持就用预览打开、告诉它是从哪一栏点开的(`from`),否则用 VS Code 默认的打开方式(见 [openingFor])。
* 给了 `spot`(条目的符号在哪)就跳过去:预览滚到那一块、闪一下,普通编辑器选中那个名字。
*/
async function openFile(uri: Uri, from: OpenedFrom, registry: CodeReaderRegistry, previews: PreviewEditorProvider, spot?: TargetSpot): Promise<void> {
const opening = openingFor(await languageOf(uri), languageId => registry.languageFor(languageId) !== undefined, spot)
if (opening.kind === 'default') {
await commands.executeCommand('vscode.open', uri, opening.selection && { selection: opening.selection })
return
}
await previews.open(uri, { from, ...(opening.reveal && { reveal: opening.reveal }) })
}
/**
* 接入代码阅读:注册表、侧栏的几栏(主栏的文件树可以按审查工单只看 PR 改过的文件,常驻的「未提交的更改」,每个收藏目录、每个「了解一块代码」找的功能各一栏;
* 行上有问路径、复制、在系统文件管理器里显示、文件注释、收藏目录)、预览编辑器(含问 AI,审查工作副本里的和从「未提交的更改」点开的标差异)、编辑器右上角的「预览 / 编辑」切换。
* 行上有问路径、复制、在系统文件管理器里显示、文件注释、收藏目录)、预览编辑器(含问 AI,审查工作副本里的、用「未提交的更改」文件行上的「打开更改」打开的标差异)、编辑器右上角的「预览 / 编辑」切换。
* 内置的语言插件、翻译插件、库规则和命令规则与其他扩展一样通过返回的 API 注册。
*/
export function registerCodeReader(context: ExtensionContext): CodeReaderApi {
const registry = new CodeReaderRegistry(context.globalState)
const supports = (languageId: string): boolean => registry.languageFor(languageId) !== undefined
const runtime = new TreeSitterRuntime(bundledWasmDir())
const report = (message: string): void => logger.add({ level: 'warn', source: 'codeReader', message })
// 有语言插件的按它的语法包解析,没有的(接口契约 openapi.json)用索引专用的语法包
@@ -121,7 +108,7 @@ export function registerCodeReader(context: ExtensionContext): CodeReaderApi {
const favorites = new FavoriteViews(context.workspaceState, slots, OPEN_COMMAND, decorator)
const features = new FeatureViews(context.workspaceState, slots, qa, item => locateItem(item, { adapterFor: languageId => registry.languageFor(languageId), runtime }))
const heads = new HeadBases()
// 预览对比什么:审查工作副本里的对比 PR 的目标分支,从「未提交的更改」一栏点开的有改动的文件对比上一次提交,别处打开的不比
// 预览对比什么:审查工作副本里的对比 PR 的目标分支,用「未提交的更改」文件行上的「打开更改」打开的有改动的文件对比上一次提交,别处打开的(含点这一栏的文件行)不比
const diffBases = previewDiffBases(review, heads)
// 预览里代码的上色、卡片的种类色跟着当前颜色主题走
const codeColors = new ThemeColors(readCodeColors)
@@ -158,7 +145,7 @@ export function registerCodeReader(context: ExtensionContext): CodeReaderApi {
registerPathAsk(
async (uri) => {
const languageId = await languageOf(uri)
return languageId !== undefined && registry.languageFor(languageId) !== undefined
return languageId !== undefined && supports(languageId)
},
uri => previews.open(uri, { focusPath: true }),
),
@@ -188,7 +175,8 @@ export function registerCodeReader(context: ExtensionContext): CodeReaderApi {
commands.registerCommand('superpowers.codeReader.refreshUncommitted', () => uncommitted.reload()),
commands.registerCommand('superpowers.codeReader.hideUncommittedTests', () => uncommitted.setTestsHidden(true)),
commands.registerCommand('superpowers.codeReader.showUncommittedTests', () => uncommitted.setTestsHidden(false)),
// 「更改」组文件行上的按钮传来的是这一行的节点
// 「未提交的更改」文件行上的按钮传来的是这一行的节点:「打开更改」两组都有,「暂存」只在「更改」组
commands.registerCommand('superpowers.codeReader.openChanges', (node?: FileNode) => node && openFile(node.uri, 'uncommitted', supports, previews)),
commands.registerCommand('superpowers.codeReader.stage', (node?: FileNode) => uncommitted.stage(node)),
// 目录行上的按钮传来的是这一行的节点;栏标题栏上的按钮不带是哪一栏,所以每个栏位各一套命令
commands.registerCommand('superpowers.codeReader.addFavorite', (node?: FileNode) => favorites.add(node)),
@@ -201,12 +189,12 @@ export function registerCodeReader(context: ExtensionContext): CodeReaderApi {
commands.registerCommand(featureCommand('remove', slot), () => features.remove(slot)),
]),
// 侧栏各栏的文件行传来的是 (文件 Uri, 从哪一栏点开的)
commands.registerCommand(OPEN_COMMAND, (uri: Uri, from: OpenedFrom) => openFile(uri, from, registry, previews)),
commands.registerCommand(OPEN_COMMAND, (uri: Uri, from: OpenedFrom) => openFile(uri, from, supports, previews)),
// 「了解一块代码」栏里的条目行传来的是这一行:按眼下的代码找到它再打开
commands.registerCommand(OPEN_FEATURE_ITEM, async (row?: FeatureRow) => {
const found = await features.target(row)
if (found)
await openFile(found.uri, 'other', registry, previews, found.spot)
await openFile(found.uri, 'other', supports, previews, found.spot)
}),
// 标题栏按钮传来的是 (文件 Uri, { groupId }),原样转给 VS Code 定位是哪一组的哪个标签
commands.registerCommand('superpowers.codeReader.showPreview', (...args: unknown[]) => switcher.showPreview(args)),