Files
superwork/vscode/src/codeReader/features/featureLocate.ts
T

60 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 「了解一块代码」调用树上的节点按符号名找位置:用语言插件把源码解析成代码块(同预览),在有名字的块里按 `类名.方法名` 一层层找。
* 每次画栏、点节点时按眼下的代码现找,代码改过也找得到。不碰 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<BlockKind>(['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<TreeSitterRuntime, 'parse'>,
adapter: LanguageAdapter,
languageId: string,
source: string,
symbol: string,
): Promise<NamedBlock | undefined> {
const tree = await runtime.parse(adapter, languageId, source)
try {
return findRepoSymbol(namedBlocks(adapter.toBlocks(tree.rootNode, source, languageId)), symbol)
}
finally {
tree.delete()
}
}