(body: string, language: string, fileStartLine: number)
| 267 | * line numbers are absolute file lines. |
| 268 | */ |
| 269 | export function scanDynamicDispatch(body: string, language: string, fileStartLine: number): BoundaryMatch[] { |
| 270 | const original = body.length > MAX_BODY_CHARS ? body.slice(0, MAX_BODY_CHARS) : body; |
| 271 | const lang = commentLang(language); |
| 272 | const stripped = blankStringContents(lang ? stripCommentsForRegex(original, lang) : original); |
| 273 | |
| 274 | const out: BoundaryMatch[] = []; |
| 275 | const seen = new Map<string, BoundaryMatch>(); // form+key → first match (counts extras) |
| 276 | |
| 277 | if (language === 'python') scanPythonGetattr(stripped, original, fileStartLine, out, seen); |
| 278 | |
| 279 | for (const spec of FORMS) { |
| 280 | if (out.length >= MAX_MATCHES_PER_BODY) break; |
| 281 | if (spec.langs && !spec.langs.has(language)) continue; |
| 282 | spec.re.lastIndex = 0; |
| 283 | let m: RegExpExecArray | null; |
| 284 | while ((m = spec.re.exec(stripped)) !== null) { |
| 285 | let sliceEnd = m.index + m[0].length; |
| 286 | if (spec.keyWindow) { |
| 287 | const windowEnd = Math.min(original.length, sliceEnd + spec.keyWindow); |
| 288 | const nl = original.indexOf('\n', sliceEnd); |
| 289 | sliceEnd = nl !== -1 && nl < windowEnd ? nl : windowEnd; |
| 290 | } |
| 291 | const origSlice = original.slice(m.index, sliceEnd); |
| 292 | const derived = spec.keyFrom?.(origSlice); |
| 293 | const dedupeKey = `${spec.form}|${derived?.key ?? ''}`; |
| 294 | const prior = seen.get(dedupeKey); |
| 295 | if (prior) { |
| 296 | prior.moreSites = (prior.moreSites ?? 0) + 1; |
| 297 | continue; |
| 298 | } |
| 299 | const line = fileStartLine + countNewlines(original, m.index); |
| 300 | const match: BoundaryMatch = { |
| 301 | form: spec.form, |
| 302 | label: spec.label, |
| 303 | snippet: snippetAround(original, m.index), |
| 304 | line, |
| 305 | ...(derived ?? {}), |
| 306 | }; |
| 307 | seen.set(dedupeKey, match); |
| 308 | out.push(match); |
| 309 | if (out.length >= MAX_MATCHES_PER_BODY) return out; |
| 310 | } |
| 311 | } |
| 312 | return out; |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * Python getattr dispatch — handled in code, not the FORMS table, because real |
no test coverage detected