Files
superwork/vscode/src/codeReader/editor/definitions.ts
T

75 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Location, LocationLink, Position } from 'vscode'
import type { RuleLocation } from '../contract'
import type { DefinitionTarget } from './definitionAction'
import { commands, Range, Uri, window, workspace } from 'vscode'
import { logger } from '../../logging/logger'
/** 查到的一处定义:文件和定义处那个名字的范围。 */
export interface FoundLocation { uri: Uri, range: Range }
/** VS Code 查到的一处定义,加上打开目标文件才知道的信息。 */
export interface Definition extends DefinitionTarget {
location: FoundLocation
/** 定义处在目标文件里的下标,预览靠它找到是哪块、哪一行属性 */
offset: number
/** 定义所在那一行的源码,列在挑选框里 */
lineText: string
}
/** uri 里 position 处名字的定义:交给 VS Code 注册的定义提供者(Pylance、TypeScript 语言服务……),和普通编辑器里 ⌘ + 点击查的是同一套。 */
export async function languageDefinitions(uri: Uri, position: Position): Promise<FoundLocation[]> {
let provided: (Location | LocationLink)[]
try {
provided = 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 []
}
return provided.map(item => 'targetUri' in item
? { uri: item.targetUri, range: item.targetSelectionRange ?? item.targetRange }
: { uri: item.uri, range: item.range })
}
/** 库规则查到的定义(RuleLocation)换成 VS Code 的位置。 */
export function ruleDefinitions(locations: readonly RuleLocation[]): FoundLocation[] {
return locations.map(({ uri, start, end }) => ({ uri: Uri.parse(uri), range: new Range(start.line, start.character, end.line, end.character) }))
}
/** 查到的各处定义打开目标文件补上行文字、下标;目标打不开成文本的(例如 Python 命名空间包指向的目录)略过。 */
export async function findDefinitions(locations: readonly FoundLocation[], supports: (languageId: string) => boolean): Promise<Definition[]> {
const found = await Promise.all(locations.map(async (location): Promise<Definition[]> => {
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
}