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 constructor( private readonly languageFor: (languageId: string) => LanguageAdapter | undefined, private readonly runtime: Pick, 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 { const kinds = await this.kindsOf(file) return kinds && kindAt(kinds, offset) } private kindsOf(file: KindSource): Promise { 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())) } }