@mrclrchtr/supi-code-intelligence 6.3.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 +16 -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/diagnostics/workspace-sentinels.ts +4 -32
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sources.ts +108 -0
- 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 +770 -56
- 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 +48 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +50 -4
- 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 +27 -11
- package/src/session/health-types.ts +21 -4
- package/src/session/health-workflow.ts +28 -22
- package/src/session/orientation-types.ts +5 -6
- package/src/session/orientation-workflow.ts +10 -2
- package/src/session/session.ts +12 -5
- package/src/session/target-workflow.ts +9 -6
- package/src/substrate/lsp/maintenance.ts +53 -47
- package/src/substrate/lsp/source-tracking.ts +252 -0
- package/src/tool/code_find/guidance.ts +3 -3
- 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 +2 -2
- 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 +3 -5
- package/src/tool/code_health/markdown.ts +90 -57
- package/src/tool/code_health/refresh-outcome.ts +51 -0
- package/src/tool/code_health/refresh-status.ts +130 -43
- 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 +29 -38
- package/src/tool/code_inspect/guidance.ts +5 -3
- package/src/tool/code_inspect/tui.ts +7 -1
- package/src/tool/code_orientation/guidance.ts +3 -4
- 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 +3 -5
- package/src/tool/code_refactor_plan/tui.ts +7 -1
- package/src/tool/code_resolve/guidance.ts +3 -5
- package/src/tool/code_resolve/result.ts +13 -10
- package/src/tool/code_resolve/tui.ts +18 -1
- package/src/tool/guidance.ts +11 -9
- 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,7 +1,8 @@
|
|
|
1
|
-
// biome-ignore-all lint/style/noExcessiveLinesPerFile: refresh orchestration
|
|
1
|
+
// biome-ignore-all lint/style/noExcessiveLinesPerFile: refresh orchestration and evidence collection stay in one cohesive module.
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import {
|
|
4
4
|
type CodeRequestControl,
|
|
5
|
+
isCodeRequestDeadlineError,
|
|
5
6
|
isCodeRequestInterruption,
|
|
6
7
|
throwIfCodeRequestInterrupted,
|
|
7
8
|
} from "@mrclrchtr/supi-code-runtime/api";
|
|
@@ -11,31 +12,23 @@ import {
|
|
|
11
12
|
type DiagnosticEvidenceSummary,
|
|
12
13
|
summarizeDiagnosticEvidence,
|
|
13
14
|
} from "../diagnostics/evidence.ts";
|
|
14
|
-
import {
|
|
15
|
+
import { raceRequestControl } from "../session/readiness.ts";
|
|
15
16
|
import {
|
|
16
17
|
type DiagnosticCacheEntry,
|
|
17
18
|
type DiagnosticSynchronization,
|
|
18
19
|
hasCurrentEvidence,
|
|
19
20
|
hasFreshEvidence,
|
|
20
|
-
hasFreshPush,
|
|
21
|
-
isCurrentSynchronization,
|
|
22
21
|
latestCurrentEvidenceReceivedAt,
|
|
23
22
|
nextDocumentVersion,
|
|
24
|
-
raceDiagnosticPull,
|
|
25
23
|
} from "./client-diagnostic-evidence.ts";
|
|
26
24
|
import type { ClientDiagnosticsHost } from "./client-diagnostic-host.ts";
|
|
27
25
|
import type {
|
|
28
26
|
DiagnosticPublicationIdentity,
|
|
29
27
|
DiagnosticPublicationSynchronization,
|
|
30
28
|
} from "./client-diagnostic-publication.ts";
|
|
31
|
-
import {
|
|
32
|
-
import
|
|
33
|
-
import {
|
|
34
|
-
DiagnosticObserver,
|
|
35
|
-
DiagnosticPullError,
|
|
36
|
-
isDiagnosticTimeout,
|
|
37
|
-
} from "./client-diagnostic-timing.ts";
|
|
38
|
-
import type { DiagnosticStateWait, DiagnosticWaitRegistry } from "./client-diagnostic-waiters.ts";
|
|
29
|
+
import { isDiagnosticRequestInvalidated } from "./client-diagnostic-request.ts";
|
|
30
|
+
import { DiagnosticObserver, isDiagnosticTimeout } from "./client-diagnostic-timing.ts";
|
|
31
|
+
import type { DiagnosticWaitRegistry } from "./client-diagnostic-waiters.ts";
|
|
39
32
|
import {
|
|
40
33
|
fingerprintDocumentContent,
|
|
41
34
|
hasConfirmedDiagnosticEvidence,
|
|
@@ -43,7 +36,6 @@ import {
|
|
|
43
36
|
} from "./client-document-state.ts";
|
|
44
37
|
import {
|
|
45
38
|
type ResynchronizeDocumentsResult,
|
|
46
|
-
reopenDocument,
|
|
47
39
|
resynchronizeOpenDocuments,
|
|
48
40
|
} from "./client-document-sync.ts";
|
|
49
41
|
import { getDiagnosticFileState } from "./client-file-state.ts";
|
|
@@ -62,7 +54,7 @@ export function sendDidCloseNotification(
|
|
|
62
54
|
export function buildDiagnosticRefreshEvidence(options: {
|
|
63
55
|
requestedFiles: readonly string[];
|
|
64
56
|
resynchronization: ResynchronizeDocumentsResult;
|
|
65
|
-
/** Synchronizations that prove evidence
|
|
57
|
+
/** Synchronizations that can prove evidence after this refresh. */
|
|
66
58
|
synchronizations: readonly DiagnosticSynchronization[];
|
|
67
59
|
failedPullUris: ReadonlySet<string>;
|
|
68
60
|
failedFiles: ReadonlySet<string>;
|
|
@@ -70,15 +62,12 @@ export function buildDiagnosticRefreshEvidence(options: {
|
|
|
70
62
|
currentEvidenceRevision: number;
|
|
71
63
|
openDocuments: ReadonlyMap<string, unknown>;
|
|
72
64
|
diagnosticStore: ReadonlyMap<string, DiagnosticCacheEntry>;
|
|
73
|
-
/** Required quiet time after the latest push for this refresh result. */
|
|
74
|
-
pushQuietMs?: number;
|
|
75
65
|
}): DiagnosticEvidenceSummary {
|
|
76
66
|
const synchronizationByFile = new Map(
|
|
77
67
|
options.synchronizations.map((item) => [uriToFile(item.uri), item]),
|
|
78
68
|
);
|
|
79
69
|
const removedFiles = new Set(options.resynchronization.removedFiles);
|
|
80
70
|
const failedFiles = new Set(options.resynchronization.failedFiles);
|
|
81
|
-
const observedAt = Date.now();
|
|
82
71
|
const documents = options.requestedFiles.map((file) => {
|
|
83
72
|
const uri = fileToUri(file);
|
|
84
73
|
const synchronization = synchronizationByFile.get(file);
|
|
@@ -101,15 +90,27 @@ export function buildDiagnosticRefreshEvidence(options: {
|
|
|
101
90
|
store: options.diagnosticStore,
|
|
102
91
|
synchronization,
|
|
103
92
|
currentEvidenceRevision: options.currentEvidenceRevision,
|
|
104
|
-
pushQuietMs: options.pushQuietMs,
|
|
105
|
-
observedAt,
|
|
106
93
|
})
|
|
107
94
|
) {
|
|
108
95
|
return { file, status: "confirmed" as const };
|
|
109
96
|
}
|
|
97
|
+
const currentPushObservation = Boolean(
|
|
98
|
+
synchronization &&
|
|
99
|
+
options.diagnosticStore.get(uri)?.source === "push" &&
|
|
100
|
+
hasCurrentEvidence(
|
|
101
|
+
options.diagnosticStore,
|
|
102
|
+
synchronization,
|
|
103
|
+
options.currentEvidenceRevision,
|
|
104
|
+
),
|
|
105
|
+
);
|
|
110
106
|
return {
|
|
111
107
|
file,
|
|
112
|
-
|
|
108
|
+
// A failed request with a current ambient push remains unconfirmed, not
|
|
109
|
+
// failed: the push is useful observation but cannot confirm the file.
|
|
110
|
+
status:
|
|
111
|
+
options.failedPullUris.has(uri) && !currentPushObservation
|
|
112
|
+
? ("failed" as const)
|
|
113
|
+
: ("unconfirmed" as const),
|
|
113
114
|
};
|
|
114
115
|
});
|
|
115
116
|
return summarizeDiagnosticEvidence(documents);
|
|
@@ -120,135 +121,101 @@ function hasSettledRefreshEvidence(options: {
|
|
|
120
121
|
store: ReadonlyMap<string, DiagnosticCacheEntry>;
|
|
121
122
|
synchronization: DiagnosticSynchronization;
|
|
122
123
|
currentEvidenceRevision: number;
|
|
123
|
-
pushQuietMs: number | undefined;
|
|
124
|
-
observedAt: number;
|
|
125
124
|
}): boolean {
|
|
126
|
-
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
const entry = options.store.get(options.synchronization.uri);
|
|
130
|
-
return (
|
|
131
|
-
options.pushQuietMs === undefined ||
|
|
132
|
-
entry?.source !== "push" ||
|
|
133
|
-
options.observedAt - entry.receivedAt >= options.pushQuietMs
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Pull one document and reject evidence from a stale client generation. */
|
|
138
|
-
export function pullClientDiagnosticEvidence(
|
|
139
|
-
options: Omit<DiagnosticPullRequest, "previousResultId"> & {
|
|
140
|
-
store: Map<string, DiagnosticCacheEntry>;
|
|
141
|
-
synchronizationId?: number;
|
|
142
|
-
evidenceRevision: number;
|
|
143
|
-
currentRevision(): number;
|
|
144
|
-
isCurrentSynchronization(): boolean;
|
|
145
|
-
isRelatedUriTracked(uri: string): boolean;
|
|
146
|
-
pull(
|
|
147
|
-
request: DiagnosticPullRequest,
|
|
148
|
-
): Promise<import("../config/types.ts").DocumentDiagnosticReport | null>;
|
|
149
|
-
},
|
|
150
|
-
): Promise<boolean> {
|
|
151
|
-
return pullDiagnosticEvidence(options);
|
|
125
|
+
return hasFreshEvidence(options.store, options.synchronization, options.currentEvidenceRevision);
|
|
152
126
|
}
|
|
153
127
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
synchronizationId?: number;
|
|
163
|
-
evidenceRevision?: number;
|
|
164
|
-
};
|
|
165
|
-
}): Promise<boolean> {
|
|
166
|
-
return pullClientDiagnosticEvidence({
|
|
167
|
-
store: options.store,
|
|
168
|
-
...options.request,
|
|
169
|
-
evidenceRevision: options.request.evidenceRevision ?? options.currentEvidenceRevision(),
|
|
170
|
-
currentRevision: options.currentEvidenceRevision,
|
|
171
|
-
isCurrentSynchronization: () =>
|
|
172
|
-
options.request.synchronizationId === undefined ||
|
|
173
|
-
isCurrentSynchronization(options.openDocuments, {
|
|
174
|
-
uri: options.request.uri,
|
|
175
|
-
synchronizationId: options.request.synchronizationId,
|
|
176
|
-
evidenceRevision: options.request.evidenceRevision,
|
|
177
|
-
}),
|
|
178
|
-
isRelatedUriTracked: options.isRelatedUriTracked,
|
|
179
|
-
pull: (request) => options.host.pullDocumentDiagnostics(request),
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** Collect pull evidence for every synchronized document in one refresh. */
|
|
184
|
-
export async function pullDiagnosticsForOpenDocuments(options: {
|
|
185
|
-
requests: readonly DiagnosticSynchronization[];
|
|
186
|
-
syncStart: number;
|
|
187
|
-
maxWaitMs: number;
|
|
188
|
-
signal?: AbortSignal;
|
|
189
|
-
deadline?: number;
|
|
190
|
-
operationId?: string;
|
|
191
|
-
currentEvidenceRevision: () => number;
|
|
192
|
-
openDocuments: ReadonlyMap<string, { evidenceRevision: number; synchronizationId: number }>;
|
|
193
|
-
diagnosticStore: ReadonlyMap<string, DiagnosticCacheEntry>;
|
|
194
|
-
waitForChange: () => DiagnosticStateWait;
|
|
195
|
-
pullDiagnostics: (options: {
|
|
128
|
+
interface OpenDocumentRequestCollectionOptions {
|
|
129
|
+
readonly requests: readonly DiagnosticSynchronization[];
|
|
130
|
+
readonly syncStart: number;
|
|
131
|
+
readonly maxWaitMs: number;
|
|
132
|
+
readonly signal?: AbortSignal;
|
|
133
|
+
readonly deadline?: number;
|
|
134
|
+
readonly operationId?: string;
|
|
135
|
+
readonly pullDiagnostics: (options: {
|
|
196
136
|
request: DiagnosticSynchronization;
|
|
197
137
|
timeoutMs: number;
|
|
198
138
|
signal: AbortSignal;
|
|
199
139
|
operationId?: string;
|
|
200
140
|
deadline?: number;
|
|
201
141
|
}) => Promise<boolean>;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const outcome = await raceDiagnosticPull({
|
|
216
|
-
pull: options.pullDiagnostics({
|
|
217
|
-
request,
|
|
218
|
-
timeoutMs: remaining,
|
|
219
|
-
signal: pullController.signal,
|
|
220
|
-
operationId: options.operationId,
|
|
221
|
-
deadline: options.deadline,
|
|
222
|
-
}),
|
|
223
|
-
waitForChange: options.waitForChange,
|
|
224
|
-
freshPush: () =>
|
|
225
|
-
hasFreshPush(options.diagnosticStore, request, options.currentEvidenceRevision()),
|
|
226
|
-
current: () => isCurrentSynchronization(options.openDocuments, request),
|
|
227
|
-
});
|
|
228
|
-
if (outcome !== "pull") pullController.abort();
|
|
229
|
-
return outcome === "pull";
|
|
230
|
-
} finally {
|
|
231
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
232
|
-
}
|
|
233
|
-
}),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Collect request-based evidence sequentially within one refresh budget. */
|
|
145
|
+
export async function pullDiagnosticsForOpenDocuments(
|
|
146
|
+
options: OpenDocumentRequestCollectionOptions,
|
|
147
|
+
): Promise<{
|
|
148
|
+
failedUris: readonly string[];
|
|
149
|
+
incompleteUris: readonly string[];
|
|
150
|
+
timedOut: boolean;
|
|
151
|
+
}> {
|
|
152
|
+
const deadline = Math.min(
|
|
153
|
+
options.syncStart + options.maxWaitMs,
|
|
154
|
+
options.deadline ?? Number.POSITIVE_INFINITY,
|
|
234
155
|
);
|
|
156
|
+
const failedUris: string[] = [];
|
|
157
|
+
const incompleteUris: string[] = [];
|
|
158
|
+
let timedOut = false;
|
|
159
|
+
|
|
160
|
+
for (const request of options.requests) {
|
|
161
|
+
throwIfCodeRequestInterrupted({ signal: options.signal, deadline: options.deadline });
|
|
162
|
+
const remaining = deadline - Date.now();
|
|
163
|
+
if (remaining <= 0) {
|
|
164
|
+
timedOut = true;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
const outcome = await collectOneRefreshRequest(options, request, remaining, deadline);
|
|
168
|
+
if (outcome === "failed") failedUris.push(request.uri);
|
|
169
|
+
if (outcome === "incomplete") incompleteUris.push(request.uri);
|
|
170
|
+
if (outcome === "timed-out") {
|
|
171
|
+
timedOut = true;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
235
175
|
|
|
236
|
-
// A cancelled refresh must not degrade into failed coverage evidence:
|
|
237
|
-
// the caller no longer awaits a result.
|
|
238
176
|
throwIfCodeRequestInterrupted({ signal: options.signal, deadline: options.deadline });
|
|
177
|
+
return { failedUris, incompleteUris, timedOut };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
type RefreshRequestOutcome = "completed" | "failed" | "incomplete" | "timed-out";
|
|
239
181
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
182
|
+
async function collectOneRefreshRequest(
|
|
183
|
+
options: OpenDocumentRequestCollectionOptions,
|
|
184
|
+
request: DiagnosticSynchronization,
|
|
185
|
+
timeoutMs: number,
|
|
186
|
+
collectionDeadline: number,
|
|
187
|
+
): Promise<RefreshRequestOutcome> {
|
|
188
|
+
const pullController = new AbortController();
|
|
189
|
+
const onAbort = () => pullController.abort(options.signal?.reason);
|
|
190
|
+
if (options.signal?.aborted) onAbort();
|
|
191
|
+
else options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
192
|
+
try {
|
|
193
|
+
const result = options.pullDiagnostics({
|
|
194
|
+
request,
|
|
195
|
+
timeoutMs,
|
|
196
|
+
signal: pullController.signal,
|
|
197
|
+
operationId: options.operationId,
|
|
198
|
+
// The adapter receives the per-refresh deadline. This prevents queued
|
|
199
|
+
// work from starting after the bounded refresh budget expires.
|
|
200
|
+
deadline: collectionDeadline,
|
|
247
201
|
});
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
);
|
|
202
|
+
const completed = await raceRequestControl(result, {
|
|
203
|
+
signal: options.signal,
|
|
204
|
+
deadline: collectionDeadline,
|
|
205
|
+
});
|
|
206
|
+
return completed ? "completed" : "incomplete";
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (isDiagnosticRequestInvalidated(error)) return "incomplete";
|
|
209
|
+
if (
|
|
210
|
+
options.signal?.aborted ||
|
|
211
|
+
(options.deadline !== undefined && Date.now() >= options.deadline)
|
|
212
|
+
) {
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
if (isCodeRequestDeadlineError(error)) return "timed-out";
|
|
216
|
+
return isDiagnosticTimeout(error) ? "timed-out" : "failed";
|
|
217
|
+
} finally {
|
|
218
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
252
219
|
}
|
|
253
220
|
}
|
|
254
221
|
|
|
@@ -258,16 +225,13 @@ export async function pullDiagnosticsForOpenDocuments(options: {
|
|
|
258
225
|
* A document whose disk content still matches its open fingerprint stays in
|
|
259
226
|
* the server's current state:
|
|
260
227
|
* - with current evidence it is reusable without document synchronization;
|
|
261
|
-
* - without current evidence it is retained: it keeps its
|
|
262
|
-
*
|
|
263
|
-
* no-op didChange. Large push-only servers (typescript-language-server)
|
|
264
|
-
* skip empty-to-empty publishes, so a no-op didChange of a clean file can
|
|
265
|
-
* never confirm — it only invalidates in-progress evidence and restarts
|
|
266
|
-
* full-program checks (#344).
|
|
228
|
+
* - without current evidence it is retained: it keeps its server version and
|
|
229
|
+
* receives fresh request or push evidence without a no-op didChange.
|
|
267
230
|
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
231
|
+
* An evidence invalidation applies to every open document, but only changed
|
|
232
|
+
* text is synchronized. This separates evidence generation
|
|
233
|
+
* from document synchronization and avoids restarting a server's full-program
|
|
234
|
+
* check for unchanged files.
|
|
271
235
|
*/
|
|
272
236
|
function classifyReusableDocuments(options: {
|
|
273
237
|
openDocuments: ReadonlyMap<string, OpenDocumentState>;
|
|
@@ -292,11 +256,6 @@ function classifyReusableDocuments(options: {
|
|
|
292
256
|
// The resynchronization path classifies removed and unreadable files.
|
|
293
257
|
continue;
|
|
294
258
|
}
|
|
295
|
-
if (document.evidenceRevision !== options.evidenceRevision) {
|
|
296
|
-
// Invalidated generation: the resync didChange re-establishes proof.
|
|
297
|
-
preloadedContent.set(uri, content);
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
259
|
if (fingerprintDocumentContent(content) !== document.contentFingerprint) {
|
|
301
260
|
preloadedContent.set(uri, content);
|
|
302
261
|
continue;
|
|
@@ -327,12 +286,22 @@ interface ClientDiagnosticRefreshOptions {
|
|
|
327
286
|
readonly failedFiles: () => ReadonlySet<string>;
|
|
328
287
|
readonly isRelatedUriTracked: (uri: string) => boolean;
|
|
329
288
|
readonly nextSynchronizationId: () => number;
|
|
289
|
+
/** Invalidate route evidence before applying a disk content change. */
|
|
290
|
+
readonly noteInputContentChange: () => number;
|
|
291
|
+
/** Keep the shared semantic barrier's verified disk observation current. */
|
|
292
|
+
readonly observeDiskContent: (uri: string, content: string) => void;
|
|
330
293
|
readonly clearFile: (uri: string) => void;
|
|
331
294
|
readonly invalidateEvidence: (uri: string) => void;
|
|
332
295
|
readonly markUnversionedSyncMoment: (uri: string) => void;
|
|
333
296
|
readonly clearFailedFile: (uri: string) => void;
|
|
334
|
-
/**
|
|
335
|
-
readonly
|
|
297
|
+
/** Shared request engine used for every request-based diagnostic source. */
|
|
298
|
+
readonly requestDiagnostics: (options: {
|
|
299
|
+
request: DiagnosticSynchronization;
|
|
300
|
+
timeoutMs: number;
|
|
301
|
+
signal?: AbortSignal;
|
|
302
|
+
deadline?: number;
|
|
303
|
+
operationId?: string;
|
|
304
|
+
}) => Promise<boolean>;
|
|
336
305
|
readonly options: { maxWaitMs?: number; quietMs?: number } & CodeRequestControl;
|
|
337
306
|
/** Push-publication telemetry surface for this client. */
|
|
338
307
|
readonly publications: {
|
|
@@ -354,20 +323,15 @@ interface PreparedRefreshDocuments {
|
|
|
354
323
|
/** Classify reusable documents, then resynchronize only the remaining set. */
|
|
355
324
|
function prepareRefreshDocuments(
|
|
356
325
|
options: ClientDiagnosticRefreshOptions,
|
|
357
|
-
supportsPull: boolean,
|
|
358
326
|
evidenceRevision: number,
|
|
359
327
|
): PreparedRefreshDocuments {
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
})
|
|
368
|
-
: undefined;
|
|
369
|
-
const reusableUris = classification?.reusableUris ?? new Set<string>();
|
|
370
|
-
const retainedUris = classification?.retainedUris ?? new Set<string>();
|
|
328
|
+
const classification = classifyReusableDocuments({
|
|
329
|
+
openDocuments: options.openDocuments,
|
|
330
|
+
diagnosticStore: options.diagnosticStore,
|
|
331
|
+
evidenceRevision,
|
|
332
|
+
failedFiles: options.failedFiles(),
|
|
333
|
+
});
|
|
334
|
+
const { reusableUris, retainedUris } = classification;
|
|
371
335
|
const documentsToResynchronize = new Map(
|
|
372
336
|
Array.from(options.openDocuments).filter(
|
|
373
337
|
([uri]) => !reusableUris.has(uri) && !retainedUris.has(uri),
|
|
@@ -379,10 +343,12 @@ function prepareRefreshDocuments(
|
|
|
379
343
|
nextVersion: (uri) => nextDocumentVersion(options.versionHistory, uri),
|
|
380
344
|
nextSynchronizationId: options.nextSynchronizationId,
|
|
381
345
|
evidenceRevision,
|
|
346
|
+
noteInputContentChange: options.noteInputContentChange,
|
|
347
|
+
observeDiskContent: options.observeDiskContent,
|
|
382
348
|
incrementalSync: options.host.usesIncrementalDocumentSync(),
|
|
383
349
|
sendNotification: (method, params) => options.host.sendNotification(method, params),
|
|
384
350
|
uriToFile,
|
|
385
|
-
preloadedContent: classification
|
|
351
|
+
preloadedContent: classification.preloadedContent,
|
|
386
352
|
clearFile: options.clearFile,
|
|
387
353
|
invalidateEvidence: options.invalidateEvidence,
|
|
388
354
|
markUnversionedSyncMoment: options.markUnversionedSyncMoment,
|
|
@@ -407,7 +373,7 @@ function prepareRefreshDocuments(
|
|
|
407
373
|
retainedSynchronizations.push({
|
|
408
374
|
uri,
|
|
409
375
|
synchronizationId: document.synchronizationId,
|
|
410
|
-
evidenceRevision:
|
|
376
|
+
...(document.evidenceRevision === evidenceRevision ? { evidenceRevision } : {}),
|
|
411
377
|
});
|
|
412
378
|
}
|
|
413
379
|
const synchronizations = [
|
|
@@ -415,13 +381,8 @@ function prepareRefreshDocuments(
|
|
|
415
381
|
...retainedSynchronizations,
|
|
416
382
|
...resynchronization.synchronizations,
|
|
417
383
|
];
|
|
418
|
-
// Pull-capable routes still request current diagnostics even when every
|
|
419
|
-
// document synchronization is reusable.
|
|
420
384
|
const fullyReusable =
|
|
421
|
-
|
|
422
|
-
!supportsPull &&
|
|
423
|
-
options.openDocuments.size > 0 &&
|
|
424
|
-
reusableUris.size === options.openDocuments.size;
|
|
385
|
+
options.openDocuments.size > 0 && reusableUris.size === options.openDocuments.size;
|
|
425
386
|
return { resynchronization, synchronizations, fullyReusable };
|
|
426
387
|
}
|
|
427
388
|
|
|
@@ -433,11 +394,19 @@ export async function refreshClientOpenDiagnostics(
|
|
|
433
394
|
// resynchronization or protocol traffic may start for a pass the caller
|
|
434
395
|
// no longer awaits.
|
|
435
396
|
throwIfCodeRequestInterrupted(options.options);
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
397
|
+
const requestAdapter = options.host.diagnosticRequestAdapter;
|
|
398
|
+
const hasDiagnosticRequestAdapter = Array.from(options.openDocuments.keys()).some((uri) =>
|
|
399
|
+
requestAdapter.supports(uri),
|
|
400
|
+
);
|
|
401
|
+
const observer = new DiagnosticObserver(
|
|
402
|
+
"refresh-open",
|
|
403
|
+
hasDiagnosticRequestAdapter,
|
|
404
|
+
options.options,
|
|
405
|
+
{
|
|
406
|
+
server: options.host.server,
|
|
407
|
+
cwd: options.host.cwd,
|
|
408
|
+
},
|
|
409
|
+
);
|
|
441
410
|
if (!options.host.isOperational()) {
|
|
442
411
|
observer.skipped(options.requestedFiles.length);
|
|
443
412
|
return summarizeDiagnosticEvidence(
|
|
@@ -452,11 +421,11 @@ export async function refreshClientOpenDiagnostics(
|
|
|
452
421
|
const maxWaitMs = options.options.maxWaitMs ?? 3_000;
|
|
453
422
|
const quietMs = options.options.quietMs ?? 200;
|
|
454
423
|
const syncStart = Date.now();
|
|
455
|
-
const prepared = prepareRefreshDocuments(options,
|
|
424
|
+
const prepared = prepareRefreshDocuments(options, options.evidenceRevision());
|
|
456
425
|
const resynchronization = prepared.resynchronization;
|
|
457
|
-
|
|
426
|
+
const synchronizations = prepared.synchronizations;
|
|
458
427
|
let failedPullUris: ReadonlySet<string> = new Set();
|
|
459
|
-
const buildEvidence = (
|
|
428
|
+
const buildEvidence = () =>
|
|
460
429
|
buildDiagnosticRefreshEvidence({
|
|
461
430
|
requestedFiles: options.requestedFiles,
|
|
462
431
|
resynchronization,
|
|
@@ -467,16 +436,8 @@ export async function refreshClientOpenDiagnostics(
|
|
|
467
436
|
currentEvidenceRevision: options.evidenceRevision(),
|
|
468
437
|
openDocuments: options.openDocuments,
|
|
469
438
|
diagnosticStore: options.diagnosticStore,
|
|
470
|
-
pushQuietMs,
|
|
471
439
|
});
|
|
472
440
|
|
|
473
|
-
// A fully reusable push-only refresh has no protocol work to collect.
|
|
474
|
-
// Preserve the existing cache timing event format and return current evidence.
|
|
475
|
-
if (prepared.fullyReusable) {
|
|
476
|
-
observer.cacheReused(synchronizations.length);
|
|
477
|
-
return buildEvidence();
|
|
478
|
-
}
|
|
479
|
-
|
|
480
441
|
const settleEpoch = options.waiters.settleEpoch;
|
|
481
442
|
observer.synchronized();
|
|
482
443
|
if (synchronizations.length === 0) {
|
|
@@ -484,63 +445,84 @@ export async function refreshClientOpenDiagnostics(
|
|
|
484
445
|
return buildEvidence();
|
|
485
446
|
}
|
|
486
447
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
});
|
|
495
|
-
failedPullUris = new Set(pull.failedPullUris);
|
|
496
|
-
if (pull.completed) return buildEvidence();
|
|
448
|
+
const requestSynchronizations = synchronizations.filter((synchronization) =>
|
|
449
|
+
requestAdapter.supports(synchronization.uri),
|
|
450
|
+
);
|
|
451
|
+
// A fully reusable push-only refresh has no protocol work to collect.
|
|
452
|
+
if (prepared.fullyReusable && requestSynchronizations.length === 0) {
|
|
453
|
+
observer.cacheReused(synchronizations.length);
|
|
454
|
+
return buildEvidence();
|
|
497
455
|
}
|
|
456
|
+
const pushSynchronizations = synchronizations.filter(
|
|
457
|
+
(synchronization) => !requestSynchronizations.includes(synchronization),
|
|
458
|
+
);
|
|
498
459
|
|
|
499
|
-
const waitForDiagnosticSettle = (
|
|
460
|
+
const waitForDiagnosticSettle = (
|
|
461
|
+
targetSynchronizations: readonly DiagnosticSynchronization[],
|
|
462
|
+
settleStart: number,
|
|
463
|
+
settleGeneration: number,
|
|
464
|
+
) =>
|
|
500
465
|
options.waiters.waitForSettle(
|
|
501
466
|
{
|
|
502
467
|
syncStart: settleStart,
|
|
503
468
|
maxWaitMs,
|
|
504
469
|
quietMs,
|
|
505
470
|
settleEpoch: settleGeneration,
|
|
506
|
-
isComplete: () =>
|
|
507
|
-
synchronizations.every((item) =>
|
|
508
|
-
hasFreshEvidence(options.diagnosticStore, item, options.evidenceRevision()),
|
|
509
|
-
),
|
|
510
471
|
latestReceived: () =>
|
|
511
472
|
latestCurrentEvidenceReceivedAt(
|
|
512
473
|
options.diagnosticStore,
|
|
513
|
-
|
|
474
|
+
targetSynchronizations,
|
|
514
475
|
options.evidenceRevision(),
|
|
515
476
|
),
|
|
516
477
|
},
|
|
517
478
|
options.options,
|
|
518
479
|
);
|
|
519
480
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const reopen = await reopenUnconfirmedDocuments({
|
|
481
|
+
if (requestSynchronizations.length > 0) {
|
|
482
|
+
const request = await collectPullEvidenceForRefresh({
|
|
523
483
|
options,
|
|
524
|
-
synchronizations,
|
|
525
|
-
|
|
526
|
-
|
|
484
|
+
synchronizations: requestSynchronizations,
|
|
485
|
+
syncStart,
|
|
486
|
+
maxWaitMs,
|
|
527
487
|
});
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
488
|
+
failedPullUris = new Set(request.failedPullUris);
|
|
489
|
+
const source = requestSourceFor(options.host, requestSynchronizations);
|
|
490
|
+
if (pushSynchronizations.length === 0) {
|
|
491
|
+
if (request.completed) observer.requestCompleted(source, requestSynchronizations.length);
|
|
492
|
+
else observer.requestIncomplete(source, request.timedOut, requestSynchronizations.length);
|
|
493
|
+
throwIfCodeRequestInterrupted(options.options);
|
|
494
|
+
emitRefreshPublicationSummary(options, synchronizations);
|
|
495
|
+
return buildEvidence();
|
|
496
|
+
}
|
|
497
|
+
if (request.completed) {
|
|
498
|
+
observer.requestCompleted(source, requestSynchronizations.length, false);
|
|
499
|
+
} else {
|
|
500
|
+
observer.requestIncomplete(source, request.timedOut, requestSynchronizations.length, false);
|
|
533
501
|
}
|
|
502
|
+
const finalSettle = await waitForDiagnosticSettle(pushSynchronizations, syncStart, settleEpoch);
|
|
503
|
+
observer.mixedSettled(source, synchronizations.length, finalSettle);
|
|
504
|
+
// Request failures are final for this bounded pass. Ambient pushes can
|
|
505
|
+
// remain useful observations, but they cannot confirm the request route.
|
|
506
|
+
throwIfCodeRequestInterrupted(options.options);
|
|
507
|
+
emitRefreshPublicationSummary(options, synchronizations);
|
|
508
|
+
return buildEvidence();
|
|
534
509
|
}
|
|
510
|
+
|
|
511
|
+
const finalSettle = await waitForDiagnosticSettle(synchronizations, syncStart, settleEpoch);
|
|
535
512
|
observer.pushSettled(synchronizations.length, finalSettle);
|
|
536
513
|
// A cancelled settle must not publish evidence the caller no longer awaits.
|
|
537
514
|
throwIfCodeRequestInterrupted(options.options);
|
|
515
|
+
emitRefreshPublicationSummary(options, synchronizations);
|
|
516
|
+
return buildEvidence();
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function emitRefreshPublicationSummary(
|
|
520
|
+
options: ClientDiagnosticRefreshOptions,
|
|
521
|
+
synchronizations: readonly DiagnosticSynchronization[],
|
|
522
|
+
): void {
|
|
538
523
|
options.publications.emitSummary({
|
|
539
524
|
operation: "refresh-open",
|
|
540
|
-
identity: {
|
|
541
|
-
server: options.host.server,
|
|
542
|
-
cwd: options.host.cwd,
|
|
543
|
-
},
|
|
525
|
+
identity: { server: options.host.server, cwd: options.host.cwd },
|
|
544
526
|
synchronizations: synchronizations.map((synchronization) => ({
|
|
545
527
|
uri: synchronization.uri,
|
|
546
528
|
synchronizationId: synchronization.synchronizationId,
|
|
@@ -553,139 +535,56 @@ export async function refreshClientOpenDiagnostics(
|
|
|
553
535
|
})),
|
|
554
536
|
operationId: options.options.operationId,
|
|
555
537
|
});
|
|
556
|
-
return buildEvidence(quietMs);
|
|
557
538
|
}
|
|
558
539
|
|
|
559
|
-
/**
|
|
560
|
-
* Pull diagnostic evidence for every synchronized document, or fall through
|
|
561
|
-
* to the push settle path when any pull fails. An interruption during the
|
|
562
|
-
* pull phase stops the refresh instead of degrading into failed coverage.
|
|
563
|
-
*/
|
|
540
|
+
/** Collect request evidence for every applicable synchronized document. */
|
|
564
541
|
async function collectPullEvidenceForRefresh(options: {
|
|
565
542
|
options: ClientDiagnosticRefreshOptions;
|
|
566
543
|
synchronizations: readonly DiagnosticSynchronization[];
|
|
567
544
|
syncStart: number;
|
|
568
545
|
maxWaitMs: number;
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const { options: refresh, synchronizations, syncStart, maxWaitMs, observer } = options;
|
|
546
|
+
}): Promise<{ completed: boolean; failedPullUris: ReadonlySet<string>; timedOut: boolean }> {
|
|
547
|
+
const { options: refresh, synchronizations, syncStart, maxWaitMs } = options;
|
|
572
548
|
try {
|
|
573
|
-
await pullDiagnosticsForOpenDocuments({
|
|
549
|
+
const result = await pullDiagnosticsForOpenDocuments({
|
|
574
550
|
requests: synchronizations,
|
|
575
551
|
syncStart,
|
|
576
552
|
maxWaitMs,
|
|
577
553
|
signal: refresh.options.signal,
|
|
578
554
|
deadline: refresh.options.deadline,
|
|
579
555
|
operationId: refresh.options.operationId,
|
|
580
|
-
currentEvidenceRevision: refresh.evidenceRevision,
|
|
581
|
-
openDocuments: refresh.openDocuments,
|
|
582
|
-
diagnosticStore: refresh.diagnosticStore,
|
|
583
|
-
waitForChange: () => refresh.waiters.waitForChange(),
|
|
584
556
|
pullDiagnostics: (pullOptions) =>
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
request: {
|
|
592
|
-
uri: pullOptions.request.uri,
|
|
593
|
-
timeoutMs: pullOptions.timeoutMs,
|
|
594
|
-
synchronizationId: pullOptions.request.synchronizationId,
|
|
595
|
-
evidenceRevision:
|
|
596
|
-
refresh.openDocuments.get(pullOptions.request.uri)?.evidenceRevision ??
|
|
597
|
-
refresh.evidenceRevision(),
|
|
598
|
-
signal: pullOptions.signal,
|
|
599
|
-
deadline: pullOptions.deadline,
|
|
600
|
-
operationId: pullOptions.operationId,
|
|
601
|
-
},
|
|
557
|
+
refresh.requestDiagnostics({
|
|
558
|
+
request: pullOptions.request,
|
|
559
|
+
timeoutMs: pullOptions.timeoutMs,
|
|
560
|
+
signal: pullOptions.signal,
|
|
561
|
+
deadline: pullOptions.deadline,
|
|
562
|
+
operationId: pullOptions.operationId,
|
|
602
563
|
}),
|
|
603
564
|
});
|
|
604
|
-
|
|
605
|
-
|
|
565
|
+
const failedPullUris = new Set(result.failedUris);
|
|
566
|
+
const incomplete =
|
|
567
|
+
result.incompleteUris.length > 0 || failedPullUris.size > 0 || result.timedOut;
|
|
568
|
+
return {
|
|
569
|
+
completed: !incomplete,
|
|
570
|
+
failedPullUris,
|
|
571
|
+
timedOut: result.timedOut,
|
|
572
|
+
};
|
|
606
573
|
} catch (error) {
|
|
607
|
-
observer.pullFailed(error);
|
|
608
|
-
if (error instanceof DiagnosticPullError) {
|
|
609
|
-
return { completed: false, failedPullUris: new Set(error.failedUris) };
|
|
610
|
-
}
|
|
611
574
|
if (isCodeRequestInterruption(error, refresh.options)) throw error;
|
|
612
|
-
return { completed: false, failedPullUris: new Set() };
|
|
575
|
+
return { completed: false, failedPullUris: new Set(), timedOut: false };
|
|
613
576
|
}
|
|
614
577
|
}
|
|
615
578
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
*
|
|
625
|
-
* Only documents this pass resynchronized are candidates: retained documents
|
|
626
|
-
* wait for the server's existing pipeline, and reopening them would cancel
|
|
627
|
-
* in-progress server work without fixing any publish gap (#344).
|
|
628
|
-
*/
|
|
629
|
-
async function reopenUnconfirmedDocuments(options: {
|
|
630
|
-
options: ClientDiagnosticRefreshOptions;
|
|
631
|
-
synchronizations: readonly DiagnosticSynchronization[];
|
|
632
|
-
/** URIs that received a didChange in this pass and may need a reopen push. */
|
|
633
|
-
reopenCandidates: ReadonlySet<string>;
|
|
634
|
-
observer: DiagnosticObserver;
|
|
635
|
-
}): Promise<{
|
|
636
|
-
performed: boolean;
|
|
637
|
-
startedAt: number;
|
|
638
|
-
synchronizations: DiagnosticSynchronization[];
|
|
639
|
-
}> {
|
|
640
|
-
const { options: refresh, synchronizations, reopenCandidates, observer } = options;
|
|
641
|
-
const startedAt = Date.now();
|
|
642
|
-
const unconfirmed = synchronizations.filter(
|
|
643
|
-
(item) =>
|
|
644
|
-
reopenCandidates.has(item.uri) &&
|
|
645
|
-
// A document with any current publication — tentative included — is
|
|
646
|
-
// not a reopen candidate: its server pipeline is alive and a republish
|
|
647
|
-
// can still promote the retained cache (ADR 0021).
|
|
648
|
-
!hasCurrentEvidence(refresh.diagnosticStore, item, refresh.evidenceRevision()),
|
|
579
|
+
function requestSourceFor(
|
|
580
|
+
host: ClientDiagnosticsHost,
|
|
581
|
+
synchronizations: readonly DiagnosticSynchronization[],
|
|
582
|
+
): "pull" | "typescript" | "mixed" {
|
|
583
|
+
const sources = new Set(
|
|
584
|
+
synchronizations.map(
|
|
585
|
+
(synchronization) => host.diagnosticRequestAdapter.sourceFor(synchronization.uri) ?? "pull",
|
|
586
|
+
),
|
|
649
587
|
);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const document = refresh.openDocuments.get(item.uri);
|
|
653
|
-
if (!document) continue;
|
|
654
|
-
const filePath = uriToFile(item.uri);
|
|
655
|
-
let content: string;
|
|
656
|
-
try {
|
|
657
|
-
content = readFileSync(filePath, "utf-8");
|
|
658
|
-
} catch {
|
|
659
|
-
// The file disappeared mid-refresh; keep the document as-is and
|
|
660
|
-
// report its current unconfirmed coverage.
|
|
661
|
-
continue;
|
|
662
|
-
}
|
|
663
|
-
reopenDocument({
|
|
664
|
-
uri: item.uri,
|
|
665
|
-
content,
|
|
666
|
-
document,
|
|
667
|
-
languageId: detectLanguageId(filePath),
|
|
668
|
-
nextVersion: () => nextDocumentVersion(refresh.versionHistory, item.uri),
|
|
669
|
-
nextSynchronizationId: refresh.nextSynchronizationId,
|
|
670
|
-
evidenceRevision: refresh.evidenceRevision(),
|
|
671
|
-
waiters: refresh.waiters,
|
|
672
|
-
sendNotification: (method, params) => refresh.host.sendNotification(method, params),
|
|
673
|
-
markUnversionedSyncMoment: () => refresh.markUnversionedSyncMoment(item.uri),
|
|
674
|
-
});
|
|
675
|
-
reopenedSynchronizations.push({
|
|
676
|
-
uri: item.uri,
|
|
677
|
-
synchronizationId: document.synchronizationId,
|
|
678
|
-
evidenceRevision: document.evidenceRevision,
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
if (reopenedSynchronizations.length === 0) {
|
|
682
|
-
return { performed: false, startedAt, synchronizations: [...synchronizations] };
|
|
683
|
-
}
|
|
684
|
-
observer.reopened(reopenedSynchronizations.length);
|
|
685
|
-
const reopenedByUri = new Map(reopenedSynchronizations.map((item) => [item.uri, item]));
|
|
686
|
-
return {
|
|
687
|
-
performed: true,
|
|
688
|
-
startedAt,
|
|
689
|
-
synchronizations: synchronizations.map((item) => reopenedByUri.get(item.uri) ?? item),
|
|
690
|
-
};
|
|
588
|
+
if (sources.size === 1) return sources.values().next().value as "pull" | "typescript";
|
|
589
|
+
return "mixed";
|
|
691
590
|
}
|