Summary
In the Delphi DFM/FMX extractor, every component node's endLine is pinned to its declaration line and is never back-patched when the matching block end is found. So a component that spans many source lines is recorded with endLine == startLine. Downstream, the context builder slices startLine..endLine to produce the "full body" an agent sees (the codegraph_node sufficiency guarantee), so every DFM component body is truncated to a single line — the agent never sees the component's properties or its On* event bindings. Verified end-to-end against a real indexed project.
Root cause
// src/extraction/dfm-extractor.ts:112-137
const objMatch = line.match(objectPattern);
if (objMatch) {
const [, , name, typeName] = objMatch;
const nodeId = generateNodeId(this.filePath, 'component', name!, lineNum);
this.nodes.push({
...
startLine: lineNum,
endLine: lineNum, // <- set at declaration, never updated
...
});
stack.push(nodeId);
continue;
}
...
// src/extraction/dfm-extractor.ts:153-156
if (endPattern.test(line)) {
if (stack.length > 1) stack.pop(); // pops the frame but never revisits its endLine
}
The endPattern branch only pops the id stack; it never sets endLine on the node being closed.
Repro (executed end-to-end against a real indexed project)
Fixture Form1.dfm:
object Form1: TForm1
Left = 0
object Button1: TButton
Left = 10
Caption = 'Click'
OnClick = Button1Click
end
end
After CodeGraph.init + indexAll, the persisted component nodes are:
Form1 lines 1-1 (actual block: 1-8)
Button1 lines 3-3 (actual block: 3-7)
Both are truncated to their declaration line. A consumer building Button1's body from startLine..endLine sees only object Button1: TButton and never the Left, Caption, or the OnClick = Button1Click event binding that lives inside the same node's own block.
Suggested direction
Track, alongside the id stack, the index into this.nodes for each pushed component; on the endPattern match (before popping), set this.nodes[idx].endLine = lineNum for the frame being closed.
Environment
main @ 2d72891, package 1.4.1, Node 22.23.1 (Linux). Reproduces on a clean npm ci && npm run build.
Summary
In the Delphi DFM/FMX extractor, every
componentnode'sendLineis pinned to its declaration line and is never back-patched when the matching blockendis found. So a component that spans many source lines is recorded withendLine == startLine. Downstream, the context builder slicesstartLine..endLineto produce the "full body" an agent sees (thecodegraph_nodesufficiency guarantee), so every DFM component body is truncated to a single line — the agent never sees the component's properties or itsOn*event bindings. Verified end-to-end against a real indexed project.Root cause
The
endPatternbranch only pops the id stack; it never setsendLineon the node being closed.Repro (executed end-to-end against a real indexed project)
Fixture
Form1.dfm:After
CodeGraph.init+indexAll, the persisted component nodes are:Both are truncated to their declaration line. A consumer building
Button1's body fromstartLine..endLinesees onlyobject Button1: TButtonand never theLeft,Caption, or theOnClick = Button1Clickevent binding that lives inside the same node's own block.Suggested direction
Track, alongside the id
stack, the index intothis.nodesfor each pushed component; on theendPatternmatch (before popping), setthis.nodes[idx].endLine = lineNumfor the frame being closed.Environment
main@2d72891, package1.4.1, Node 22.23.1 (Linux). Reproduces on a cleannpm ci && npm run build.