( content: string, language: string )
| 293 | } |
| 294 | |
| 295 | export async function extractTreeSitterSymbols( |
| 296 | content: string, |
| 297 | language: string |
| 298 | ): Promise<TreeSitterSymbolExtraction | null> { |
| 299 | if (!supportsTreeSitter(language) || !content.trim()) { |
| 300 | return null; |
| 301 | } |
| 302 | |
| 303 | if (Buffer.byteLength(content, 'utf8') > MAX_TREE_SITTER_PARSE_BYTES) { |
| 304 | return null; |
| 305 | } |
| 306 | |
| 307 | try { |
| 308 | const parser = await getParserForLanguage(language); |
| 309 | setParseTimeout(parser); |
| 310 | |
| 311 | let tree: ReturnType<Parser['parse']>; |
| 312 | try { |
| 313 | tree = parser.parse(content); |
| 314 | } catch (error) { |
| 315 | evictParser(language, parser); |
| 316 | throw error; |
| 317 | } |
| 318 | |
| 319 | if (!tree) { |
| 320 | evictParser(language, parser); |
| 321 | return null; |
| 322 | } |
| 323 | |
| 324 | try { |
| 325 | const hasErrorValue = tree.rootNode.hasError as unknown; |
| 326 | const rootHasError = |
| 327 | typeof hasErrorValue === 'function' |
| 328 | ? Boolean((hasErrorValue as () => unknown)()) |
| 329 | : Boolean(hasErrorValue); |
| 330 | |
| 331 | if (rootHasError) { |
| 332 | return null; |
| 333 | } |
| 334 | |
| 335 | const nodes = tree.rootNode.descendantsOfType([...SYMBOL_CANDIDATE_NODE_TYPES]); |
| 336 | const seen = new Set<string>(); |
| 337 | const symbols: TreeSitterSymbol[] = []; |
| 338 | |
| 339 | for (const node of nodes) { |
| 340 | if (!node || !node.isNamed || shouldSkipNode(language, node)) { |
| 341 | continue; |
| 342 | } |
| 343 | |
| 344 | const symbol = buildSymbol(node, content); |
| 345 | if (symbol.name === 'anonymous') { |
| 346 | continue; |
| 347 | } |
| 348 | |
| 349 | const key = `${symbol.kind}:${symbol.name}:${symbol.startLine}:${symbol.endLine}`; |
| 350 | if (seen.has(key)) { |
| 351 | continue; |
| 352 | } |
no test coverage detected