@mrclrchtr/supi-code-intelligence 6.4.0 → 7.0.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/README.md +164 -82
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/README.md +35 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +43 -35
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +83 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +108 -92
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +44 -82
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +3 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +9 -68
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +45 -58
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +230 -331
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +201 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +129 -70
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-typescript.ts +395 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +8 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +685 -183
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +60 -60
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-request-enrollment.ts +75 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-barrier.ts +443 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-errors.ts +30 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-file.ts +15 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +290 -37
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +72 -16
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +30 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +608 -45
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +19 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/package.json +1 -1
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +1 -2
- package/src/analysis/health/file-scope.ts +4 -1
- package/src/analysis/health/server-status.ts +1 -1
- package/src/analysis/relations/callees.ts +17 -8
- package/src/analysis/relations/implementations.ts +1 -2
- package/src/analysis/relations/references.ts +8 -4
- package/src/analysis/relations/types.ts +6 -30
- package/src/analysis/target/anchored.ts +9 -1
- package/src/analysis/target/symbol.ts +29 -11
- package/src/analysis/target/types.ts +14 -6
- package/src/api.ts +2 -0
- package/src/session/graph/collect.ts +18 -3
- package/src/session/health-refresh.ts +12 -1
- package/src/session/health-types.ts +18 -6
- package/src/session/health-workflow.ts +21 -19
- package/src/session/orientation-types.ts +5 -6
- package/src/session/orientation-workflow.ts +10 -2
- package/src/session/target-workflow.ts +9 -6
- package/src/substrate/lsp/source-tracking.ts +11 -6
- package/src/tool/code_find/guidance.ts +2 -2
- package/src/tool/code_find/spec.ts +5 -4
- package/src/tool/code_find/tui.ts +7 -1
- package/src/tool/code_graph/details.ts +32 -0
- package/src/tool/code_graph/display.ts +54 -0
- package/src/tool/code_graph/format.ts +60 -0
- package/src/tool/code_graph/guidance.ts +1 -1
- package/src/tool/code_graph/markdown.ts +76 -198
- package/src/tool/code_graph/read-next.ts +65 -0
- package/src/tool/code_graph/result.ts +118 -83
- package/src/tool/code_graph/tui.ts +216 -54
- package/src/tool/code_health/guidance.ts +2 -4
- package/src/tool/code_health/markdown.ts +75 -58
- package/src/tool/code_health/refresh-outcome.ts +41 -1
- package/src/tool/code_health/refresh-status.ts +114 -48
- package/src/tool/code_health/scope.ts +78 -0
- package/src/tool/code_health/spec.ts +1 -1
- package/src/tool/code_health/tui.ts +25 -38
- package/src/tool/code_inspect/tui.ts +7 -1
- package/src/tool/code_orientation/result.ts +31 -8
- package/src/tool/code_orientation/tui.ts +26 -5
- package/src/tool/code_refactor_apply/guidance.ts +1 -1
- package/src/tool/code_refactor_apply/tui.ts +7 -3
- package/src/tool/code_refactor_plan/guidance.ts +1 -1
- package/src/tool/code_refactor_plan/tui.ts +7 -1
- package/src/tool/code_resolve/guidance.ts +2 -4
- package/src/tool/code_resolve/result.ts +13 -10
- package/src/tool/code_resolve/tui.ts +18 -1
- package/src/tool/infra/truncate.ts +39 -13
- package/src/tool/result/candidate-row.ts +27 -0
- package/src/tool/result/errors.ts +21 -6
- package/src/tool/result/types.ts +7 -3
- package/src/tool/schemas.ts +5 -0
- package/src/types/execution.ts +2 -0
- package/src/types/index.ts +2 -0
- package/src/ui/tui/candidate-legacy-body.ts +69 -0
- package/src/ui/tui/candidate-selection.ts +366 -0
- package/src/ui/tui/common.ts +26 -31
- package/src/ui/tui/execution-error.ts +92 -0
|
@@ -1,9 +1,209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CodeRequestControl,
|
|
3
|
+
throwIfCodeRequestInterrupted,
|
|
4
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
5
|
+
import type { DocumentDiagnosticReport } from "../config/types.ts";
|
|
6
|
+
import { raceRequestControl } from "../session/readiness.ts";
|
|
7
|
+
|
|
8
|
+
/** Evidence source used by an explicit diagnostic request. */
|
|
9
|
+
export type DiagnosticRequestSource = "pull" | "typescript";
|
|
10
|
+
|
|
11
|
+
/** Error used when local document or client invalidation supersedes a request. */
|
|
12
|
+
export class DiagnosticRequestInvalidatedError extends Error {
|
|
13
|
+
constructor(message = "Diagnostic request was invalidated.") {
|
|
14
|
+
super(message);
|
|
15
|
+
// biome-ignore lint/security/noSecrets: This is a stable error class name.
|
|
16
|
+
this.name = "DiagnosticRequestInvalidatedError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Test whether a request stopped because its local evidence generation ended. */
|
|
21
|
+
export function isDiagnosticRequestInvalidated(error: unknown): boolean {
|
|
22
|
+
return error instanceof DiagnosticRequestInvalidatedError;
|
|
23
|
+
}
|
|
24
|
+
|
|
1
25
|
/** Sanitized ownership and transport control for one explicit diagnostic pull. */
|
|
2
26
|
export interface DiagnosticPullRequest {
|
|
3
27
|
readonly uri: string;
|
|
4
28
|
readonly previousResultId: string | undefined;
|
|
5
29
|
readonly timeoutMs: number;
|
|
30
|
+
/** Adapter cancellation stops adapter work, not the owned transport. */
|
|
6
31
|
readonly signal?: AbortSignal;
|
|
7
|
-
readonly deadline?: number;
|
|
8
32
|
readonly operationId?: string;
|
|
9
33
|
}
|
|
34
|
+
|
|
35
|
+
/** Result returned by one request adapter. */
|
|
36
|
+
export interface DiagnosticRequestResult {
|
|
37
|
+
readonly source: DiagnosticRequestSource;
|
|
38
|
+
readonly report: DocumentDiagnosticReport;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The result and transport lifetime of one adapter request are separate.
|
|
43
|
+
* `result` may stop at a local deadline while `settled` waits for the
|
|
44
|
+
* underlying protocol request. This prevents a replacement request from
|
|
45
|
+
* entering the same server route too early.
|
|
46
|
+
*/
|
|
47
|
+
export interface DiagnosticRequestExecution<T> {
|
|
48
|
+
readonly result: Promise<T>;
|
|
49
|
+
readonly settled: Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Internal seam for one explicit, file-scoped diagnostic request. */
|
|
53
|
+
export interface DiagnosticRequestAdapter {
|
|
54
|
+
/** Whether this adapter can collect the requested URI right now. */
|
|
55
|
+
supports(uri: string): boolean;
|
|
56
|
+
/** Return the source that will be used for a URI, when known. */
|
|
57
|
+
sourceFor(uri: string): DiagnosticRequestSource | undefined;
|
|
58
|
+
/** Start one request without transferring caller cancellation to transport. */
|
|
59
|
+
collect(request: DiagnosticPullRequest): DiagnosticRequestExecution<DiagnosticRequestResult>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Combine adapters in priority order. The first applicable adapter wins. */
|
|
63
|
+
export function createPriorityDiagnosticRequestAdapter(
|
|
64
|
+
adapters: readonly DiagnosticRequestAdapter[],
|
|
65
|
+
): DiagnosticRequestAdapter {
|
|
66
|
+
const find = (uri: string) => adapters.find((adapter) => adapter.supports(uri));
|
|
67
|
+
return {
|
|
68
|
+
supports: (uri) => find(uri) !== undefined,
|
|
69
|
+
sourceFor: (uri) => find(uri)?.sourceFor(uri),
|
|
70
|
+
collect: (request) => {
|
|
71
|
+
const adapter = find(request.uri);
|
|
72
|
+
if (!adapter) {
|
|
73
|
+
const error = new Error("No diagnostic request adapter applies to this document.");
|
|
74
|
+
return { result: Promise.reject(error), settled: Promise.resolve() };
|
|
75
|
+
}
|
|
76
|
+
return adapter.collect(request);
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ScheduledDiagnosticRequest<T> {
|
|
82
|
+
readonly key: string;
|
|
83
|
+
readonly start: () => DiagnosticRequestExecution<T>;
|
|
84
|
+
readonly result: Promise<T>;
|
|
85
|
+
readonly resolve: (value: T | PromiseLike<T>) => void;
|
|
86
|
+
readonly reject: (reason?: unknown) => void;
|
|
87
|
+
readonly settled: Promise<void>;
|
|
88
|
+
readonly settle: () => void;
|
|
89
|
+
consumers: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Share duplicate file requests and serialize requests for one client route.
|
|
94
|
+
* The queue is deliberately small: callers must not create an unbounded
|
|
95
|
+
* server-side tsserver request backlog after a timeout.
|
|
96
|
+
*/
|
|
97
|
+
export class DiagnosticRequestScheduler {
|
|
98
|
+
static readonly MAX_PENDING_REQUESTS = 32;
|
|
99
|
+
|
|
100
|
+
readonly #jobs = new Map<string, ScheduledDiagnosticRequest<unknown>>();
|
|
101
|
+
readonly #pending: Array<ScheduledDiagnosticRequest<unknown>> = [];
|
|
102
|
+
#active: ScheduledDiagnosticRequest<unknown> | undefined;
|
|
103
|
+
|
|
104
|
+
/** Start or join one request, while keeping the underlying job shared. */
|
|
105
|
+
run<T>(
|
|
106
|
+
key: string,
|
|
107
|
+
start: () => DiagnosticRequestExecution<T>,
|
|
108
|
+
control?: CodeRequestControl,
|
|
109
|
+
): Promise<T> {
|
|
110
|
+
throwIfCodeRequestInterrupted(control);
|
|
111
|
+
let job = this.#jobs.get(key) as ScheduledDiagnosticRequest<T> | undefined;
|
|
112
|
+
if (!job) {
|
|
113
|
+
if (this.#pending.length >= DiagnosticRequestScheduler.MAX_PENDING_REQUESTS) {
|
|
114
|
+
return Promise.reject(new Error("Diagnostic request queue is full."));
|
|
115
|
+
}
|
|
116
|
+
let resolve!: (value: T | PromiseLike<T>) => void;
|
|
117
|
+
let reject!: (reason?: unknown) => void;
|
|
118
|
+
const result = new Promise<T>((jobResolve, jobReject) => {
|
|
119
|
+
resolve = jobResolve;
|
|
120
|
+
reject = jobReject;
|
|
121
|
+
});
|
|
122
|
+
let settle!: () => void;
|
|
123
|
+
const settled = new Promise<void>((resolveSettled) => {
|
|
124
|
+
settle = resolveSettled;
|
|
125
|
+
});
|
|
126
|
+
job = {
|
|
127
|
+
key,
|
|
128
|
+
start,
|
|
129
|
+
result,
|
|
130
|
+
resolve,
|
|
131
|
+
reject,
|
|
132
|
+
settled,
|
|
133
|
+
settle,
|
|
134
|
+
consumers: 0,
|
|
135
|
+
};
|
|
136
|
+
this.#jobs.set(key, job as ScheduledDiagnosticRequest<unknown>);
|
|
137
|
+
this.#pending.push(job as ScheduledDiagnosticRequest<unknown>);
|
|
138
|
+
this.#pump();
|
|
139
|
+
}
|
|
140
|
+
const currentJob = job;
|
|
141
|
+
currentJob.consumers++;
|
|
142
|
+
const waiting = raceRequestControl(currentJob.result, control);
|
|
143
|
+
void waiting
|
|
144
|
+
.finally(() =>
|
|
145
|
+
this.#releaseConsumer(currentJob as unknown as ScheduledDiagnosticRequest<unknown>),
|
|
146
|
+
)
|
|
147
|
+
.catch(() => {});
|
|
148
|
+
return waiting;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Drop requests that have not reached the transport. Active work is kept. */
|
|
152
|
+
clearPending(reason = new DiagnosticRequestInvalidatedError()): void {
|
|
153
|
+
this.clearPendingWhere(() => true, reason);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Drop matching queued requests without disturbing other documents. */
|
|
157
|
+
clearPendingWhere(
|
|
158
|
+
predicate: (key: string) => boolean,
|
|
159
|
+
reason = new DiagnosticRequestInvalidatedError(),
|
|
160
|
+
): void {
|
|
161
|
+
const pending = this.#pending.filter((job) => predicate(job.key));
|
|
162
|
+
for (const job of pending) {
|
|
163
|
+
const index = this.#pending.indexOf(job);
|
|
164
|
+
if (index >= 0) this.#pending.splice(index, 1);
|
|
165
|
+
this.#jobs.delete(job.key);
|
|
166
|
+
job.reject(reason);
|
|
167
|
+
job.settle();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
#releaseConsumer(job: ScheduledDiagnosticRequest<unknown>): void {
|
|
172
|
+
job.consumers = Math.max(0, job.consumers - 1);
|
|
173
|
+
if (job.consumers > 0 || this.#active === job) return;
|
|
174
|
+
const pendingIndex = this.#pending.indexOf(job);
|
|
175
|
+
if (pendingIndex < 0) return;
|
|
176
|
+
this.#pending.splice(pendingIndex, 1);
|
|
177
|
+
this.#jobs.delete(job.key);
|
|
178
|
+
job.reject(new Error("Diagnostic request had no remaining callers."));
|
|
179
|
+
job.settle();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#pump(): void {
|
|
183
|
+
if (this.#active !== undefined) return;
|
|
184
|
+
const job = this.#pending.shift();
|
|
185
|
+
if (!job) return;
|
|
186
|
+
this.#active = job;
|
|
187
|
+
let execution: DiagnosticRequestExecution<unknown>;
|
|
188
|
+
try {
|
|
189
|
+
execution = job.start();
|
|
190
|
+
} catch (error) {
|
|
191
|
+
job.reject(error);
|
|
192
|
+
job.settle();
|
|
193
|
+
this.#active = undefined;
|
|
194
|
+
this.#jobs.delete(job.key);
|
|
195
|
+
this.#pump();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
execution.result.then(job.resolve, job.reject).catch(() => {});
|
|
199
|
+
execution.settled
|
|
200
|
+
.catch(() => {})
|
|
201
|
+
.then(() => {
|
|
202
|
+
if (this.#active !== job) return;
|
|
203
|
+
this.#active = undefined;
|
|
204
|
+
this.#jobs.delete(job.key);
|
|
205
|
+
job.settle();
|
|
206
|
+
this.#pump();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -5,24 +5,20 @@ import {
|
|
|
5
5
|
} from "@mrclrchtr/supi-core/debug";
|
|
6
6
|
import { boundCwd } from "../debug-telemetry.ts";
|
|
7
7
|
|
|
8
|
-
type DiagnosticCollection =
|
|
8
|
+
type DiagnosticCollection =
|
|
9
|
+
| "cache"
|
|
10
|
+
| "fallback"
|
|
11
|
+
| "mixed"
|
|
12
|
+
| "none"
|
|
13
|
+
| "pull"
|
|
14
|
+
| "push"
|
|
15
|
+
| "typescript";
|
|
9
16
|
type DiagnosticFreshness = "not-observed" | "observed";
|
|
10
17
|
type DiagnosticOutcome = "completed" | "incomplete" | "skipped" | "timed-out";
|
|
11
18
|
type DiagnosticPullOutcome = "completed" | "failed" | "not-supported" | "not-used" | "timed-out";
|
|
12
|
-
type
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
| "tentative"
|
|
16
|
-
| "released"
|
|
17
|
-
| "settled"
|
|
18
|
-
| "timed-out";
|
|
19
|
-
type DiagnosticSettleOutcome =
|
|
20
|
-
| "not-used"
|
|
21
|
-
| "published"
|
|
22
|
-
| "tentative"
|
|
23
|
-
| "quiet"
|
|
24
|
-
| "released"
|
|
25
|
-
| "timed-out";
|
|
19
|
+
type DiagnosticRequestSource = "pull" | "typescript" | "mixed";
|
|
20
|
+
type DiagnosticPushOutcome = "not-used" | "tentative" | "released" | "settled" | "timed-out";
|
|
21
|
+
type DiagnosticSettleOutcome = "not-used" | "quiet" | "released" | "timed-out";
|
|
26
22
|
type DiagnosticTimingOperation = "refresh-open" | "sync-file";
|
|
27
23
|
|
|
28
24
|
/** Result of waiting for a quiet push-diagnostic window. */
|
|
@@ -31,8 +27,11 @@ export interface DiagnosticSettleResult {
|
|
|
31
27
|
readonly freshness: DiagnosticFreshness;
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
/** Result of
|
|
35
|
-
export type DiagnosticPushWaitOutcome = "
|
|
30
|
+
/** Result of collecting one file's ambient push diagnostics. */
|
|
31
|
+
export type DiagnosticPushWaitOutcome = "tentative" | "released" | "timed-out";
|
|
32
|
+
|
|
33
|
+
/** Result of waiting for one ambient push observation. */
|
|
34
|
+
export type DiagnosticPushObservationOutcome = "observed" | "released" | "timed-out";
|
|
36
35
|
|
|
37
36
|
interface DiagnosticTimingData {
|
|
38
37
|
readonly collection: DiagnosticCollection;
|
|
@@ -42,21 +41,10 @@ interface DiagnosticTimingData {
|
|
|
42
41
|
readonly outcome: DiagnosticOutcome;
|
|
43
42
|
readonly pull: DiagnosticPullOutcome;
|
|
44
43
|
readonly push: DiagnosticPushOutcome;
|
|
45
|
-
readonly reopen: number;
|
|
46
44
|
readonly settle: DiagnosticSettleOutcome;
|
|
47
45
|
readonly timedOut: boolean;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
/** Internal pull failure that retains timeout state and failed document URIs. */
|
|
51
|
-
export class DiagnosticPullError extends Error {
|
|
52
|
-
constructor(
|
|
53
|
-
readonly timedOut: boolean,
|
|
54
|
-
readonly failedUris: readonly string[] = [],
|
|
55
|
-
) {
|
|
56
|
-
super("pull diagnostics incomplete");
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
48
|
/** Bounded identity for one diagnostic timing observation. */
|
|
61
49
|
export interface DiagnosticTimingIdentity {
|
|
62
50
|
/** Configured server name. */
|
|
@@ -76,15 +64,16 @@ export interface DiagnosticTimingIdentity {
|
|
|
76
64
|
export class DiagnosticObserver {
|
|
77
65
|
readonly #timer = startDebugTimer();
|
|
78
66
|
#pull: "failed" | "not-supported" | "timed-out";
|
|
79
|
-
#
|
|
67
|
+
#requestSource: DiagnosticRequestSource | undefined;
|
|
68
|
+
#request: "failed" | "timed-out" | undefined;
|
|
80
69
|
|
|
81
70
|
constructor(
|
|
82
71
|
readonly operation: DiagnosticTimingOperation,
|
|
83
|
-
readonly
|
|
72
|
+
readonly hasDiagnosticRequestAdapter: boolean,
|
|
84
73
|
readonly control?: CodeRequestControl,
|
|
85
74
|
readonly identity?: DiagnosticTimingIdentity,
|
|
86
75
|
) {
|
|
87
|
-
this.#pull =
|
|
76
|
+
this.#pull = hasDiagnosticRequestAdapter ? "failed" : "not-supported";
|
|
88
77
|
}
|
|
89
78
|
|
|
90
79
|
synchronized(): void {
|
|
@@ -100,7 +89,6 @@ export class DiagnosticObserver {
|
|
|
100
89
|
outcome: "skipped",
|
|
101
90
|
pull: "not-used",
|
|
102
91
|
push: "not-used",
|
|
103
|
-
reopen: 0,
|
|
104
92
|
settle: "not-used",
|
|
105
93
|
timedOut: false,
|
|
106
94
|
});
|
|
@@ -115,7 +103,6 @@ export class DiagnosticObserver {
|
|
|
115
103
|
outcome: "completed",
|
|
116
104
|
pull: "not-used",
|
|
117
105
|
push: "not-used",
|
|
118
|
-
reopen: 0,
|
|
119
106
|
settle: "not-used",
|
|
120
107
|
timedOut: false,
|
|
121
108
|
});
|
|
@@ -131,7 +118,6 @@ export class DiagnosticObserver {
|
|
|
131
118
|
outcome: "completed",
|
|
132
119
|
pull: "completed",
|
|
133
120
|
push: "not-used",
|
|
134
|
-
reopen: 0,
|
|
135
121
|
settle: "not-used",
|
|
136
122
|
timedOut: false,
|
|
137
123
|
},
|
|
@@ -139,40 +125,119 @@ export class DiagnosticObserver {
|
|
|
139
125
|
);
|
|
140
126
|
}
|
|
141
127
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
this.#
|
|
128
|
+
/** Record a completed request-based collection. */
|
|
129
|
+
requestCompleted(source: DiagnosticRequestSource, documentCount: number, finish = true): void {
|
|
130
|
+
this.#requestSource = source;
|
|
131
|
+
this.#request = undefined;
|
|
132
|
+
if (source === "pull" && finish) {
|
|
133
|
+
this.pullCompleted(documentCount);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (!finish) {
|
|
137
|
+
this.#timer.mark("request");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.#finish(
|
|
141
|
+
{
|
|
142
|
+
collection: source === "typescript" ? "typescript" : "mixed",
|
|
143
|
+
documentCount,
|
|
144
|
+
fallback: false,
|
|
145
|
+
freshness: "observed",
|
|
146
|
+
outcome: "completed",
|
|
147
|
+
pull: this.hasDiagnosticRequestAdapter ? "not-used" : "not-supported",
|
|
148
|
+
push: "not-used",
|
|
149
|
+
settle: "not-used",
|
|
150
|
+
timedOut: false,
|
|
151
|
+
},
|
|
152
|
+
"request",
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Record a request failure without finishing the operation yet. */
|
|
157
|
+
requestFailed(source: DiagnosticRequestSource | undefined, error: unknown): void {
|
|
158
|
+
this.#requestSource = source;
|
|
159
|
+
this.#request = isDiagnosticTimeout(error) ? "timed-out" : "failed";
|
|
160
|
+
this.#timer.mark("request");
|
|
145
161
|
}
|
|
146
162
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
163
|
+
/** Finish an incomplete request collection without using push confirmation. */
|
|
164
|
+
requestIncomplete(
|
|
165
|
+
source: DiagnosticRequestSource | undefined,
|
|
166
|
+
timedOut = false,
|
|
167
|
+
documentCount = 1,
|
|
168
|
+
finish = true,
|
|
169
|
+
): void {
|
|
170
|
+
const requestSource = source ?? this.#requestSource ?? "pull";
|
|
171
|
+
this.#requestSource = requestSource;
|
|
172
|
+
this.#request = timedOut || this.#request === "timed-out" ? "timed-out" : "failed";
|
|
173
|
+
if (!finish) {
|
|
174
|
+
this.#timer.mark("request");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
this.#finish(
|
|
178
|
+
{
|
|
179
|
+
collection: requestSource === "typescript" ? "typescript" : requestSource,
|
|
180
|
+
documentCount,
|
|
181
|
+
fallback: false,
|
|
182
|
+
freshness: "not-observed",
|
|
183
|
+
outcome: this.#request === "timed-out" ? "timed-out" : "incomplete",
|
|
184
|
+
pull: requestSource === "pull" ? this.#request : "not-supported",
|
|
185
|
+
push: "not-used",
|
|
186
|
+
settle: "not-used",
|
|
187
|
+
timedOut: this.#request === "timed-out",
|
|
188
|
+
},
|
|
189
|
+
"request",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Finish a mixed request and push observation pass. */
|
|
194
|
+
mixedSettled(
|
|
195
|
+
source: DiagnosticRequestSource,
|
|
196
|
+
documentCount: number,
|
|
197
|
+
settle: DiagnosticSettleResult,
|
|
198
|
+
): void {
|
|
199
|
+
const timedOut = settle.outcome === "timed-out" || this.#request === "timed-out";
|
|
200
|
+
const requestOutcome =
|
|
201
|
+
this.#request === "timed-out"
|
|
202
|
+
? ("timed-out" as const)
|
|
203
|
+
: this.#request === "failed"
|
|
204
|
+
? ("failed" as const)
|
|
205
|
+
: ("completed" as const);
|
|
206
|
+
this.#finish(
|
|
207
|
+
{
|
|
208
|
+
collection: "mixed",
|
|
209
|
+
documentCount,
|
|
210
|
+
fallback: false,
|
|
211
|
+
freshness: settle.freshness,
|
|
212
|
+
outcome: timedOut ? "timed-out" : "incomplete",
|
|
213
|
+
pull: source === "pull" ? requestOutcome : "not-supported",
|
|
214
|
+
push: timedOut ? "timed-out" : settle.outcome === "released" ? "released" : "settled",
|
|
215
|
+
settle: settle.outcome,
|
|
216
|
+
timedOut,
|
|
217
|
+
},
|
|
218
|
+
"push-settle",
|
|
219
|
+
);
|
|
150
220
|
}
|
|
151
221
|
|
|
152
|
-
/**
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
reopened(count: number): void {
|
|
159
|
-
this.#reopened += count;
|
|
160
|
-
this.#timer.mark("first-settle");
|
|
222
|
+
/** Record a request that could not start within its collection budget. */
|
|
223
|
+
requestTimedOut(source: DiagnosticRequestSource | undefined): void {
|
|
224
|
+
this.#requestSource = source;
|
|
225
|
+
this.#request = "timed-out";
|
|
226
|
+
this.#timer.mark("request");
|
|
227
|
+
this.requestIncomplete(source, true);
|
|
161
228
|
}
|
|
162
229
|
|
|
163
230
|
pushSettled(documentCount: number, settle: DiagnosticSettleResult): void {
|
|
164
231
|
const timedOut = settle.outcome === "timed-out";
|
|
165
|
-
const completed = settle.outcome === "quiet" && settle.freshness === "observed";
|
|
166
232
|
this.#finish(
|
|
167
233
|
{
|
|
168
|
-
collection: this.
|
|
234
|
+
collection: this.hasDiagnosticRequestAdapter ? "fallback" : "push",
|
|
169
235
|
documentCount,
|
|
170
|
-
fallback: this.
|
|
236
|
+
fallback: this.hasDiagnosticRequestAdapter,
|
|
171
237
|
freshness: settle.freshness,
|
|
172
|
-
outcome:
|
|
238
|
+
outcome: timedOut ? "timed-out" : "incomplete",
|
|
173
239
|
pull: this.#pull,
|
|
174
240
|
push: timedOut ? "timed-out" : settle.outcome === "released" ? "released" : "settled",
|
|
175
|
-
reopen: this.#reopened,
|
|
176
241
|
settle: settle.outcome,
|
|
177
242
|
timedOut: timedOut || this.#pull === "timed-out",
|
|
178
243
|
},
|
|
@@ -181,30 +246,28 @@ export class DiagnosticObserver {
|
|
|
181
246
|
}
|
|
182
247
|
|
|
183
248
|
pushWaitCompleted(documentCount: number, push: DiagnosticPushWaitOutcome): void {
|
|
184
|
-
const confirmed = push === "published";
|
|
185
|
-
// A tentative result used the full wait budget without a confirming
|
|
186
|
-
// republish. Keep its distinct push value and report the budget expiry.
|
|
187
249
|
const timedOut = push === "tentative" || push === "timed-out";
|
|
188
|
-
const observed = push === "
|
|
250
|
+
const observed = push === "tentative";
|
|
189
251
|
this.#finish(
|
|
190
252
|
{
|
|
191
|
-
collection: this.
|
|
253
|
+
collection: this.hasDiagnosticRequestAdapter ? "fallback" : "push",
|
|
192
254
|
documentCount,
|
|
193
|
-
fallback: this.
|
|
255
|
+
fallback: this.hasDiagnosticRequestAdapter,
|
|
194
256
|
freshness: observed ? "observed" : "not-observed",
|
|
195
|
-
outcome:
|
|
257
|
+
outcome: timedOut ? "timed-out" : "incomplete",
|
|
196
258
|
pull: this.#pull,
|
|
197
259
|
push,
|
|
198
|
-
|
|
199
|
-
settle: push,
|
|
260
|
+
settle: timedOut ? "timed-out" : push,
|
|
200
261
|
timedOut: timedOut || this.#pull === "timed-out",
|
|
201
262
|
},
|
|
202
263
|
"push-settle",
|
|
203
264
|
);
|
|
204
265
|
}
|
|
205
266
|
|
|
206
|
-
#finish(
|
|
207
|
-
|
|
267
|
+
#finish(
|
|
268
|
+
data: DiagnosticTimingData,
|
|
269
|
+
finalPhase?: "pull" | "push-settle" | "request" | "synchronize",
|
|
270
|
+
): void {
|
|
208
271
|
this.#timer.finish(
|
|
209
272
|
() => ({
|
|
210
273
|
operationId: this.control?.operationId,
|
|
@@ -215,10 +278,7 @@ export class DiagnosticObserver {
|
|
|
215
278
|
cwd: boundCwd(this.identity?.cwd),
|
|
216
279
|
data: {
|
|
217
280
|
operation: this.operation,
|
|
218
|
-
...
|
|
219
|
-
// Reopen fallback usage is recorded only when it happened; the
|
|
220
|
-
// default event shape stays stable for consumers of #322 telemetry.
|
|
221
|
-
...(reopen > 0 ? { reopen } : {}),
|
|
281
|
+
...data,
|
|
222
282
|
...(this.identity?.server !== undefined
|
|
223
283
|
? { server: truncateIdentity(this.identity.server) }
|
|
224
284
|
: {}),
|
|
@@ -234,6 +294,5 @@ export class DiagnosticObserver {
|
|
|
234
294
|
|
|
235
295
|
/** Return whether a diagnostic failure represents a timeout without retaining its message. */
|
|
236
296
|
export function isDiagnosticTimeout(error: unknown): boolean {
|
|
237
|
-
if (error instanceof DiagnosticPullError) return error.timedOut;
|
|
238
297
|
return error instanceof Error && /\btimed? ?out\b|\btimeout\b/i.test(error.message);
|
|
239
298
|
}
|