import type { DisplayName, LocalSymbol, RuleContext, SourceDocument } from '../contract' import type { CodeReaderRegistry } from '../registry' import type { TreeSitterRuntime } from '../runtime' import type { DocumentMessage } from './messages' import { fileAttributes, mergeAttributeNames } from '../attributes' import { readBlockViews } from '../blockView' import { commandRuleLookup } from '../commandRules' import { markEntryBlocks } from '../entries/entryMarks' import { ruleAttributeNames } from '../libraryRules' /** 生成预览数据要读的那点文档信息(TextDocument 的子集)。 */ export interface DocumentSnapshot { languageId: string version: number getText: () => string /** 这份文件是项目写明的入口时,预览里名字对上的块标成入口(见 entries/entryPoints.ts) */ entryNames?: readonly string[] } /** 发给预览的文档,和这份源码里起了中文名的定义(扩展端接着找用到它们的各处)、语言插件自己认得出的名字(LocalSymbol)。 */ export interface DocumentRead { message: DocumentMessage displayNames: DisplayName[] symbols: LocalSymbol[] } /** * 当前文档 → 发给预览的 document 消息;语言不支持或解析失败时带 error,blocks 为空。库规则出错不算失败,交给 report 记下; * 规则查名字的定义时问 lookup(RuleContext.lookup),翻译插件问调用的名字定义成哪种块时问 calleeKind(SourceDocument.calleeKind)。 * 筛选属性和值的名字先取翻译插件的,没给的再取库规则的(LibraryRule.attributeNames)。 * 入口文件里名字对上的块,概念列表的第一个换成入口(markEntryBlocks)。 */ export async function documentMessage( document: DocumentSnapshot, registry: Pick, runtime: Pick, report: (message: string) => void, lookup?: RuleContext['lookup'], calleeKind?: SourceDocument['calleeKind'], ): Promise { const { languageId, version } = document const source = document.getText() const translator = registry.selectedTranslator(languageId) const message: DocumentMessage = { type: 'document', version, languageId, blocks: [], attributes: [], translators: registry.translatorsFor(languageId).map(({ id, label }) => ({ id, label })), translatorId: translator?.id ?? '', names: [], } const adapter = registry.languageFor(languageId) if (!adapter) return { message: { ...message, error: `代码阅读还不支持「${languageId}」这种语言` }, displayNames: [], symbols: [] } try { const rules = registry.rulesFor(languageId) const { blocks, displayNames, symbols } = await readBlockViews(runtime, adapter, translator, languageId, source, { rules: { rules, report, lookup }, calleeKind, commandRule: commandRuleLookup(registry.commandRulesFor(languageId), report), embedded: (embeddedId) => { const embeddedAdapter = registry.languageFor(embeddedId) return embeddedAdapter && { adapter: embeddedAdapter, translator: registry.selectedTranslator(embeddedId), commandRule: commandRuleLookup(registry.commandRulesFor(embeddedId), report), } }, }) const names = mergeAttributeNames([translator?.attributeNames, ...ruleAttributeNames(rules, translator?.displayLanguage, report)]) return { message: { ...message, blocks: markEntryBlocks(blocks, source, document.entryNames ?? []), attributes: fileAttributes(blocks, names) }, displayNames, symbols } } catch (err) { return { message: { ...message, error: err instanceof Error ? err.message : String(err) }, displayNames: [], symbols: [] } } }