@mrclrchtr/supi-code-intelligence 5.0.0 → 6.0.1
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 +4 -2
- package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
- package/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/README.md +36 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- 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-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +12 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +67 -28
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +63 -6
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +46 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +214 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +278 -79
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +29 -8
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +101 -19
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +19 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +49 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +73 -15
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +5 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-extends.ts +68 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-path.ts +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +176 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +4 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +46 -10
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +4 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +65 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +143 -21
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +43 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- 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-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +58 -126
- package/src/analysis/health/file-scope.ts +184 -0
- package/src/analysis/health/recovery.ts +8 -1
- package/src/analysis/readiness.ts +4 -0
- package/src/analysis/search/ast-scan.ts +4 -1
- package/src/analysis/search/pattern-analysis.ts +10 -3
- package/src/analysis/search/pattern.ts +6 -1
- package/src/config.ts +35 -1
- package/src/extension.ts +57 -14
- package/src/overview/overview-data.ts +9 -2
- package/src/overview/overview.ts +49 -30
- package/src/overview/types.ts +2 -0
- package/src/session/find-types.ts +2 -2
- package/src/session/find-workflow.ts +3 -2
- package/src/session/health-refresh.ts +76 -49
- package/src/session/health-types.ts +7 -1
- package/src/session/health-workflow.ts +35 -18
- package/src/session/input/workflows.ts +2 -2
- package/src/session/inspect-workflow.ts +2 -1
- package/src/session/orientation/collect.ts +2 -2
- package/src/session/orientation-types.ts +2 -0
- package/src/session/orientation-workflow.ts +8 -2
- package/src/session/refactor-workflow.ts +2 -1
- package/src/session/session.ts +25 -1
- package/src/substrate/lsp/maintenance.ts +105 -23
- package/src/substrate/lsp/recovery.ts +11 -5
- package/src/tool/code_find/execute.ts +23 -0
- package/src/tool/code_find/guidance.ts +8 -0
- package/src/tool/{find → code_find}/render.ts +1 -1
- package/src/tool/{result/find.ts → code_find/result.ts} +36 -11
- package/src/tool/code_find/spec.ts +37 -0
- package/src/tool/code_graph/execute.ts +24 -0
- package/src/tool/code_graph/guidance.ts +6 -0
- package/src/tool/{graph → code_graph}/markdown.ts +1 -1
- package/src/tool/{result/graph.ts → code_graph/result.ts} +69 -4
- package/src/tool/code_graph/spec.ts +39 -0
- package/src/tool/{health → code_health}/execute.ts +2 -18
- package/src/tool/code_health/guidance.ts +8 -0
- package/src/tool/{health → code_health}/markdown.ts +68 -18
- package/src/tool/{result/health.ts → code_health/result.ts} +29 -4
- package/src/tool/code_health/spec.ts +41 -0
- package/src/tool/code_inspect/execute.ts +20 -0
- package/src/tool/code_inspect/guidance.ts +6 -0
- package/src/tool/{inspect → code_inspect}/markdown.ts +1 -1
- package/src/tool/{result/inspect.ts → code_inspect/result.ts} +32 -3
- package/src/tool/code_inspect/spec.ts +25 -0
- package/src/tool/code_orientation/execute.ts +25 -0
- package/src/tool/code_orientation/guidance.ts +9 -0
- package/src/tool/{orientation → code_orientation}/markdown.ts +1 -1
- package/src/tool/{result/orientation.ts → code_orientation/result.ts} +67 -3
- package/src/tool/code_orientation/spec.ts +25 -0
- package/src/tool/code_refactor_apply/execute.ts +17 -0
- package/src/tool/code_refactor_apply/guidance.ts +5 -0
- package/src/tool/{refactor-apply/execute.ts → code_refactor_apply/result.ts} +5 -11
- package/src/tool/code_refactor_apply/spec.ts +26 -0
- package/src/tool/code_refactor_plan/execute.ts +27 -0
- package/src/tool/code_refactor_plan/guidance.ts +8 -0
- package/src/tool/{refactor-plan/execute.ts → code_refactor_plan/result.ts} +9 -22
- package/src/tool/code_refactor_plan/spec.ts +25 -0
- package/src/tool/code_resolve/execute.ts +19 -0
- package/src/tool/code_resolve/guidance.ts +8 -0
- package/src/tool/{resolve → code_resolve}/markdown.ts +1 -1
- package/src/tool/{result/resolve.ts → code_resolve/result.ts} +37 -24
- package/src/tool/code_resolve/spec.ts +25 -0
- package/src/tool/guidance.ts +71 -52
- package/src/tool/infra/truncate.ts +40 -0
- package/src/tool/{refactor-plan/markdown.ts → refactor-markdown.ts} +4 -7
- package/src/tool/register.ts +9 -97
- package/src/tool/result/refactor.ts +1 -4
- package/src/tool/schemas.ts +26 -157
- package/src/tool/specs.ts +40 -126
- package/src/tool/find/execute.ts +0 -43
- package/src/tool/graph/execute.ts +0 -82
- package/src/tool/inspect/execute.ts +0 -40
- package/src/tool/orientation/execute.ts +0 -80
- package/src/tool/resolve/execute.ts +0 -53
- /package/src/tool/{find → code_find}/ast-kinds.ts +0 -0
- /package/src/tool/{find → code_find}/markdown.ts +0 -0
- /package/src/tool/{find → code_find}/modes.ts +0 -0
- /package/src/tool/{find → code_find}/tui.ts +0 -0
- /package/src/tool/{graph → code_graph}/tui.ts +0 -0
- /package/src/tool/{health → code_health}/refresh-status.ts +0 -0
- /package/src/tool/{health → code_health}/semantic-state.ts +0 -0
- /package/src/tool/{health → code_health}/tui.ts +0 -0
- /package/src/tool/{inspect → code_inspect}/tui.ts +0 -0
- /package/src/tool/{orientation → code_orientation}/tui.ts +0 -0
- /package/src/tool/{refactor-apply → code_refactor_apply}/tui.ts +0 -0
- /package/src/tool/{refactor-plan → code_refactor_plan}/tui.ts +0 -0
- /package/src/tool/{resolve → code_resolve}/tui.ts +0 -0
|
@@ -163,6 +163,25 @@ export interface DiagnosticCacheEntry {
|
|
|
163
163
|
evidenceRevision?: number;
|
|
164
164
|
version?: number;
|
|
165
165
|
resultId?: string;
|
|
166
|
+
/**
|
|
167
|
+
* Valid publications observed for the entry's synchronization. The first
|
|
168
|
+
* valid publication for a synchronization is tentative; a later valid
|
|
169
|
+
* publication promotes the entry to confirmed (ADR 0021). A confirmed
|
|
170
|
+
* pull entry counts as the first publication when a push continues the
|
|
171
|
+
* same synchronization, so that push keeps the confirmation. Pull
|
|
172
|
+
* entries as such carry no count and are confirmed on arrival.
|
|
173
|
+
*/
|
|
174
|
+
publications?: number;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Test whether one push entry is still tentative for its synchronization.
|
|
179
|
+
*
|
|
180
|
+
* A pull entry confirms on arrival; a push entry confirms only after a
|
|
181
|
+
* later valid publication for the same synchronization (ADR 0021).
|
|
182
|
+
*/
|
|
183
|
+
export function isTentativePushEntry(entry: DiagnosticCacheEntry | undefined): boolean {
|
|
184
|
+
return Boolean(entry && entry.source === "push" && (entry.publications ?? 1) < 2);
|
|
166
185
|
}
|
|
167
186
|
|
|
168
187
|
/** One document synchronization that needs current diagnostic evidence. */
|
|
@@ -204,6 +223,7 @@ export function hasFreshPush(
|
|
|
204
223
|
const entry = store.get(synchronization.uri);
|
|
205
224
|
return Boolean(
|
|
206
225
|
entry?.source === "push" &&
|
|
226
|
+
!isTentativePushEntry(entry) &&
|
|
207
227
|
entry.synchronizationId === synchronization.synchronizationId &&
|
|
208
228
|
(synchronization.evidenceRevision === undefined ||
|
|
209
229
|
entry.evidenceRevision === synchronization.evidenceRevision) &&
|
|
@@ -211,8 +231,11 @@ export function hasFreshPush(
|
|
|
211
231
|
);
|
|
212
232
|
}
|
|
213
233
|
|
|
214
|
-
/**
|
|
215
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Test whether stored evidence matches a synchronization without a
|
|
236
|
+
* confirmation gate. A tentative push matches here but cannot confirm.
|
|
237
|
+
*/
|
|
238
|
+
export function hasCurrentEvidence(
|
|
216
239
|
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
217
240
|
synchronization: DiagnosticSynchronization,
|
|
218
241
|
currentEvidenceRevision?: number,
|
|
@@ -227,8 +250,26 @@ export function hasFreshEvidence(
|
|
|
227
250
|
);
|
|
228
251
|
}
|
|
229
252
|
|
|
230
|
-
/**
|
|
231
|
-
export function
|
|
253
|
+
/** Test whether pull or push evidence confirms the supplied synchronization. */
|
|
254
|
+
export function hasFreshEvidence(
|
|
255
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
256
|
+
synchronization: DiagnosticSynchronization,
|
|
257
|
+
currentEvidenceRevision?: number,
|
|
258
|
+
): boolean {
|
|
259
|
+
return (
|
|
260
|
+
hasCurrentEvidence(store, synchronization, currentEvidenceRevision) &&
|
|
261
|
+
!isTentativePushEntry(store.get(synchronization.uri))
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Return the latest cache update that matches one of the synchronizations.
|
|
267
|
+
*
|
|
268
|
+
* Every accepted publication restarts the settle quiet period, tentative
|
|
269
|
+
* publications included, so this helper does not apply the confirmation
|
|
270
|
+
* gate (ADR 0021).
|
|
271
|
+
*/
|
|
272
|
+
export function latestCurrentEvidenceReceivedAt(
|
|
232
273
|
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
233
274
|
synchronizations: DiagnosticSynchronization[],
|
|
234
275
|
currentEvidenceRevision?: number,
|
|
@@ -236,7 +277,7 @@ export function latestFreshEvidenceReceivedAt(
|
|
|
236
277
|
let latest = 0;
|
|
237
278
|
for (const synchronization of synchronizations) {
|
|
238
279
|
const entry = store.get(synchronization.uri);
|
|
239
|
-
if (
|
|
280
|
+
if (hasCurrentEvidence(store, synchronization, currentEvidenceRevision)) {
|
|
240
281
|
latest = Math.max(latest, entry?.receivedAt ?? 0);
|
|
241
282
|
}
|
|
242
283
|
}
|
|
@@ -9,6 +9,7 @@ export interface ClientDiagnosticsHost {
|
|
|
9
9
|
cwd?: string;
|
|
10
10
|
isOperational(): boolean;
|
|
11
11
|
supportsPullDiagnostics(): boolean;
|
|
12
|
+
usesIncrementalDocumentSync(): boolean;
|
|
12
13
|
sendNotification(method: string, params: unknown): void;
|
|
13
14
|
pullDocumentDiagnostics(request: DiagnosticPullRequest): Promise<DocumentDiagnosticReport | null>;
|
|
14
15
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Bounded push-publication telemetry for one client's diagnostic state.
|
|
2
|
+
//
|
|
3
|
+
// Push-only servers can publish an early result and a later semantic result
|
|
4
|
+
// for one document synchronization (ADR 0021). This tracker records one
|
|
5
|
+
// bounded per-synchronization publication summary per diagnostic operation
|
|
6
|
+
// and one ambient event when a later publication promotes a synchronization
|
|
7
|
+
// that an earlier operation already reported unconfirmed. Events carry only
|
|
8
|
+
// bounded server, workspace, relative-file, synchronization identity, count,
|
|
9
|
+
// and timing data. They never carry diagnostic payloads or source text.
|
|
10
|
+
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { recordDebugEvent } from "@mrclrchtr/supi-core/debug";
|
|
13
|
+
import { boundCwd, truncateIdentity } from "../debug-telemetry.ts";
|
|
14
|
+
import { uriToFile } from "../utils.ts";
|
|
15
|
+
|
|
16
|
+
/** Maximum tracked synchronizations before the oldest entry is evicted. */
|
|
17
|
+
export const MAX_TRACKED_SYNCHRONIZATIONS = 64;
|
|
18
|
+
|
|
19
|
+
/** Maximum synchronization entries in one bounded publication summary. */
|
|
20
|
+
export const MAX_SUMMARY_SYNCHRONIZATIONS = 16;
|
|
21
|
+
|
|
22
|
+
/** Maximum publication count retained for telemetry; 2 means two or more. */
|
|
23
|
+
const MAX_PUBLICATIONS = 2;
|
|
24
|
+
|
|
25
|
+
/** Bounded identity for one publication telemetry observation. */
|
|
26
|
+
export interface DiagnosticPublicationIdentity {
|
|
27
|
+
/** Configured server name. */
|
|
28
|
+
readonly server?: string;
|
|
29
|
+
/** Absolute workspace root. */
|
|
30
|
+
readonly cwd?: string;
|
|
31
|
+
/** Workspace-relative file path; sync-file operations only. */
|
|
32
|
+
readonly file?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** One awaited synchronization of a completed diagnostic operation. */
|
|
36
|
+
export interface DiagnosticPublicationSynchronization {
|
|
37
|
+
readonly uri: string;
|
|
38
|
+
readonly synchronizationId: number;
|
|
39
|
+
readonly evidenceRevision: number;
|
|
40
|
+
readonly confirmed: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** One bounded per-synchronization publication summary entry. */
|
|
44
|
+
export interface DiagnosticPublicationSummaryEntry {
|
|
45
|
+
readonly synchronizationId: number;
|
|
46
|
+
readonly publications: number;
|
|
47
|
+
readonly firstReceivedAt: number;
|
|
48
|
+
readonly lastReceivedAt: number;
|
|
49
|
+
readonly confirmed: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface SynchronizationPublicationState {
|
|
53
|
+
readonly uri: string;
|
|
54
|
+
readonly synchronizationId: number;
|
|
55
|
+
readonly evidenceRevision: number;
|
|
56
|
+
publications: number;
|
|
57
|
+
firstReceivedAt: number;
|
|
58
|
+
lastReceivedAt: number;
|
|
59
|
+
/** When a finished operation first reported this synchronization unconfirmed. */
|
|
60
|
+
unconfirmedAt?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function synchronizationKey(
|
|
64
|
+
uri: string,
|
|
65
|
+
synchronizationId: number,
|
|
66
|
+
evidenceRevision: number,
|
|
67
|
+
): string {
|
|
68
|
+
return `${uri}|${synchronizationId}|${evidenceRevision}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Track bounded push-publication counts for one LSP client. */
|
|
72
|
+
export class DiagnosticPublicationTracker {
|
|
73
|
+
readonly #states = new Map<string, SynchronizationPublicationState>();
|
|
74
|
+
|
|
75
|
+
constructor(
|
|
76
|
+
private readonly identity: { server?: string; cwd?: string },
|
|
77
|
+
private readonly fileFor: (uri: string) => string | undefined,
|
|
78
|
+
) {}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Record one accepted push publication for a synchronization.
|
|
82
|
+
*
|
|
83
|
+
* The state map is bounded to {@link MAX_TRACKED_SYNCHRONIZATIONS}; the
|
|
84
|
+
* oldest tracked synchronization is evicted when the bound is exceeded.
|
|
85
|
+
*/
|
|
86
|
+
record(
|
|
87
|
+
uri: string,
|
|
88
|
+
synchronizationId: number,
|
|
89
|
+
evidenceRevision: number,
|
|
90
|
+
receivedAt: number = Date.now(),
|
|
91
|
+
): void {
|
|
92
|
+
const key = synchronizationKey(uri, synchronizationId, evidenceRevision);
|
|
93
|
+
const existing = this.#states.get(key);
|
|
94
|
+
if (existing) {
|
|
95
|
+
existing.publications = Math.min(existing.publications + 1, MAX_PUBLICATIONS);
|
|
96
|
+
existing.lastReceivedAt = receivedAt;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
this.#states.set(key, {
|
|
100
|
+
uri,
|
|
101
|
+
synchronizationId,
|
|
102
|
+
evidenceRevision,
|
|
103
|
+
publications: 1,
|
|
104
|
+
firstReceivedAt: receivedAt,
|
|
105
|
+
lastReceivedAt: receivedAt,
|
|
106
|
+
});
|
|
107
|
+
if (this.#states.size > MAX_TRACKED_SYNCHRONIZATIONS) {
|
|
108
|
+
const oldest = this.#states.keys().next().value;
|
|
109
|
+
if (oldest !== undefined) this.#states.delete(oldest);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Emit one bounded per-synchronization publication summary.
|
|
115
|
+
*
|
|
116
|
+
* Synchronizations with no observed push publication are omitted. A
|
|
117
|
+
* synchronization that ends unconfirmed is marked so a later promotion can
|
|
118
|
+
* emit the ambient late-republish event.
|
|
119
|
+
*/
|
|
120
|
+
emitSummary(options: {
|
|
121
|
+
readonly operation: "refresh-open" | "sync-file";
|
|
122
|
+
readonly identity: DiagnosticPublicationIdentity;
|
|
123
|
+
readonly synchronizations: readonly DiagnosticPublicationSynchronization[];
|
|
124
|
+
readonly operationId?: string;
|
|
125
|
+
}): void {
|
|
126
|
+
const entries: DiagnosticPublicationSummaryEntry[] = [];
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
for (const synchronization of options.synchronizations) {
|
|
129
|
+
const state = this.#states.get(
|
|
130
|
+
synchronizationKey(
|
|
131
|
+
synchronization.uri,
|
|
132
|
+
synchronization.synchronizationId,
|
|
133
|
+
synchronization.evidenceRevision,
|
|
134
|
+
),
|
|
135
|
+
);
|
|
136
|
+
if (!state) continue;
|
|
137
|
+
entries.push({
|
|
138
|
+
synchronizationId: state.synchronizationId,
|
|
139
|
+
publications: state.publications,
|
|
140
|
+
firstReceivedAt: state.firstReceivedAt,
|
|
141
|
+
lastReceivedAt: state.lastReceivedAt,
|
|
142
|
+
confirmed: synchronization.confirmed,
|
|
143
|
+
});
|
|
144
|
+
if (!synchronization.confirmed) state.unconfirmedAt ??= now;
|
|
145
|
+
else state.unconfirmedAt = undefined;
|
|
146
|
+
}
|
|
147
|
+
if (entries.length === 0) return;
|
|
148
|
+
recordDebugEvent({
|
|
149
|
+
operationId: options.operationId,
|
|
150
|
+
source: "lsp",
|
|
151
|
+
level: "debug",
|
|
152
|
+
category: "diagnostics.publication",
|
|
153
|
+
message: "LSP diagnostic publication summary",
|
|
154
|
+
cwd: boundCwd(options.identity.cwd),
|
|
155
|
+
data: {
|
|
156
|
+
operation: options.operation,
|
|
157
|
+
synchronizations: entries.slice(0, MAX_SUMMARY_SYNCHRONIZATIONS),
|
|
158
|
+
...(options.identity.server !== undefined
|
|
159
|
+
? { server: truncateIdentity(options.identity.server) }
|
|
160
|
+
: {}),
|
|
161
|
+
...(options.identity.file !== undefined
|
|
162
|
+
? { file: truncateIdentity(options.identity.file) }
|
|
163
|
+
: {}),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Observe one promotion of a synchronization by a later publication.
|
|
170
|
+
*
|
|
171
|
+
* The ambient late-republish event fires only when a finished operation
|
|
172
|
+
* previously reported the synchronization unconfirmed. The mark is cleared
|
|
173
|
+
* so one promotion emits at most one event.
|
|
174
|
+
*/
|
|
175
|
+
promoted(
|
|
176
|
+
uri: string,
|
|
177
|
+
synchronizationId: number,
|
|
178
|
+
evidenceRevision: number,
|
|
179
|
+
receivedAt: number = Date.now(),
|
|
180
|
+
): void {
|
|
181
|
+
const state = this.#states.get(synchronizationKey(uri, synchronizationId, evidenceRevision));
|
|
182
|
+
const unconfirmedAt = state?.unconfirmedAt;
|
|
183
|
+
if (!state || state.publications < 2 || unconfirmedAt === undefined) return;
|
|
184
|
+
state.unconfirmedAt = undefined;
|
|
185
|
+
const file = this.fileFor(uri);
|
|
186
|
+
recordDebugEvent({
|
|
187
|
+
source: "lsp",
|
|
188
|
+
level: "debug",
|
|
189
|
+
category: "diagnostics.publication",
|
|
190
|
+
message: "LSP diagnostic late republish",
|
|
191
|
+
cwd: boundCwd(this.identity.cwd),
|
|
192
|
+
data: {
|
|
193
|
+
synchronizationId,
|
|
194
|
+
publications: state.publications,
|
|
195
|
+
receivedAt,
|
|
196
|
+
delayMs: Math.max(0, receivedAt - unconfirmedAt),
|
|
197
|
+
...(this.identity.server !== undefined
|
|
198
|
+
? { server: truncateIdentity(this.identity.server) }
|
|
199
|
+
: {}),
|
|
200
|
+
...(file !== undefined ? { file: truncateIdentity(file) } : {}),
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Return a workspace-relative diagnostic file path for telemetry identity. */
|
|
207
|
+
export function trackerFileIdentityFor(
|
|
208
|
+
cwd: string | undefined,
|
|
209
|
+
): (uri: string) => string | undefined {
|
|
210
|
+
return (uri) => {
|
|
211
|
+
if (cwd === undefined) return undefined;
|
|
212
|
+
return path.relative(cwd, path.resolve(cwd, uriToFile(uri)));
|
|
213
|
+
};
|
|
214
|
+
}
|