/** * 「了解一块代码」调用树上的节点按符号名找位置:用语言插件把源码解析成代码块(同预览),在有名字的块里按 `类名.方法名` 一层层找。 * 每次画栏、点节点时按眼下的代码现找,代码改过也找得到。不碰 VS Code;语言没有插件时用 VS Code 的文档符号(见 featureViews.ts)。 */ import type { BlockKind, CodeBlock, LanguageAdapter, Node, SourceRange } from '../contract' import type { TreeSitterRuntime } from '../runtime' import { ownName } from '../blockView' import { findRepoSymbol } from '../editor/repoPath' /** 块树上有名字的一块:名字、种类、名字在源码里的范围;子孙里有名字的挂在下面。 */ export interface NamedBlock { name: string kind: BlockKind range: SourceRange children: NamedBlock[] } /** 语言插件不给名字、按语法树里的声明找名字的块:类型(TypeScript 的 interface、type)、类型里的成员、别的声明语句(Rust 的 type)。 */ const DECLARATIONS = new Set(['type', 'field', 'statement']) /** * 块的名字:函数、类、组件、变量(见 ownName),Rust 的 impl、trait(`impl Order` 里的方法写成 `Order.pay`); * DECLARATIONS 那几种按语法树里声明的 name 字段找,export 语句先进到它导出的声明里。 */ function nameOf(block: CodeBlock): Node | undefined { if (block.kind === 'impl' || block.kind === 'trait') return block.facts.name if (!DECLARATIONS.has(block.kind)) return ownName(block) const declaration = block.node.childForFieldName('declaration') ?? block.node return declaration.childForFieldName('name') ?? undefined } /** 块树里有名字的那些;没名字的块(条件、调用语句)跳过,它里面有名字的往上提一层。 */ export function namedBlocks(blocks: readonly CodeBlock[]): NamedBlock[] { return blocks.flatMap((block): NamedBlock[] => { const children = namedBlocks(block.children) const name = nameOf(block) return name ? [{ name: name.text, kind: block.kind, range: { startIndex: name.startIndex, endIndex: name.endIndex }, children }] : children }) } /** 用语言插件解析 source,按 symbol(`login`、`AuthService.login`)找那一块;找不到没有。语法树用完即释放。 */ export async function locateInSource( runtime: Pick, adapter: LanguageAdapter, languageId: string, source: string, symbol: string, ): Promise { const tree = await runtime.parse(adapter, languageId, source) try { return findRepoSymbol(namedBlocks(adapter.toBlocks(tree.rootNode, source, languageId)), symbol) } finally { tree.delete() } }