* Check if a node matches a symbol query. * * Accepts simple names (`run`) and three flavors of qualifier: * - dotted `Session.request` (TS/JS/Python) * - colon-pair `stage_apply::run` (Rust, C++, Ruby) * - slash `configurator/stage_apply` (path-ish) *
(node: Node, symbol: string)
| 4637 | * Python — `stage_apply::run` matches a `run` in `stage_apply.rs`) |
| 4638 | */ |
| 4639 | private matchesSymbol(node: Node, symbol: string): boolean { |
| 4640 | // Simple name match |
| 4641 | if (node.name === symbol) return true; |
| 4642 | // File basename match (e.g., "product-card" matches "product-card.liquid") |
| 4643 | if (node.kind === 'file' && node.name.replace(/\.[^.]+$/, '') === symbol) return true; |
| 4644 | |
| 4645 | // Qualified-name lookups: split on any supported separator. `\w` keeps |
| 4646 | // identifier chars (incl. `_`) intact; everything else is treated as |
| 4647 | // a separator we tolerate. |
| 4648 | if (!/[.\/]|::/.test(symbol)) return false; |
| 4649 | const parts = symbol.split(/::|[./]/).filter((p) => p.length > 0); |
| 4650 | if (parts.length < 2) return false; |
| 4651 | |
| 4652 | const lastPart = parts[parts.length - 1]!; |
| 4653 | if (node.name !== lastPart) return false; |
| 4654 | |
| 4655 | // Stage 1: qualified-name suffix match. The extractor joins the |
| 4656 | // semantic hierarchy with `::`, so `Session.request` and |
| 4657 | // `Session::request` both become `Session::request` here. |
| 4658 | const colonSuffix = parts.join('::'); |
| 4659 | if (node.qualifiedName.includes(colonSuffix)) return true; |
| 4660 | |
| 4661 | // Stage 2: file-path containment. Rust modules and Python packages |
| 4662 | // are not in `qualifiedName` — they're encoded in the file path. So |
| 4663 | // `stage_apply::run` matches a `run` in any file whose path |
| 4664 | // contains a `stage_apply` segment (with or without an extension). |
| 4665 | // |
| 4666 | // Filter out Rust path prefixes that have no file-system equivalent. |
| 4667 | const containerHints = parts.slice(0, -1).filter((p) => !RUST_PATH_PREFIXES.has(p)); |
| 4668 | if (containerHints.length === 0) return false; |
| 4669 | |
| 4670 | const segments = node.filePath.split('/').filter((s) => s.length > 0); |
| 4671 | return containerHints.every((hint) => |
| 4672 | segments.some((seg) => seg === hint || seg.replace(/\.[^.]+$/, '') === hint) |
| 4673 | ); |
| 4674 | } |
| 4675 | |
| 4676 | /** |
| 4677 | * Find ALL definitions matching a name, ranked, so codegraph_node can return |
no test coverage detected