79 lines
3.8 KiB
TypeScript
79 lines
3.8 KiB
TypeScript
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<CodeReaderRegistry, 'languageFor' | 'translatorsFor' | 'selectedTranslator' | 'rulesFor' | 'commandRulesFor'>,
|
||
runtime: Pick<TreeSitterRuntime, 'parse'>,
|
||
report: (message: string) => void,
|
||
lookup?: RuleContext['lookup'],
|
||
calleeKind?: SourceDocument['calleeKind'],
|
||
): Promise<DocumentRead> {
|
||
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: [] }
|
||
}
|
||
}
|