✨ feat(vscode): 代码阅读预览按住 ⌘ / Ctrl 点名字跳到定义

This commit is contained in:
cruldra committed 2026-09-25 12:54:45 +08:00
1 parent e5db1a2e65
commit 4e146eb48f
20 files changed
+870 -60

No files matched your search

@@ -0,0 +1,68 @@
import type { Location, LocationLink, Position, Range, Uri } from 'vscode'
import type { DefinitionTarget } from './definitionAction'
import { commands, window, workspace } from 'vscode'
import { logger } from '../../logging/logger'
/** VS Code 查到的一处定义,加上打开目标文件才知道的信息。 */
export interface Definition extends DefinitionTarget {
location: { uri: Uri, range: Range }
/** 定义处在目标文件里的下标,预览靠它找到是哪块、哪一行属性 */
offset: number
/** 定义所在那一行的源码,列在挑选框里 */
lineText: string
}
/** 交给 VS Code 注册的定义提供者(Pylance、TypeScript 语言服务……),和普通编辑器里 ⌘ + 点击查的是同一套。 */
async function provided(uri: Uri, position: Position): Promise<(Location | LocationLink)[]> {
try {
return await commands.executeCommand<(Location | LocationLink)[] | undefined>('vscode.executeDefinitionProvider', uri, position) ?? []
}
catch (err) {
logger.add({ level: 'warn', source: 'codeReader', message: `查定义失败 ${uri.toString()}:${position.line + 1}:${err instanceof Error ? err.message : String(err)}` })
return []
}
}
export async function hasDefinition(uri: Uri, position: Position): Promise<boolean> {
return (await provided(uri, position)).length > 0
}
/** uri 里 position 处名字的所有定义;目标打不开成文本的(例如 Python 命名空间包指向的目录)略过。 */
export async function findDefinitions(uri: Uri, position: Position, supports: (languageId: string) => boolean): Promise<Definition[]> {
const found = await Promise.all((await provided(uri, position)).map(async (item): Promise<Definition[]> => {
const location = 'targetUri' in item
? { uri: item.targetUri, range: item.targetSelectionRange ?? item.targetRange }
: { uri: item.uri, range: item.range }
const { start } = location.range
try {
const document = await workspace.openTextDocument(location.uri)
return [{
uri: location.uri.toString(),
line: start.line,
character: start.character,
supported: supports(document.languageId),
location,
offset: document.offsetAt(start),
lineText: document.lineAt(start.line).text,
}]
}
catch (err) {
logger.add({ level: 'warn', source: 'codeReader', message: `定义所在的 ${location.uri.toString()} 打不开:${err instanceof Error ? err.message : String(err)}` })
return []
}
}))
return found.flat()
}
/** 几处定义里挑一处:每项是「相对路径:行号」,下面一行是那行源码。 */
export async function pickDefinition<T extends Definition>(definitions: T[]): Promise<T | undefined> {
const picked = await window.showQuickPick(
definitions.map(definition => ({
label: `${workspace.asRelativePath(definition.location.uri)}:${definition.line + 1}`,
detail: definition.lineText.trim(),
definition,
})),
{ placeHolder: '跳到哪一处定义', matchOnDetail: true },
)
return picked?.definition
}