MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / closureCollectionEdges

Function closureCollectionEdges

src/resolution/callback-synthesizer.ts:252–326  ·  view source on GitHub ↗

* Closure-collection dispatch: dispatcher iterates a closure-collection property * invoking each element; registrar appends a closure to the same-named property. * Emits dispatcher → registrar so a flow reaches the registration site (where the * appended closure's body — and its callers — live).

(queries: QueryBuilder, ctx: ResolutionContext, onYield: MaybeYield)

Source from the content-addressed store, hash-verified

250 * name shared across unrelated classes can't fan out into noise.
251 */
252async function closureCollectionEdges(queries: QueryBuilder, ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]> {
253 const dispatchers = new Map<string, Array<{ node: Node; line: number }>>(); // field → dispatcher methods + forEach line
254 const registrars = new Map<string, Array<{ node: Node; line: number }>>(); // field → registrar methods + append line
255
256 const addReg = (field: string | undefined, node: Node, absLine: number) => {
257 if (!field || /^\d+$/.test(field)) return; // `$0.append` mis-captures the `0`; the write-RE owns that field
258 const arr = registrars.get(field) ?? [];
259 if (!arr.some((r) => r.node.id === node.id)) arr.push({ node, line: absLine });
260 registrars.set(field, arr);
261 };
262
263 // Slices EVERY Swift/Kotlin method/function's source (no cheap name-gate), so
264 // on a repo with a huge file this is the heaviest synthesis pass — yield
265 // mid-scan (and mid-match-loop below: a single generated function dense with
266 // matches must not starve the watchdog either) so it can't wedge the #850
267 // watchdog on its own (#1091, #1235).
268 let scanned = 0;
269 let matchTick = 0;
270 for (const m of methodAndFunctionNodes(queries)) {
271 if ((++scanned & 127) === 0) await onYield();
272 if (!CC_LANGUAGES.has(m.language)) continue;
273 const content = ctx.readFile(m.filePath);
274 const src = content && sliceLines(content, m.startLine, m.endLine);
275 if (!src) continue;
276 const hasForEach = src.includes('.forEach');
277 const hasAppend = src.includes('.append(') || src.includes('.add(') || src.includes('.push(') || src.includes('.insert(');
278 if (!hasForEach && !hasAppend) continue;
279 const lineAt = makeLineAt(src, m.startLine ?? 1);
280
281 if (hasForEach) {
282 CC_DISPATCH_RE.lastIndex = 0;
283 let d: RegExpExecArray | null;
284 while ((d = CC_DISPATCH_RE.exec(src))) {
285 if ((++matchTick & 255) === 0) await onYield();
286 const arr = dispatchers.get(d[1]!) ?? [];
287 if (!arr.some((n) => n.node.id === m.id)) arr.push({ node: m, line: lineAt(d.index) });
288 dispatchers.set(d[1]!, arr);
289 }
290 }
291 if (hasAppend) {
292 CC_APPEND_WRITE_RE.lastIndex = 0;
293 let w: RegExpExecArray | null;
294 while ((w = CC_APPEND_WRITE_RE.exec(src))) {
295 if ((++matchTick & 255) === 0) await onYield();
296 addReg(w[2] || w[1], m, lineAt(w.index)); // nested `$0.streams` else the `.write` receiver
297 }
298 CC_APPEND_DIRECT_RE.lastIndex = 0;
299 let a: RegExpExecArray | null;
300 while ((a = CC_APPEND_DIRECT_RE.exec(src))) {
301 if ((++matchTick & 255) === 0) await onYield();
302 addReg(a[1], m, lineAt(a.index));
303 }
304 }
305 }
306
307 const edges: Edge[] = [];
308 const seen = new Set<string>();
309 for (const [field, disps] of dispatchers) {

Callers 1

Calls 10

methodAndFunctionNodesFunction · 0.85
sliceLinesFunction · 0.85
makeLineAtFunction · 0.85
hasMethod · 0.80
readFileMethod · 0.80
addRegFunction · 0.70
execMethod · 0.65
getMethod · 0.65
lineAtFunction · 0.50
setMethod · 0.45

Tested by

no test coverage detected