Files
superwork/vscode/src/codeReader/definitionKinds.ts
T

55 lines
2.2 KiB
TypeScript

import type { NamedKind } from './blockView'
import type { BlockKind, LanguageAdapter } from './contract'
import type { TreeSitterRuntime } from './runtime'
import { kindAt, readNamedKinds } from './blockView'
import { RecentPromises } from './recentPromises'
/** 定义所在的那份文件(TextDocument 的子集):uri、版本、语言、内容。 */
export interface KindSource {
uri: string
version: number
languageId: string
getText: () => string
}
/** 默认记几份文件 */
const LIMIT = 64
/**
* 调用的名字的定义是哪种块(分新建还是调用):定义所在的文件解析一遍,按 uri 记着这一版各块的名字和种类(见 NamedKind),
* 同一版再问不再解析,几处同时问同一版也只解析一次;预览发文档时把读出的那份交进来(remember),定义就在这份文档里时直接用。
* 最近用过的留 limit 份。这里不碰 VS Code。
*/
export class DefinitionKinds {
private readonly files: RecentPromises<NamedKind[] | undefined>
constructor(
private readonly languageFor: (languageId: string) => LanguageAdapter | undefined,
private readonly runtime: Pick<TreeSitterRuntime, 'parse'>,
limit = LIMIT,
) {
this.files = new RecentPromises(limit)
}
/** uri 这份文件 version 这一版各块的名字和种类(预览发文档时读出的)。 */
remember(uri: string, version: number, kinds: NamedKind[]): void {
this.files.set(uri, version, Promise.resolve(kinds))
}
/** file 里 offset 处的名字定义的是哪种块;没有语言插件、不在哪块的名字上时没有。 */
async kindAt(file: KindSource, offset: number): Promise<BlockKind | undefined> {
const kinds = await this.kindsOf(file)
return kinds && kindAt(kinds, offset)
}
private kindsOf(file: KindSource): Promise<NamedKind[] | undefined> {
const known = this.files.get(file.uri, file.version)
if (known)
return known
const adapter = this.languageFor(file.languageId)
if (!adapter)
return Promise.resolve(undefined)
return this.files.set(file.uri, file.version, readNamedKinds(this.runtime, adapter, file.languageId, file.getText()))
}
}