( rootDir: string, cache: EmbeddingCache, queryTerms: Set<string>, queryVec: number[], symbol: IdentifierDoc, fileLines: Map<string, string[]>, limit: number, )
| 255 | } |
| 256 | |
| 257 | async function rankCallSites( |
| 258 | rootDir: string, |
| 259 | cache: EmbeddingCache, |
| 260 | queryTerms: Set<string>, |
| 261 | queryVec: number[], |
| 262 | symbol: IdentifierDoc, |
| 263 | fileLines: Map<string, string[]>, |
| 264 | limit: number, |
| 265 | ): Promise<{ sites: CallSite[]; total: number }> { |
| 266 | const callPattern = symbol.kind === "function" || symbol.kind === "method" |
| 267 | ? new RegExp(`\\b${escapeRegex(symbol.name)}\\s*\\(`) |
| 268 | : new RegExp(`\\b${escapeRegex(symbol.name)}\\b`); |
| 269 | |
| 270 | const candidates: { file: string; line: number; context: string; keywordScore: number }[] = []; |
| 271 | |
| 272 | for (const [file, lines] of fileLines) { |
| 273 | for (let i = 0; i < lines.length; i++) { |
| 274 | const raw = lines[i]; |
| 275 | if (!callPattern.test(raw)) { |
| 276 | callPattern.lastIndex = 0; |
| 277 | continue; |
| 278 | } |
| 279 | callPattern.lastIndex = 0; |
| 280 | |
| 281 | if (file === symbol.path && i + 1 === symbol.line) continue; |
| 282 | if (isDefinitionLine(raw, symbol.name)) continue; |
| 283 | |
| 284 | const context = raw.trim().slice(0, 220); |
| 285 | const keywordScore = getKeywordCoverage(queryTerms, `${file} ${context}`); |
| 286 | candidates.push({ |
| 287 | file, |
| 288 | line: i + 1, |
| 289 | context, |
| 290 | keywordScore, |
| 291 | }); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | if (candidates.length === 0) return { sites: [], total: 0 }; |
| 296 | |
| 297 | const embedBudget = Math.max(30, limit * 4); |
| 298 | const sampled = candidates |
| 299 | .slice() |
| 300 | .sort((a, b) => b.keywordScore - a.keywordScore) |
| 301 | .slice(0, Math.min(embedBudget, candidates.length)); |
| 302 | |
| 303 | const uncached: { key: string; hash: string; text: string }[] = []; |
| 304 | const keyedCandidates: { candidate: (typeof sampled)[number]; key: string; hash: string }[] = []; |
| 305 | |
| 306 | for (const candidate of sampled) { |
| 307 | const key = `${CALLSITE_CACHE_PREFIX}${candidate.file}:${candidate.line}`; |
| 308 | const text = `${candidate.file} ${candidate.context}`; |
| 309 | const hash = hashContent(text); |
| 310 | keyedCandidates.push({ candidate, key, hash }); |
| 311 | if (cache[key]?.hash !== hash) { |
| 312 | uncached.push({ key, hash, text }); |
| 313 | } |
| 314 | } |
no test coverage detected