import type { BlockKind, CodeBlock, LanguageAdapter } from './contract' import { readFileSync } from 'node:fs' import * as path from 'node:path' import { TreeSitterRuntime } from './runtime' /** 单元测试直接用依赖包里的 wasm,不走打包产物。 */ export const WASM_DIR = path.resolve(__dirname, '../../node_modules/@vscode/tree-sitter-wasm/wasm') export const runtime = new TreeSitterRuntime(WASM_DIR) export function readFixture(name: string): string { return readFileSync(path.join(__dirname, 'fixtures', name), 'utf8') } /** 解析 source,把代码块树交给 use;语法树随后释放。 */ export async function withBlocks(adapter: LanguageAdapter, languageId: string, source: string, use: (blocks: CodeBlock[]) => T): Promise { const tree = await runtime.parse(adapter, languageId, source) try { return use(adapter.toBlocks(tree.rootNode, source)) } finally { tree.delete() } } /** 和原型 CODE_TREE 同样的写法:at 是 1 起的行号(单行,或 [起, 止])。 */ export interface Outline { at: number | [number, number], kind: BlockKind, kids?: Outline[] } export function outline(blocks: CodeBlock[]): Outline[] { return blocks.map((block) => { const from = block.header.startRow + 1 const to = block.header.endRow + 1 const item: Outline = { at: from === to ? from : [from, to], kind: block.kind } if (block.children.length > 0) item.kids = outline(block.children) return item }) } export function walk(blocks: CodeBlock[], visit: (block: CodeBlock) => void): void { for (const block of blocks) { visit(block) walk(block.children, visit) } }