mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-18 22:02:49 +08:00
fix(ui): count module-level initializer calls as the file's top-level code
Walking a declaration's initializer attributes its calls to the declared name, so `const service = new Service()` no longer leaves a calls edge on the FILE node — and the viewer's entry points ranking and the file screen's top-level count, which read only the file node's edges, went blank on exactly the files that run the most. Count a module-level variable or constant's calls with the file in both places.
This commit is contained in:
+1
-1
@@ -204,7 +204,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
- Python module-level assignments now contribute the calls their right-hand side makes — `app = FastAPI()`, `ENGINE = create_engine(url)`, `handler = lambda: run()`, a registry dict or list of handlers. Previously everything a module wires up at import time was missing from the graph, so the objects it builds looked unreferenced. Re-index with `codegraph index -f` after upgrading to pick up the new edges.
|
||||
- Rust `const` and `static` initializers now contribute their calls: `static REGISTRY: Lazy<Cfg> = Lazy::new(|| build())`, `const LEN: usize = compute_len()`. Previously anything a lazily-built singleton or a computed constant called was missing from the graph entirely. Re-index with `codegraph index -f` after upgrading to pick up the new edges.
|
||||
- Scala `val`/`var` definitions now contribute the calls their initializer makes — `val handler = () => process(msg)`, `val client = buildClient()`, `lazy val engine = start()`. Previously everything on the right-hand side was dropped, which on a val-heavy codebase (SpinalHDL hardware descriptions, Akka wiring) is most of the wiring: a 32-file SpinalHDL project gained 812 references it had been missing. Re-index with `codegraph index -f` after upgrading to pick up the new edges.
|
||||
- TypeScript and JavaScript module-level declarations now name themselves as the caller of whatever their initializer runs. `const cfg = loadConfig()` recorded the *file* as loadConfig's caller, which is no use for callers or impact; it now records `cfg`. And an object literal that wasn't exported — `const handlers = { onSave: () => persist() }` — was skipped entirely, so nothing inside it reached the graph at all. Re-index with `codegraph index -f` after upgrading to pick up the new edges.
|
||||
- TypeScript and JavaScript module-level declarations now name themselves as the caller of whatever their initializer runs. `const cfg = loadConfig()` recorded the *file* as loadConfig's caller, which is no use for callers or impact; it now records `cfg`, and the viewer's entry points and file screens still count that call as the file's top-level code. And an object literal that wasn't exported — `const handlers = { onSave: () => persist() }` — was skipped entirely, so nothing inside it reached the graph at all. Re-index with `codegraph index -f` after upgrading to pick up the new edges.
|
||||
- `codegraph_explore` again lists a dynamic-dispatch link when the same two symbols are also joined by an ordinary call, instead of dropping it from the summary.
|
||||
- Java fields initialized with a lambda or an anonymous class — `private final Runnable r = () -> doWork();`, `new LocationListener() { … }`, `Parcelable.Creator` — now contribute call edges, and the anonymous class and its overrides become real symbols instead of being invisible. Previously everything inside a field initializer was dropped, so a method reached only from one looked like it had no callers. Re-index with `codegraph index -f` after upgrading to pick up the new edges.
|
||||
- Kotlin `init { }` blocks and destructuring declarations no longer swallow their code: `init { val cfg = load() }` and `val (a, b) = makePair()` contributed no call edge at all, and now attribute to the enclosing class or file.
|
||||
|
||||
+25
-6
@@ -2184,6 +2184,12 @@ export class QueryBuilder {
|
||||
* build script does its work on the way down the file. `instantiates` counts
|
||||
* the same way — `new Server(...)` at module scope is the same act.
|
||||
*
|
||||
* A call made while initializing a module-level `variable` / `constant` —
|
||||
* `const service = new Service()`, `app = FastAPI()` — is attributed to the
|
||||
* declared name (#693), not to the file, so the file's own edges alone would
|
||||
* miss most of what a real entry point runs. Those names are the file's
|
||||
* top-level code too, so `tops` counts them alongside the file node.
|
||||
*
|
||||
* Ranking multiplies the two things an entry point does: it runs (calls), and
|
||||
* it wires the project together (distinct other files its symbols reach). One
|
||||
* alone is misleading — a registration table makes hundreds of module-level
|
||||
@@ -2196,12 +2202,25 @@ export class QueryBuilder {
|
||||
if (limit <= 0) return [];
|
||||
return this.db
|
||||
.prepare(
|
||||
`WITH runs AS (
|
||||
SELECT e.source AS id, COUNT(*) AS calls
|
||||
FROM edges e
|
||||
JOIN nodes n ON n.id = e.source
|
||||
WHERE n.kind = 'file' AND e.kind IN ('calls', 'instantiates')
|
||||
GROUP BY e.source
|
||||
`WITH tops AS (
|
||||
SELECT n.id AS file_id, n.id AS src
|
||||
FROM nodes n
|
||||
WHERE n.kind = 'file'
|
||||
UNION ALL
|
||||
SELECT c.source AS file_id, c.target AS src
|
||||
FROM edges c
|
||||
JOIN nodes f ON f.id = c.source
|
||||
JOIN nodes v ON v.id = c.target
|
||||
WHERE c.kind = 'contains'
|
||||
AND f.kind = 'file'
|
||||
AND v.kind IN ('variable', 'constant')
|
||||
),
|
||||
runs AS (
|
||||
SELECT t.file_id AS id, COUNT(*) AS calls
|
||||
FROM tops t
|
||||
JOIN edges e ON e.source = t.src
|
||||
WHERE e.kind IN ('calls', 'instantiates')
|
||||
GROUP BY t.file_id
|
||||
),
|
||||
cand AS (
|
||||
SELECT r.id AS id, n.file_path AS fp, r.calls AS calls
|
||||
|
||||
@@ -66,6 +66,7 @@ export function buildFile(cg: CodeGraph, projectRoot: string, requested: string)
|
||||
const nodes = cg.getNodesInFile(storedPath);
|
||||
const nodeIds = nodes.map((n) => n.id);
|
||||
const inThisFile = new Set(nodeIds);
|
||||
const nodeKindById = new Map(nodes.map((n) => [n.id, n.kind]));
|
||||
const fileNode = nodes.find((n) => n.kind === 'file') ?? null;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -106,8 +107,20 @@ export function buildFile(cg: CodeGraph, projectRoot: string, requested: string)
|
||||
// the same signal `/api/entrypoints` ranks on. It is worth a line on this
|
||||
// screen because the outline cannot show it: top-level code belongs to no
|
||||
// symbol, so the only way to read it is to open the file node itself.
|
||||
// A call made while initializing a module-level variable or constant is
|
||||
// attributed to that name (#693), so those names are top-level code too and
|
||||
// are counted with the file — the same set `getTopCallingFiles` ranks on.
|
||||
const moduleLevelValueIds = fileNode
|
||||
? cg
|
||||
.getOutgoingEdgesFrom([fileNode.id], ['contains'])
|
||||
.map((e) => e.target)
|
||||
.filter((id) => {
|
||||
const kind = nodeKindById.get(id);
|
||||
return kind === 'variable' || kind === 'constant';
|
||||
})
|
||||
: [];
|
||||
const topLevelEdges = fileNode
|
||||
? cg.getOutgoingEdgesFrom([fileNode.id], ['calls', 'instantiates'])
|
||||
? cg.getOutgoingEdgesFrom([fileNode.id, ...moduleLevelValueIds], ['calls', 'instantiates'])
|
||||
: [];
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user