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

Function springEventEdges

src/resolution/callback-synthesizer.ts:2677–2758  ·  view source on GitHub ↗
(ctx: ResolutionContext, onYield: MaybeYield)

Source from the content-addressed store, hash-verified

2675}
2676
2677async function springEventEdges(ctx: ResolutionContext, onYield: MaybeYield): Promise<Edge[]> {
2678 let scannedFiles = 0;
2679 // Pass 1 — event-type → listener methods, scanning only event-relevant files.
2680 // This is the ONLY full read sweep: publisher files are recorded here so
2681 // pass 2 re-reads just those instead of every .java file again (#1212 —
2682 // the double full-repo read was one of the tail's longest unyielded spans).
2683 const listeners = new Map<string, Node[]>();
2684 const publisherFiles: string[] = [];
2685 for (const file of ctx.getAllFiles()) {
2686 if ((++scannedFiles & 15) === 0) await onYield();
2687 if (!SPRING_JAVA_EXT.test(file)) continue;
2688 const content = ctx.readFile(file);
2689 if (!content) continue;
2690 if (content.includes('.publishEvent(')) publisherFiles.push(file);
2691 const hasAnno = content.includes('@EventListener') || content.includes('@TransactionalEventListener');
2692 const hasAppListener = SPRING_APP_LISTENER_RE.test(content);
2693 if (!hasAnno && !hasAppListener) continue;
2694 const lines = content.split('\n');
2695 for (const node of ctx.getNodesInFile(file)) {
2696 if (node.kind !== 'method') continue;
2697 // Collect this method's own leading annotation block (consecutive `@`-lines from startLine).
2698 const annoLines: string[] = [];
2699 for (let i = node.startLine - 1; i < lines.length && i < node.startLine + 7; i++) {
2700 const t = (lines[i] ?? '').trim();
2701 if (!t.startsWith('@')) break; // reached the declaration → stop (no bleed into next method)
2702 annoLines.push(t);
2703 }
2704 const head = annoLines.join('\n');
2705 const annotated = hasAnno && SPRING_LISTENER_ANNO_RE.test(head);
2706 const isAppListener = hasAppListener && node.name === 'onApplicationEvent';
2707 if (!annotated && !isAppListener) continue;
2708 let type = springFirstParamType(node.signature);
2709 if (!type && annotated) {
2710 const m = SPRING_ANNO_TYPE_RE.exec(head);
2711 if (m) type = m[1]!;
2712 }
2713 if (!type) continue;
2714 let arr = listeners.get(type);
2715 if (!arr) { arr = []; listeners.set(type, arr); }
2716 arr.push(node);
2717 }
2718 }
2719 if (!listeners.size) return [];
2720
2721 // Pass 2 — link each publishEvent(new XEvent(...)) site → every listener of
2722 // XEvent. Only the publisher files recorded in pass 1 are (re-)read.
2723 const edges: Edge[] = [];
2724 const seen = new Set<string>();
2725 for (const file of publisherFiles) {
2726 if ((++scannedFiles & 15) === 0) await onYield();
2727 const content = ctx.readFile(file);
2728 if (!content || !content.includes('.publishEvent(')) continue;
2729 const safe = stripCommentsForRegex(content, 'java');
2730 const nodesInFile = ctx.getNodesInFile(file);
2731 SPRING_PUBLISH_RE.lastIndex = 0;
2732 let m: RegExpExecArray | null;
2733 let added = 0;
2734 while ((m = SPRING_PUBLISH_RE.exec(safe)) && added < SPRING_FANOUT_CAP) {

Callers 1

Calls 11

stripCommentsForRegexFunction · 0.90
springFirstParamTypeFunction · 0.85
enclosingFnFunction · 0.85
readFileMethod · 0.80
hasMethod · 0.80
getAllFilesMethod · 0.65
getNodesInFileMethod · 0.65
execMethod · 0.65
getMethod · 0.65
joinMethod · 0.45
setMethod · 0.45

Tested by

no test coverage detected