@ttsc/graph 0.19.2 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/TtscGraphApplication.js +5 -1
- package/lib/TtscGraphApplication.js.map +1 -1
- package/lib/bin.js +15 -3
- package/lib/bin.js.map +1 -1
- package/lib/index.d.ts +13 -2
- package/lib/index.js +25 -30
- package/lib/index.js.map +1 -1
- package/lib/launcherArgs.d.ts +24 -0
- package/lib/launcherArgs.js +136 -0
- package/lib/launcherArgs.js.map +1 -0
- package/lib/model/TtscGraphMemory.d.ts +9 -5
- package/lib/model/TtscGraphMemory.js +35 -58
- package/lib/model/TtscGraphMemory.js.map +1 -1
- package/lib/model/TtscGraphNodeId.d.ts +17 -0
- package/lib/model/TtscGraphNodeId.js +69 -0
- package/lib/model/TtscGraphNodeId.js.map +1 -0
- package/lib/model/TtscGraphSession.d.ts +23 -7
- package/lib/model/TtscGraphSession.js +423 -171
- package/lib/model/TtscGraphSession.js.map +1 -1
- package/lib/model/TtscGraphSourceReader.d.ts +27 -0
- package/lib/model/TtscGraphSourceReader.js +97 -0
- package/lib/model/TtscGraphSourceReader.js.map +1 -0
- package/lib/model/loadGraph.d.ts +14 -0
- package/lib/model/loadGraph.js +256 -143
- package/lib/model/loadGraph.js.map +1 -1
- package/lib/reduce.d.ts +4 -1
- package/lib/reduce.js +112 -29
- package/lib/reduce.js.map +1 -1
- package/lib/resolveGraphBinary.d.ts +9 -3
- package/lib/resolveGraphBinary.js +9 -3
- package/lib/resolveGraphBinary.js.map +1 -1
- package/lib/server/createServer.js +22 -11
- package/lib/server/createServer.js.map +1 -1
- package/lib/server/pathPolicy.d.ts +6 -0
- package/lib/server/pathPolicy.js +10 -1
- package/lib/server/pathPolicy.js.map +1 -1
- package/lib/server/resolveHandle.js +22 -18
- package/lib/server/resolveHandle.js.map +1 -1
- package/lib/server/resultAudit.d.ts +56 -14
- package/lib/server/resultAudit.js +90 -24
- package/lib/server/resultAudit.js.map +1 -1
- package/lib/server/resultNext.d.ts +5 -0
- package/lib/server/resultNext.js.map +1 -1
- package/lib/server/runDetails.d.ts +9 -2
- package/lib/server/runDetails.js +132 -157
- package/lib/server/runDetails.js.map +1 -1
- package/lib/server/runEntrypoints.js +1 -1
- package/lib/server/runEntrypoints.js.map +1 -1
- package/lib/server/runLookup.js +1 -1
- package/lib/server/runLookup.js.map +1 -1
- package/lib/server/runTour.js +113 -30
- package/lib/server/runTour.js.map +1 -1
- package/lib/server/runTrace.d.ts +22 -0
- package/lib/server/runTrace.js +293 -63
- package/lib/server/runTrace.js.map +1 -1
- package/lib/structures/ITtscGraphApplication.d.ts +8 -7
- package/lib/structures/ITtscGraphDetails.d.ts +18 -8
- package/lib/structures/ITtscGraphDump.d.ts +13 -13
- package/lib/structures/ITtscGraphNode.d.ts +71 -0
- package/lib/structures/ITtscGraphTour.d.ts +1 -1
- package/lib/structures/ITtscGraphTrace.d.ts +1 -1
- package/lib/view.js +18 -28
- package/lib/view.js.map +1 -1
- package/package.json +7 -4
- package/src/TtscGraphApplication.ts +8 -1
- package/src/bin.ts +13 -3
- package/src/index.ts +35 -27
- package/src/launcherArgs.ts +168 -0
- package/src/model/TtscGraphMemory.ts +34 -60
- package/src/model/TtscGraphNodeId.ts +77 -0
- package/src/model/TtscGraphSession.ts +253 -53
- package/src/model/TtscGraphSourceReader.ts +117 -0
- package/src/model/loadGraph.ts +6 -1
- package/src/reduce.ts +136 -31
- package/src/resolveGraphBinary.ts +9 -3
- package/src/server/createServer.ts +12 -2
- package/src/server/pathPolicy.ts +10 -1
- package/src/server/resolveHandle.ts +21 -22
- package/src/server/resultAudit.ts +93 -23
- package/src/server/resultNext.ts +6 -0
- package/src/server/runDetails.ts +134 -165
- package/src/server/runEntrypoints.ts +1 -1
- package/src/server/runLookup.ts +1 -1
- package/src/server/runTour.ts +133 -39
- package/src/server/runTrace.ts +401 -65
- package/src/structures/ITtscGraphApplication.ts +8 -7
- package/src/structures/ITtscGraphDetails.ts +18 -8
- package/src/structures/ITtscGraphDump.ts +13 -13
- package/src/structures/ITtscGraphNode.ts +80 -0
- package/src/structures/ITtscGraphTour.ts +1 -1
- package/src/structures/ITtscGraphTrace.ts +1 -1
- package/src/view.ts +25 -23
|
@@ -26,12 +26,22 @@
|
|
|
26
26
|
* anything was checked afterwards — lost two points and put the file reads
|
|
27
27
|
* back.
|
|
28
28
|
*
|
|
29
|
-
* So the weight is carried by
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
29
|
+
* So the weight is carried by named provenance, not by a loud voice. State who
|
|
30
|
+
* resolved the fact and what this payload did with it, and the instruction that
|
|
31
|
+
* follows reads as a conclusion rather than a demand. Never mystify the result,
|
|
32
|
+
* and never insult the reader for checking it.
|
|
33
|
+
*
|
|
34
|
+
* The claim has to be one this code can keep. An earlier wording said the
|
|
35
|
+
* server assembled the result and "verified them again on the way out" — a
|
|
36
|
+
* second pass, by a second party, after assembly. No such pass exists: this
|
|
37
|
+
* layer holds no `Program` and no checker, it runs one pure projection over an
|
|
38
|
+
* in-memory `TtscGraphMemory`, and it selects a constant by request type
|
|
39
|
+
* (#818). What is true is stronger than it sounds and is what the text now
|
|
40
|
+
* says: the compiler resolved these facts when the snapshot was built, the
|
|
41
|
+
* graph holds what it resolved, and this result is a projection of exactly
|
|
42
|
+
* those facts that adds none of its own. A reader who checks that claim finds
|
|
43
|
+
* it holds; a reader who checked the old one found a promise the code could not
|
|
44
|
+
* have kept.
|
|
35
45
|
*
|
|
36
46
|
* ## Two guarantees, not one
|
|
37
47
|
*
|
|
@@ -47,14 +57,18 @@
|
|
|
47
57
|
* payload, and told it to stop where the shortlist's coverage was exactly what
|
|
48
58
|
* needed judging.
|
|
49
59
|
*
|
|
50
|
-
* So
|
|
51
|
-
* from an explicit handle or structure (`trace`, `
|
|
52
|
-
* result is what the graph holds for what you named, bounded
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
60
|
+
* So the audits split by what the reader must judge. {@link RESULT_AUDIT} covers
|
|
61
|
+
* the walks from an explicit handle or structure (`trace`, `overview`): the
|
|
62
|
+
* result is what the graph holds for what you named, bounded where `truncated`
|
|
63
|
+
* says. {@link RESULT_AUDIT_DETAILS} covers `details`, whose result is not one
|
|
64
|
+
* bounded whole but two halves — a symbol's own shape, returned complete, and
|
|
65
|
+
* its fan-out, returned as a slice with `trace` for the rest — so it names
|
|
66
|
+
* which half to trust outright and which to follow.
|
|
67
|
+
* {@link RESULT_AUDIT_SELECTION} covers the ranked shortlists (`lookup`,
|
|
68
|
+
* `entrypoints`, `tour`): it keeps the same fact-verification and adds that the
|
|
69
|
+
* _selection_ is heuristic, so whether the shortlist covers the question is the
|
|
70
|
+
* caller's to judge — and a follow-up request or a read of a cited span is a
|
|
71
|
+
* sound next step, not a failure to trust the result.
|
|
58
72
|
*
|
|
59
73
|
* Keep both narrow, and keep the stop-or-continue decision with the result's
|
|
60
74
|
* `next`, so neither contradicts a partial result that legitimately asks for
|
|
@@ -65,10 +79,10 @@
|
|
|
65
79
|
export const RESULT_AUDIT: string = `
|
|
66
80
|
AUDITED BEFORE RETURNING. READ FIRST.
|
|
67
81
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
Every fact in this \`result\` — each name, span, edge, signature, and step — was resolved by
|
|
83
|
+
the TypeScript compiler against the program for the snapshot this call synced to, and the
|
|
84
|
+
graph holds what it resolved. This \`result\` is a projection of exactly those facts and adds
|
|
85
|
+
none of its own, so for this snapshot a fact here cannot disagree with the program.
|
|
72
86
|
|
|
73
87
|
This is the structure the graph holds for the handles you named, not a shortlist matched
|
|
74
88
|
against a natural-language question. Trust every fact it gives and re-verify none: a returned
|
|
@@ -88,11 +102,11 @@ after you edit the source.
|
|
|
88
102
|
export const RESULT_AUDIT_SELECTION: string = `
|
|
89
103
|
AUDITED BEFORE RETURNING. READ FIRST.
|
|
90
104
|
|
|
91
|
-
Each fact in this \`result\` — every name, span, edge, and signature — was
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
confirm it.
|
|
105
|
+
Each fact in this \`result\` — every name, span, edge, and signature — was resolved by the
|
|
106
|
+
TypeScript compiler against the program for the snapshot this call synced to, and the graph
|
|
107
|
+
holds what it resolved. This \`result\` is a projection of exactly those facts and adds none of
|
|
108
|
+
its own, so for this snapshot a fact here cannot disagree with the program: a returned span
|
|
109
|
+
is the citation, not a cue to open the file to confirm it.
|
|
96
110
|
|
|
97
111
|
What was selected is heuristic, not exhaustive. This result was matched against your
|
|
98
112
|
natural-language question, scored and ranked, held to a few hits per file, and cut to a
|
|
@@ -104,6 +118,62 @@ span is a sound next step, not a failure to trust the result.
|
|
|
104
118
|
Follow \`next\` for where that leaves the question.
|
|
105
119
|
`.trim();
|
|
106
120
|
|
|
121
|
+
/**
|
|
122
|
+
* The audit for `details`, whose contract is not the other exact operations'.
|
|
123
|
+
*
|
|
124
|
+
* `trace` walks and marks where the walk was cut with `truncated`; `details`
|
|
125
|
+
* does not walk. It resolves a named handle, and its result splits in two. What
|
|
126
|
+
* a symbol _is_ — its members, its values, its signature — is bounded by the
|
|
127
|
+
* declaration and returned whole, so the old "trust it, do not open the file"
|
|
128
|
+
* is finally true of it rather than contradicted by a capped member list. What
|
|
129
|
+
* a symbol _reaches or is reached by_ — its calls, its type references, its
|
|
130
|
+
* implementers, its dependents — is bounded by how widely it is used, not by
|
|
131
|
+
* the symbol, so returning it whole is a `trace`/impact answer of a thousand
|
|
132
|
+
* refs in a "what is this" call. That half is a short orientation slice, and
|
|
133
|
+
* the audit has to say which half is which so the reader trusts the complete
|
|
134
|
+
* one and reaches for `trace` on the other, instead of reading the file for a
|
|
135
|
+
* member list that is already here.
|
|
136
|
+
*/
|
|
137
|
+
export const RESULT_AUDIT_DETAILS: string = `
|
|
138
|
+
AUDITED BEFORE RETURNING. READ FIRST.
|
|
139
|
+
|
|
140
|
+
Every fact in this \`result\` — each name, span, edge, signature, member, and value — was
|
|
141
|
+
resolved by the TypeScript compiler against the program for the snapshot this call synced to,
|
|
142
|
+
and the graph holds what it resolved. This \`result\` is a projection of exactly those facts
|
|
143
|
+
and adds none of its own, so for this snapshot a fact here cannot disagree with the program.
|
|
144
|
+
|
|
145
|
+
This is the structure the graph holds for the handles you named. What a symbol is — its
|
|
146
|
+
members, its values, its signature — is complete: trust it and do not open the file to read
|
|
147
|
+
what is already here. What a symbol reaches or is reached by — its calls, its type
|
|
148
|
+
references, its implementers, and under \`neighbors\` its dependents — is a short orientation
|
|
149
|
+
slice, not the whole set, because that grows with how widely a symbol is used; \`trace\`
|
|
150
|
+
follows it in full.
|
|
151
|
+
|
|
152
|
+
Follow \`next\`: answer from this result, and re-call the graph only when it says inspect, or
|
|
153
|
+
after you edit the source.
|
|
154
|
+
`.trim();
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The details audit for a result whose member list a caller cap truncated.
|
|
158
|
+
*
|
|
159
|
+
* The unconditional text tells the reader a symbol's members are complete and
|
|
160
|
+
* not to open the file for them. After `memberLimit` cuts the list that is
|
|
161
|
+
* false, and it is the one claim a caller cannot check from the result: the
|
|
162
|
+
* members that were removed left nothing behind to notice. So the claim is
|
|
163
|
+
* withdrawn for exactly that half and everything else the audit verifies is
|
|
164
|
+
* kept.
|
|
165
|
+
*/
|
|
166
|
+
export const RESULT_AUDIT_DETAILS_CAPPED: string = RESULT_AUDIT_DETAILS.replace(
|
|
167
|
+
// Matched by shape rather than by exact spelling. This file is stored with
|
|
168
|
+
// CRLF, so a needle carrying a plain newline never matches the template
|
|
169
|
+
// literal's real line breaks and the replacement would silently do nothing —
|
|
170
|
+
// the capped audit would come out identical to the uncapped one.
|
|
171
|
+
/What a symbol is[\s\S]*?what is already here\./,
|
|
172
|
+
"What a symbol is — its values and its signature — is complete. Its member list was cut to " +
|
|
173
|
+
"the `memberLimit` you asked for, so it is a slice, not the whole set: re-request with a " +
|
|
174
|
+
"larger cap for the rest.",
|
|
175
|
+
);
|
|
176
|
+
|
|
107
177
|
/** The escape branch carries no graph facts, so it claims none. */
|
|
108
178
|
export const RESULT_AUDIT_ESCAPE: string =
|
|
109
179
|
"This escape carries no graph facts to audit.";
|
package/src/server/resultNext.ts
CHANGED
|
@@ -7,6 +7,12 @@ export interface IRunnerOutput<T> {
|
|
|
7
7
|
|
|
8
8
|
/** How to use the result next. */
|
|
9
9
|
next: ITtscGraphNext;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Set when a caller cap removed members from the result, so the audit can
|
|
13
|
+
* stop claiming the symbol's members are complete.
|
|
14
|
+
*/
|
|
15
|
+
membersCapped?: boolean;
|
|
10
16
|
}
|
|
11
17
|
|
|
12
18
|
export function resultNext(
|
package/src/server/runDetails.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
1
|
import { TtscGraphMemory } from "../model/TtscGraphMemory";
|
|
5
2
|
import { ITtscGraphDecorator } from "../structures/ITtscGraphDecorator";
|
|
6
3
|
import { ITtscGraphDetails } from "../structures/ITtscGraphDetails";
|
|
@@ -15,17 +12,17 @@ import { IRunnerOutput, resultNext } from "./resultNext";
|
|
|
15
12
|
const MAX_SIGNATURE_LINES = 4;
|
|
16
13
|
// A doc summary is one sentence; the rest of the comment is the file's to keep.
|
|
17
14
|
const MAX_DOC_CHARS = 200;
|
|
18
|
-
//
|
|
15
|
+
// A symbol's fan-out — what it calls, what names it in a type, what depends on
|
|
16
|
+
// it — scales with how popular it is, not with the symbol: a central type is
|
|
17
|
+
// named in a thousand places, and returning all of them is a hundred thousand
|
|
18
|
+
// tokens of "who uses this", which is a trace/impact question, not "what is
|
|
19
|
+
// this". So fan-out is a small default slice; identity (members, literals) is
|
|
20
|
+
// not, because a class's members and a union's values are the symbol itself and
|
|
21
|
+
// are bounded by the declaration.
|
|
19
22
|
const DEFAULT_NEIGHBORS = 2;
|
|
20
23
|
const MAX_NEIGHBORS = 3;
|
|
21
|
-
// A container outline can be long; default to a scannable first page.
|
|
22
|
-
const DEFAULT_MEMBERS = 6;
|
|
23
|
-
const MAX_MEMBERS = 8;
|
|
24
|
-
// Direct dependency groups are orientation slices, not full fan-out dumps.
|
|
25
24
|
const DEFAULT_DEPENDENCIES = 2;
|
|
26
25
|
const MAX_DEPENDENCIES = 4;
|
|
27
|
-
// Object literal outlines are navigation aids, not source excerpts.
|
|
28
|
-
const MAX_OBJECT_MEMBER_LINES = 300;
|
|
29
26
|
// Structural relationships are navigation, not the dependency picture details is for.
|
|
30
27
|
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports", "imports"]);
|
|
31
28
|
// Kinds whose value is their member outline, not implementation text.
|
|
@@ -47,17 +44,25 @@ export function runDetails(
|
|
|
47
44
|
graph: TtscGraphMemory,
|
|
48
45
|
props: ITtscGraphDetails.IRequest,
|
|
49
46
|
): IRunnerOutput<ITtscGraphDetails> {
|
|
50
|
-
|
|
47
|
+
// Identity is the whole answer. The caller named this handle to learn what it
|
|
48
|
+
// is, and a class's members or a union's values are the symbol itself — cut
|
|
49
|
+
// them and the model reads the file for the rest, the read this index exists
|
|
50
|
+
// to remove. So `memberLimit` and `literals` default to unlimited. Fan-out
|
|
51
|
+
// does not: what names or uses a symbol is bounded by its popularity, not by
|
|
52
|
+
// it, so those stay a small slice with `trace` for the rest.
|
|
53
|
+
const memberLimit = limitOf(props.memberLimit);
|
|
54
|
+
// True once any handle's member list is cut by the cap above. It travels with
|
|
55
|
+
// the result so the audit can withdraw the completeness claim for exactly
|
|
56
|
+
// that half — a caller cannot notice the cut from the result itself.
|
|
57
|
+
let membersCapped = false;
|
|
58
|
+
const neighborLimit = capOf(
|
|
51
59
|
props.neighborLimit,
|
|
52
60
|
DEFAULT_NEIGHBORS,
|
|
53
|
-
1,
|
|
54
61
|
MAX_NEIGHBORS,
|
|
55
62
|
);
|
|
56
|
-
const
|
|
57
|
-
const dependencyLimit = bound(
|
|
63
|
+
const dependencyLimit = capOf(
|
|
58
64
|
props.dependencyLimit,
|
|
59
65
|
DEFAULT_DEPENDENCIES,
|
|
60
|
-
1,
|
|
61
66
|
MAX_DEPENDENCIES,
|
|
62
67
|
);
|
|
63
68
|
const wantNeighbors = props.neighbors === true;
|
|
@@ -97,11 +102,10 @@ export function runDetails(
|
|
|
97
102
|
file: node.file,
|
|
98
103
|
};
|
|
99
104
|
if (node.evidence?.startLine) detail.line = node.evidence.startLine;
|
|
100
|
-
const sig = signatureOf(graph
|
|
105
|
+
const sig = signatureOf(graph, node);
|
|
101
106
|
if (sig !== undefined) detail.signature = sig;
|
|
102
|
-
const doc = docOf(graph
|
|
107
|
+
const doc = docOf(graph, node);
|
|
103
108
|
if (doc !== undefined) detail.doc = doc;
|
|
104
|
-
const signatureLiterals = literalSummaries(sig);
|
|
105
109
|
const decorators = decoratorsOf(node);
|
|
106
110
|
if (decorators !== undefined) detail.decorators = decorators;
|
|
107
111
|
const implementation = evidenceCoordinatesOf(node.implementation);
|
|
@@ -139,19 +143,34 @@ export function runDetails(
|
|
|
139
143
|
);
|
|
140
144
|
if (implementedBy.length > 0) detail.implementedBy = implementedBy;
|
|
141
145
|
if (CONTAINER_KINDS.has(node.kind)) {
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
// Read one past the cap so the cut is observable. Without it a full list
|
|
147
|
+
// and a truncated one are the same value, and the audit went on claiming
|
|
148
|
+
// the members were whole.
|
|
149
|
+
const list = members(graph, node, memberLimit + 1);
|
|
150
|
+
if (list.length > memberLimit) membersCapped = true;
|
|
151
|
+
const shown = list.slice(0, memberLimit);
|
|
152
|
+
if (shown.length > 0) detail.members = shown;
|
|
144
153
|
}
|
|
145
|
-
if (node.kind === "variable"
|
|
146
|
-
const list = objectLiteralMembers(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
if (node.kind === "variable") {
|
|
155
|
+
const list = objectLiteralMembers(node, memberLimit + 1);
|
|
156
|
+
if (list.length > memberLimit) membersCapped = true;
|
|
157
|
+
const shown = list.slice(0, memberLimit);
|
|
158
|
+
if (shown.length > 0) detail.members = shown;
|
|
159
|
+
}
|
|
160
|
+
// An enum's members ride on its own node rather than on `contains` edges,
|
|
161
|
+
// because they are not nodes: the outline above finds nothing for an enum
|
|
162
|
+
// and always did. Its signature stops at the `{`, so without this the one
|
|
163
|
+
// kind whose entire content is its member list answered with none of it.
|
|
164
|
+
// Uncapped like every other identity list — the members are the enum.
|
|
165
|
+
if (node.kind === "enum") {
|
|
166
|
+
const list = enumMembers(node, memberLimit + 1);
|
|
167
|
+
if (list.length > memberLimit) membersCapped = true;
|
|
168
|
+
const shown = list.slice(0, memberLimit);
|
|
169
|
+
if (shown.length > 0) detail.members = shown;
|
|
170
|
+
}
|
|
171
|
+
if (node.literals !== undefined && node.literals.length > 0) {
|
|
172
|
+
detail.literals = node.literals;
|
|
152
173
|
}
|
|
153
|
-
if (signatureLiterals.length > 0)
|
|
154
|
-
detail.literals = signatureLiterals.slice(0, 6);
|
|
155
174
|
if (wantNeighbors) {
|
|
156
175
|
detail.dependsOn = refs(
|
|
157
176
|
graph,
|
|
@@ -171,6 +190,7 @@ export function runDetails(
|
|
|
171
190
|
nodes.push(detail);
|
|
172
191
|
}
|
|
173
192
|
return {
|
|
193
|
+
...(membersCapped ? { membersCapped: true } : {}),
|
|
174
194
|
result: {
|
|
175
195
|
type: "details",
|
|
176
196
|
nodes,
|
|
@@ -212,7 +232,7 @@ function members(
|
|
|
212
232
|
kind: member.kind,
|
|
213
233
|
};
|
|
214
234
|
if (member.evidence?.startLine) m.line = member.evidence.startLine;
|
|
215
|
-
const sig = signatureOf(graph
|
|
235
|
+
const sig = signatureOf(graph, member);
|
|
216
236
|
if (sig !== undefined) m.signature = sig;
|
|
217
237
|
const decorators = decoratorsOf(member);
|
|
218
238
|
if (decorators !== undefined) m.decorators = decorators;
|
|
@@ -222,84 +242,38 @@ function members(
|
|
|
222
242
|
return out;
|
|
223
243
|
}
|
|
224
244
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
245
|
+
/**
|
|
246
|
+
* An enum's members, owner-qualified so the name reads the way the code writes
|
|
247
|
+
* it, with the value each carries as its signature.
|
|
248
|
+
*
|
|
249
|
+
* The name is why this exists. `literals` answers what values the enum admits,
|
|
250
|
+
* but a caller writes `Colors.Red` and never `"red"`, so an enum the graph
|
|
251
|
+
* already held sent a caller that had named it to the file for the one fact it
|
|
252
|
+
* came for (#738).
|
|
253
|
+
*/
|
|
254
|
+
function enumMembers(
|
|
255
|
+
node: ITtscGraphNode,
|
|
228
256
|
limit: number,
|
|
229
257
|
): ITtscGraphDetails.IMember[] {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
let depth = 0;
|
|
238
|
-
let entered = false;
|
|
239
|
-
for (let i = start; i <= end; i++) {
|
|
240
|
-
const raw = lines[i] ?? "";
|
|
241
|
-
const text = stripStrings(raw);
|
|
242
|
-
const before = depth;
|
|
243
|
-
if (entered && before === 1) {
|
|
244
|
-
const member = objectMemberOf(raw, i + 1);
|
|
245
|
-
if (member !== undefined) {
|
|
246
|
-
members.push(member);
|
|
247
|
-
if (members.length >= limit) break;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
for (const char of text) {
|
|
251
|
-
if (char === "{") {
|
|
252
|
-
depth++;
|
|
253
|
-
entered = true;
|
|
254
|
-
} else if (char === "}") {
|
|
255
|
-
depth = Math.max(0, depth - 1);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return members;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function objectMemberOf(
|
|
263
|
-
line: string,
|
|
264
|
-
lineNumber: number,
|
|
265
|
-
): ITtscGraphDetails.IMember | undefined {
|
|
266
|
-
const text = line.trim();
|
|
267
|
-
if (
|
|
268
|
-
text === "" ||
|
|
269
|
-
text.startsWith("//") ||
|
|
270
|
-
text.startsWith("/*") ||
|
|
271
|
-
text.startsWith("*")
|
|
272
|
-
) {
|
|
273
|
-
return undefined;
|
|
274
|
-
}
|
|
275
|
-
const property = /^(['"]?)([A-Za-z_$][\w$-]*)\1\s*\??\s*:/.exec(text);
|
|
276
|
-
if (property !== null) {
|
|
277
|
-
return {
|
|
278
|
-
name: property[2]!,
|
|
279
|
-
kind: "property",
|
|
280
|
-
line: lineNumber,
|
|
281
|
-
signature: signatureLine(text),
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
const method =
|
|
285
|
-
/^(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_$][\w$-]*)\s*\(/.exec(text);
|
|
286
|
-
if (method !== null) {
|
|
287
|
-
return {
|
|
288
|
-
name: method[1]!,
|
|
289
|
-
kind: "method",
|
|
290
|
-
line: lineNumber,
|
|
291
|
-
signature: signatureLine(text),
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
return undefined;
|
|
258
|
+
return (node.enumMembers ?? []).slice(0, limit).map((member) => ({
|
|
259
|
+
name: `${node.qualifiedName ?? node.name}.${member.name}`,
|
|
260
|
+
kind: "property",
|
|
261
|
+
...(member.value !== undefined
|
|
262
|
+
? { signature: `${member.name} = ${member.value}` }
|
|
263
|
+
: {}),
|
|
264
|
+
}));
|
|
295
265
|
}
|
|
296
266
|
|
|
297
|
-
function
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
267
|
+
function objectLiteralMembers(
|
|
268
|
+
node: ITtscGraphNode,
|
|
269
|
+
limit: number,
|
|
270
|
+
): ITtscGraphDetails.IMember[] {
|
|
271
|
+
return (node.objectMembers ?? []).slice(0, limit).map((member) => ({
|
|
272
|
+
name: member.name,
|
|
273
|
+
kind: member.kind,
|
|
274
|
+
...(member.line !== undefined ? { line: member.line } : {}),
|
|
275
|
+
...(member.signature !== undefined ? { signature: member.signature } : {}),
|
|
276
|
+
}));
|
|
303
277
|
}
|
|
304
278
|
|
|
305
279
|
/** Map dependency edges to references on their far endpoint, dropping structure. */
|
|
@@ -328,13 +302,11 @@ function refs(
|
|
|
328
302
|
if (evidence !== undefined) ref.evidence = evidence;
|
|
329
303
|
ranked.push({ ref, rank: refRank(ref, edge) });
|
|
330
304
|
}
|
|
305
|
+
// Ranked so a caller that does throttle (the tour) keeps the strongest refs,
|
|
306
|
+
// not the ones nearest the top of a file. Uncapped, `limit` is Infinity and
|
|
307
|
+
// the sort is just a stable order.
|
|
331
308
|
ranked.sort((a, b) => a.rank - b.rank);
|
|
332
|
-
|
|
333
|
-
for (const item of ranked) {
|
|
334
|
-
out.push(item.ref);
|
|
335
|
-
if (out.length >= limit) break;
|
|
336
|
-
}
|
|
337
|
-
return out;
|
|
309
|
+
return ranked.map((item) => item.ref).slice(0, limit);
|
|
338
310
|
}
|
|
339
311
|
|
|
340
312
|
const executionKinds = new Set([
|
|
@@ -375,17 +347,7 @@ function dependencyRefs(
|
|
|
375
347
|
rank: refRank(ref, edge),
|
|
376
348
|
});
|
|
377
349
|
}
|
|
378
|
-
ranked
|
|
379
|
-
const out: ITtscGraphDetails.IReference[] = [];
|
|
380
|
-
const seen = new Set<string>();
|
|
381
|
-
for (const item of ranked) {
|
|
382
|
-
const key = `${item.ref.relation}:${item.ref.id}`;
|
|
383
|
-
if (seen.has(key)) continue;
|
|
384
|
-
seen.add(key);
|
|
385
|
-
out.push(item.ref);
|
|
386
|
-
if (out.length >= limit) break;
|
|
387
|
-
}
|
|
388
|
-
return out;
|
|
350
|
+
return rankedRefs(ranked, limit);
|
|
389
351
|
}
|
|
390
352
|
|
|
391
353
|
function incomingDependencyRefs(
|
|
@@ -416,6 +378,14 @@ function incomingDependencyRefs(
|
|
|
416
378
|
rank: refRank(ref, edge),
|
|
417
379
|
});
|
|
418
380
|
}
|
|
381
|
+
return rankedRefs(ranked, limit);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** Sort by rank, drop duplicate (relation, id) pairs, and cut to `limit`. */
|
|
385
|
+
function rankedRefs(
|
|
386
|
+
ranked: Array<{ ref: ITtscGraphDetails.IReference; rank: number }>,
|
|
387
|
+
limit: number,
|
|
388
|
+
): ITtscGraphDetails.IReference[] {
|
|
419
389
|
ranked.sort((a, b) => a.rank - b.rank);
|
|
420
390
|
const out: ITtscGraphDetails.IReference[] = [];
|
|
421
391
|
const seen = new Set<string>();
|
|
@@ -429,38 +399,30 @@ function incomingDependencyRefs(
|
|
|
429
399
|
return out;
|
|
430
400
|
}
|
|
431
401
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
function cleanLiteral(value: string | undefined): string | undefined {
|
|
444
|
-
const text = value?.replace(/\s+/g, " ").trim();
|
|
445
|
-
if (
|
|
446
|
-
text === undefined ||
|
|
447
|
-
text === "" ||
|
|
448
|
-
text.length > 40 ||
|
|
449
|
-
/^[{}()[\],.:;]+$/.test(text)
|
|
450
|
-
) {
|
|
451
|
-
return undefined;
|
|
452
|
-
}
|
|
453
|
-
return text;
|
|
402
|
+
/**
|
|
403
|
+
* An identity list's cap: none by default, honored when a caller passes one.
|
|
404
|
+
* details answers a named handle's own shape in full — its members, its values
|
|
405
|
+
* — so the default is unlimited; the tour passes an explicit number to embed a
|
|
406
|
+
* compact slice of its own.
|
|
407
|
+
*/
|
|
408
|
+
function limitOf(value: number | undefined): number {
|
|
409
|
+
return value === undefined || !Number.isFinite(value)
|
|
410
|
+
? Infinity
|
|
411
|
+
: Math.max(1, Math.floor(value));
|
|
454
412
|
}
|
|
455
413
|
|
|
456
|
-
|
|
414
|
+
/**
|
|
415
|
+
* A fan-out list's cap: a small default, clamped to a ceiling. What names or
|
|
416
|
+
* uses a symbol grows with its popularity, not with the symbol, so the whole
|
|
417
|
+
* list is a trace/impact answer and details returns an orientation slice.
|
|
418
|
+
*/
|
|
419
|
+
function capOf(
|
|
457
420
|
value: number | undefined,
|
|
458
421
|
fallback: number,
|
|
459
|
-
min: number,
|
|
460
422
|
max: number,
|
|
461
423
|
): number {
|
|
462
424
|
const n = value === undefined || !Number.isFinite(value) ? fallback : value;
|
|
463
|
-
return Math.max(
|
|
425
|
+
return Math.max(1, Math.min(max, Math.floor(n)));
|
|
464
426
|
}
|
|
465
427
|
|
|
466
428
|
/**
|
|
@@ -546,16 +508,6 @@ function evidenceCoordinatesOf(
|
|
|
546
508
|
};
|
|
547
509
|
}
|
|
548
510
|
|
|
549
|
-
/** Read a file's lines once, or undefined when it cannot be read. */
|
|
550
|
-
function fileLines(project: string, file: string): string[] | undefined {
|
|
551
|
-
if (file === "") return undefined;
|
|
552
|
-
try {
|
|
553
|
-
return fs.readFileSync(path.join(project, file), "utf8").split(/\r?\n/);
|
|
554
|
-
} catch {
|
|
555
|
-
return undefined;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
511
|
/**
|
|
560
512
|
* What the declaration says it is: the first sentence of the doc comment
|
|
561
513
|
* written above it.
|
|
@@ -569,12 +521,12 @@ function fileLines(project: string, file: string): string[] | undefined {
|
|
|
569
521
|
* symbol with what it is for is doing an index's job.
|
|
570
522
|
*/
|
|
571
523
|
export function docOf(
|
|
572
|
-
|
|
524
|
+
graph: TtscGraphMemory,
|
|
573
525
|
node: ITtscGraphNode,
|
|
574
526
|
): string | undefined {
|
|
575
527
|
const evidence = node.evidence;
|
|
576
528
|
const lines =
|
|
577
|
-
evidence === undefined ? undefined :
|
|
529
|
+
evidence === undefined ? undefined : graph.source.lines(evidence.file);
|
|
578
530
|
if (lines === undefined || evidence === undefined) return undefined;
|
|
579
531
|
let index = evidence.startLine - 2;
|
|
580
532
|
while (index >= 0 && (lines[index] ?? "").trim() === "") index--;
|
|
@@ -611,22 +563,39 @@ export function docOf(
|
|
|
611
563
|
* The declaration signature: the head of the declaration up to and including
|
|
612
564
|
* the line that opens its body (`{`), or the single declaration line when there
|
|
613
565
|
* is no brace, capped so a wrapped signature cannot run away.
|
|
566
|
+
*
|
|
567
|
+
* It never runs past the declaration's own span. The stop used to be the brace,
|
|
568
|
+
* the trailing semicolon, or the line cap, and a declaration ending in none of
|
|
569
|
+
* them read its neighbors instead: an enum member ends in a comma, so `VIEW`
|
|
570
|
+
* came back as itself plus the two members after it and the closing brace. The
|
|
571
|
+
* span is the fact that says where the declaration ends, and it was already on
|
|
572
|
+
* the node.
|
|
614
573
|
*/
|
|
615
574
|
export function signatureOf(
|
|
616
|
-
|
|
575
|
+
graph: TtscGraphMemory,
|
|
617
576
|
node: ITtscGraphNode,
|
|
618
577
|
): string | undefined {
|
|
578
|
+
// The producer cuts the head where the compiler says the body opens, so when
|
|
579
|
+
// it supplied one there is nothing left to infer. The scan below only runs
|
|
580
|
+
// where it could not: it reads whole physical lines and stops at the first one
|
|
581
|
+
// holding a `{`, which leaks implementation text when a declaration shares its
|
|
582
|
+
// line with its body and stops early when the head itself contains a brace.
|
|
583
|
+
if (node.signature !== undefined && node.signature !== "") {
|
|
584
|
+
const capped = node.signature.split("\n").slice(0, MAX_SIGNATURE_LINES);
|
|
585
|
+
const head = capped.join("\n").trim();
|
|
586
|
+
if (head !== "") return head;
|
|
587
|
+
}
|
|
619
588
|
const evidence = node.evidence;
|
|
620
589
|
const lines =
|
|
621
|
-
evidence === undefined ? undefined :
|
|
590
|
+
evidence === undefined ? undefined : graph.source.lines(evidence.file);
|
|
622
591
|
if (lines === undefined || evidence === undefined) return undefined;
|
|
623
592
|
const start = Math.max(0, evidence.startLine - 1);
|
|
593
|
+
const last =
|
|
594
|
+
evidence.endLine === undefined
|
|
595
|
+
? lines.length - 1
|
|
596
|
+
: Math.min(lines.length - 1, evidence.endLine - 1);
|
|
624
597
|
const out: string[] = [];
|
|
625
|
-
for (
|
|
626
|
-
let i = start;
|
|
627
|
-
i < lines.length && out.length < MAX_SIGNATURE_LINES;
|
|
628
|
-
i++
|
|
629
|
-
) {
|
|
598
|
+
for (let i = start; i <= last && out.length < MAX_SIGNATURE_LINES; i++) {
|
|
630
599
|
const line = lines[i];
|
|
631
600
|
if (line === undefined) break;
|
|
632
601
|
out.push(line);
|
|
@@ -118,7 +118,7 @@ function nodeOf(
|
|
|
118
118
|
};
|
|
119
119
|
if (node.evidence?.startLine !== undefined)
|
|
120
120
|
out.line = node.evidence.startLine;
|
|
121
|
-
const signature = signatureOf(graph
|
|
121
|
+
const signature = signatureOf(graph, node);
|
|
122
122
|
if (signature !== undefined) out.signature = signature;
|
|
123
123
|
const decorators = decoratorsOf(node);
|
|
124
124
|
if (decorators !== undefined) out.decorators = decorators;
|
package/src/server/runLookup.ts
CHANGED
|
@@ -88,7 +88,7 @@ export function runLookup(
|
|
|
88
88
|
for (const hit of hits) {
|
|
89
89
|
const node = graph.node(hit.id);
|
|
90
90
|
if (node === undefined) continue;
|
|
91
|
-
const sig = signatureOf(graph
|
|
91
|
+
const sig = signatureOf(graph, node);
|
|
92
92
|
if (sig !== undefined) hit.signature = sig;
|
|
93
93
|
}
|
|
94
94
|
return {
|