( rootPath: string, symbol: string, limit = 10 )
| 102 | } |
| 103 | |
| 104 | export async function findSymbolReferences( |
| 105 | rootPath: string, |
| 106 | symbol: string, |
| 107 | limit = 10 |
| 108 | ): Promise<SymbolReferencesResult> { |
| 109 | const normalizedSymbol = symbol.trim(); |
| 110 | const normalizedLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 10; |
| 111 | |
| 112 | if (!normalizedSymbol) { |
| 113 | return { |
| 114 | status: 'error', |
| 115 | message: 'Symbol is required' |
| 116 | }; |
| 117 | } |
| 118 | |
| 119 | const indexPath = path.join(rootPath, CODEBASE_CONTEXT_DIRNAME, KEYWORD_INDEX_FILENAME); |
| 120 | |
| 121 | let chunksRaw: unknown; |
| 122 | try { |
| 123 | const content = await fs.readFile(indexPath, 'utf-8'); |
| 124 | chunksRaw = JSON.parse(content); |
| 125 | } catch (error) { |
| 126 | throw new IndexCorruptedError( |
| 127 | `Keyword index missing or unreadable (rebuild required): ${ |
| 128 | error instanceof Error ? error.message : String(error) |
| 129 | }` |
| 130 | ); |
| 131 | } |
| 132 | |
| 133 | if (Array.isArray(chunksRaw)) { |
| 134 | throw new IndexCorruptedError( |
| 135 | 'Legacy keyword index format detected (missing header). Rebuild required.' |
| 136 | ); |
| 137 | } |
| 138 | |
| 139 | const chunks = |
| 140 | chunksRaw !== null && |
| 141 | typeof chunksRaw === 'object' && |
| 142 | 'chunks' in chunksRaw && |
| 143 | Array.isArray(chunksRaw.chunks) |
| 144 | ? (chunksRaw.chunks as unknown[]) |
| 145 | : null; |
| 146 | |
| 147 | if (!chunks) { |
| 148 | throw new IndexCorruptedError('Keyword index corrupted: expected { header, chunks }'); |
| 149 | } |
| 150 | |
| 151 | const usages: SymbolUsage[] = []; |
| 152 | let usageCount = 0; |
| 153 | |
| 154 | const escapedSymbol = escapeRegex(normalizedSymbol); |
| 155 | const prefilter = new RegExp(`\\b${escapedSymbol}\\b`); |
| 156 | const matcher = new RegExp(`\\b${escapedSymbol}\\b`, 'g'); |
| 157 | |
| 158 | // Prefilter candidate files from the keyword index. We do not trust chunk contents for |
| 159 | // exact reference counting when Tree-sitter is available; chunks only guide which files to scan. |
| 160 | const chunksByFile = new Map< |
| 161 | string, |
no test coverage detected