feat(steps): rows read in the code's order; a hop written inside another call says so
- branch-guards callSiteInTree: a call's span and the call it is written inside the arguments of (`within`), stopping at a function or block boundary - steps.ts: each step records the hop that first reached it (position, span, enclosing call — the fold's first hop out of the root, inherited down the fold); a row is ordered by that position, a hop inside another site's arguments before that site, and `WireStep.order` carries it; links carry `within` - map-model: an `order` option — the row's initial order, sweeps over parents only, tie-broken by it; the Map and Screens tabs pass none and are unchanged - viewer: rows laid out by `order`; `inside res.json(…)` in the panel rows and the tooltip - tests: servers fixture (a token signed inside the reply's arguments: `within`, and the row `create · queue · mail · jwt.sign · 201`), model row order; spec §3.13, CHANGELOG Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01REFyW9hmNrxhwN5wxRoAkC
This commit is contained in:
co-authored by
Claude Fable 5
parent
46e3e7aaa0
commit
783f3954ec
@@ -435,6 +435,30 @@ export interface CallSiteText {
|
||||
argList: string[];
|
||||
/** A status code written as an object property in the arguments (`{ status: 201 }`), which the abbreviation to keys would hide. */
|
||||
status?: number;
|
||||
/** Where the call starts and ends in the source (1-based lines) — the span another site may be written inside. */
|
||||
span?: { start: { line: number; column: number }; end: { line: number; column: number } };
|
||||
/**
|
||||
* The call this one is written inside the arguments of — `res.json` for the
|
||||
* `generateToken(…)` in `res.json({ token: generateToken(…) })` — normalised
|
||||
* like `callee`. Absent at the top of a statement, and never reaching out of
|
||||
* the function or block the call is in.
|
||||
*/
|
||||
within?: string;
|
||||
}
|
||||
|
||||
/** A node the climb to an enclosing call must not cross: the call is then a statement of its own inside a callback. */
|
||||
const CALL_BOUNDARY = /function|lambda|closure|block|statement|body|declaration/;
|
||||
|
||||
/** The nearest call whose ARGUMENTS contain `call`, as its callee chain; null when there is none this side of a function or block. */
|
||||
function enclosingCallText(call: SyntaxNode): string | null {
|
||||
for (let node = call.parent, up = 0; node && up < 12; node = node.parent, up++) {
|
||||
if (CALL_BOUNDARY.test(node.type)) return null;
|
||||
if (!CALL_TYPES.has(node.type)) continue;
|
||||
const container = argumentsOf(node);
|
||||
if (container && container.startIndex <= call.startIndex && call.endIndex <= container.endIndex) return calleeChainText(node, container);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const STATUS_KEY = /^(?:status|statusCode|status_code|code)$/;
|
||||
@@ -502,7 +526,19 @@ export function callSiteInTree(root: SyntaxNode, source: string, line: number, c
|
||||
}
|
||||
const text = parts.join(', ');
|
||||
if (status === null) status = statusSetBefore(call, callee);
|
||||
return { callee, args: text.length > MAX_ARGS_TEXT ? `${text.slice(0, MAX_ARGS_TEXT - 1)}…` : text, argList: parts, ...(status !== null ? { status } : {}) };
|
||||
const span = {
|
||||
start: { line: call.startPosition.row + 1, column: call.startPosition.column },
|
||||
end: { line: call.endPosition.row + 1, column: call.endPosition.column },
|
||||
};
|
||||
const within = enclosingCallText(call);
|
||||
return {
|
||||
callee,
|
||||
args: text.length > MAX_ARGS_TEXT ? `${text.slice(0, MAX_ARGS_TEXT - 1)}…` : text,
|
||||
argList: parts,
|
||||
span,
|
||||
...(within ? { within } : {}),
|
||||
...(status !== null ? { status } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
const BODY_REPLY = /^(?:res|response|reply|rep|ctx|c|context)\.(?:json|jsonp|send|render|sendFile|download|end|text|html|body)$/;
|
||||
|
||||
Reference in New Issue
Block a user