(
parserResult: { parser: any; language: any },
lang: string,
source: string,
)
| 75 | } |
| 76 | |
| 77 | function extractWithTreeSitter( |
| 78 | parserResult: { parser: any; language: any }, |
| 79 | lang: string, |
| 80 | source: string, |
| 81 | ): ExtractedSymbol[] { |
| 82 | const tree = parserResult.parser.parse(source); |
| 83 | const root = tree.rootNode; |
| 84 | const nodeMap = SYMBOL_NODE_TYPES[lang]; |
| 85 | if (!nodeMap) return []; |
| 86 | |
| 87 | const symbols: ExtractedSymbol[] = []; |
| 88 | // Build a flat reverse lookup: node type → kind name |
| 89 | const typeToKind = new Map<string, string>(); |
| 90 | for (const [kind, types] of Object.entries(nodeMap)) { |
| 91 | for (const t of types) typeToKind.set(t, kind); |
| 92 | } |
| 93 | |
| 94 | function getName(node: any): string | null { |
| 95 | const nameNode = node.childForFieldName('name'); |
| 96 | if (nameNode) return nameNode.text; |
| 97 | for (let i = 0; i < node.childCount; i++) { |
| 98 | const c = node.child(i); |
| 99 | if (c && (c.type === 'identifier' || c.type === 'property_identifier' || c.type === 'type_identifier')) { |
| 100 | return c.text; |
| 101 | } |
| 102 | } |
| 103 | return null; |
| 104 | } |
| 105 | |
| 106 | function getEnclosingNamedSymbol(node: any): string | null { |
| 107 | let cur = node.parent; |
| 108 | while (cur) { |
| 109 | const kind = typeToKind.get(cur.type); |
| 110 | if (kind) { |
| 111 | const name = getName(cur); |
| 112 | if (name) return name; |
| 113 | } |
| 114 | cur = cur.parent; |
| 115 | } |
| 116 | return null; |
| 117 | } |
| 118 | |
| 119 | function visit(node: any) { |
| 120 | let kind = typeToKind.get(node.type); |
| 121 | // Rust maps `function_item` to BOTH `function` and `method`, so the flat |
| 122 | // type→kind map can't tell them apart (last write wins). Disambiguate by |
| 123 | // context: a `fn` enclosed in an impl/trait block is a method, otherwise a |
| 124 | // free function. |
| 125 | if (lang === 'rust' && node.type === 'function_item') { |
| 126 | let cur = node.parent; |
| 127 | let insideImplOrTrait = false; |
| 128 | while (cur) { |
| 129 | if (cur.type === 'impl_item' || cur.type === 'trait_item') { insideImplOrTrait = true; break; } |
| 130 | cur = cur.parent; |
| 131 | } |
| 132 | kind = insideImplOrTrait ? 'method' : 'function'; |
| 133 | } |
| 134 | if (kind) { |
no test coverage detected