@mrclrchtr/supi-code-intelligence 4.9.0 → 5.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 +2 -1
- 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-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/README.md +80 -5
- 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-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -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 +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/package.json +7 -6
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +10 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +206 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +78 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +114 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +372 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +14 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +62 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +491 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +215 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +206 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +370 -266
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +52 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +185 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-file-state.ts +25 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +467 -115
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +238 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +10 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +38 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +79 -9
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +13 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +300 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +464 -96
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-refactor-provider.ts +130 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +45 -329
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/refactor-planning.ts +57 -120
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-edit-normalizer.ts +320 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +207 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/readiness.ts +72 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +151 -26
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +40 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +50 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +136 -120
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +49 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +102 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +26 -6
- 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-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -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 +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/LICENSE +21 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/README.md +258 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/babel.cjs +257 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/jiti.cjs +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-cli.mjs +34 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-hooks.mjs +124 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-native.mjs +121 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.d.mts +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.mjs +4 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-static.mjs +23 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.cjs +30 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.cts +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.mts +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.mjs +29 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/types.d.ts +420 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/package.json +146 -0
- 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 +7 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +0 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +16 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +65 -87
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +47 -95
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-timing.ts +147 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client-lifecycle.ts +39 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +561 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-message-size.ts +24 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +452 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +12 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/imports.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +5 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +33 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/blocking-test-bootstrap.mjs +34 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/bootstrap.mjs +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store-helpers.ts +23 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +400 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/request-control.ts +48 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-parser-helpers.ts +24 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-query-helpers.ts +43 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +421 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +116 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +201 -0
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +236 -24
- package/src/analysis/health/recovery.ts +6 -2
- package/src/analysis/provider.ts +58 -0
- package/src/analysis/readiness.ts +30 -9
- package/src/analysis/refactor/apply.ts +59 -20
- package/src/analysis/refactor/mutation-authority.ts +143 -0
- package/src/analysis/refactor/position.ts +47 -0
- package/src/analysis/refactor/safety.ts +18 -10
- package/src/analysis/relations/callees.ts +9 -1
- package/src/analysis/relations/types.ts +5 -2
- package/src/analysis/search/ast-scan-timing.ts +9 -4
- package/src/analysis/search/ast-scan.ts +1 -1
- package/src/analysis/search/deadline.ts +2 -2
- package/src/analysis/search/pattern-analysis.ts +46 -32
- package/src/analysis/search/pattern.ts +12 -2
- package/src/analysis/target/anchored.ts +20 -11
- package/src/analysis/target/file.ts +18 -11
- package/src/analysis/target/identity.ts +19 -3
- package/src/analysis/target/symbol.ts +36 -15
- package/src/api.ts +4 -0
- package/src/diagnostics/evidence.ts +51 -0
- package/src/session/capability-adapter.ts +12 -3
- package/src/session/find-workflow.ts +17 -7
- package/src/session/graph/collect.ts +18 -2
- package/src/session/graph-workflow.ts +19 -12
- package/src/session/health-refresh.ts +148 -0
- package/src/session/health-types.ts +37 -15
- package/src/session/health-workflow.ts +37 -39
- package/src/session/inspect/collect.ts +20 -3
- package/src/session/inspect-workflow.ts +10 -5
- package/src/session/orientation/collect.ts +10 -1
- package/src/session/orientation/context-facts.ts +3 -1
- package/src/session/orientation/context-sections.ts +20 -7
- package/src/session/orientation/gather.ts +17 -11
- package/src/session/orientation-types.ts +2 -1
- package/src/session/orientation-workflow.ts +15 -8
- package/src/session/refactor-plans.ts +2 -0
- package/src/session/refactor-workflow.ts +59 -14
- package/src/session/session.ts +4 -1
- package/src/session/target-workflow.ts +54 -23
- package/src/session/workflow-control.ts +7 -3
- package/src/substrate/lsp/lifecycle.ts +26 -46
- package/src/substrate/lsp/maintenance.ts +199 -25
- package/src/substrate/lsp/state.ts +2 -2
- package/src/substrate/workspace-provider-host.ts +25 -3
- package/src/tool/find/execute.ts +7 -1
- package/src/tool/find/render.ts +20 -3
- package/src/tool/find/tui.ts +23 -21
- package/src/tool/graph/execute.ts +23 -3
- package/src/tool/graph/tui.ts +59 -46
- package/src/tool/health/execute.ts +7 -2
- package/src/tool/health/markdown.ts +56 -14
- package/src/tool/health/refresh-status.ts +105 -0
- package/src/tool/health/tui.ts +95 -98
- package/src/tool/infra/workflow-control.ts +8 -0
- package/src/tool/inspect/execute.ts +7 -1
- package/src/tool/inspect/tui.ts +26 -18
- package/src/tool/orientation/execute.ts +21 -3
- package/src/tool/orientation/tui.ts +51 -35
- package/src/tool/refactor-apply/execute.ts +3 -0
- package/src/tool/refactor-apply/tui.ts +43 -25
- package/src/tool/refactor-plan/execute.ts +4 -0
- package/src/tool/refactor-plan/tui.ts +33 -20
- package/src/tool/register.ts +127 -22
- package/src/tool/resolve/execute.ts +18 -1
- package/src/tool/resolve/tui.ts +42 -24
- package/src/tool/result/display.ts +107 -0
- package/src/tool/result/errors.ts +20 -17
- package/src/tool/result/find.ts +52 -8
- package/src/tool/result/graph.ts +44 -1
- package/src/tool/result/health.ts +53 -1
- package/src/tool/result/inspect.ts +113 -1
- package/src/tool/result/orientation.ts +66 -14
- package/src/tool/result/refactor.ts +36 -4
- package/src/tool/result/resolve.ts +44 -7
- package/src/tool/result/types.ts +20 -0
- package/src/types/execution.ts +18 -5
- package/src/types/index.ts +8 -1
- package/src/ui/status-command.ts +5 -3
- package/src/ui/status-overlay.ts +4 -0
- package/src/ui/tui/common.ts +108 -10
- package/src/ui/tui/display.ts +48 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +0 -247
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import type { PublishDiagnosticsParams } from "../config/types.ts";
|
|
2
|
+
import { uriToFile } from "../utils.ts";
|
|
3
|
+
import {
|
|
4
|
+
type DiagnosticCacheEntry,
|
|
5
|
+
isValidPublishDiagnosticsParams,
|
|
6
|
+
} from "./client-diagnostic-evidence.ts";
|
|
7
|
+
import type { ClientDiagnosticSnapshot, OpenDocumentState } from "./client-document-state.ts";
|
|
8
|
+
import { hasCurrentDiagnosticEvidence } from "./client-document-state.ts";
|
|
9
|
+
import { getDiagnosticFileState } from "./client-file-state.ts";
|
|
10
|
+
|
|
11
|
+
interface ApplyPushOptions {
|
|
12
|
+
store: Map<string, DiagnosticCacheEntry>;
|
|
13
|
+
openDocuments: ReadonlyMap<string, OpenDocumentState>;
|
|
14
|
+
params: PublishDiagnosticsParams;
|
|
15
|
+
evidenceRevision: number;
|
|
16
|
+
/** Client-side sync moment for the URI, or undefined when never synchronized. */
|
|
17
|
+
unversionedSyncMoment: number | undefined;
|
|
18
|
+
/** Whether a lifecycle close marked this URI fail-closed for versioned pushes. */
|
|
19
|
+
closedVersionedBarrier: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Apply one valid push publication and return whether it can release current waiters.
|
|
24
|
+
*
|
|
25
|
+
* Fail-closed policy (ADR 0020): unversioned pushes are rejected for closed
|
|
26
|
+
* and untracked URIs and for arrivals before the URI's sync moment; versioned
|
|
27
|
+
* pushes for a URI closed by a lifecycle operation are rejected because their
|
|
28
|
+
* version cannot be verified. An unversioned push that arrives after the sync
|
|
29
|
+
* moment of an open document is accepted and re-stamped with that document's
|
|
30
|
+
* current synchronization state.
|
|
31
|
+
*/
|
|
32
|
+
export function applyPushDiagnostics(options: ApplyPushOptions): boolean {
|
|
33
|
+
if (!isValidPublishDiagnosticsParams(options.params)) return false;
|
|
34
|
+
const openDocument = options.openDocuments.get(options.params.uri);
|
|
35
|
+
if (options.params.version !== undefined) {
|
|
36
|
+
if (!Number.isInteger(options.params.version)) return false;
|
|
37
|
+
if (openDocument && options.params.version !== openDocument.version) return false;
|
|
38
|
+
if (!openDocument && options.closedVersionedBarrier) return false;
|
|
39
|
+
}
|
|
40
|
+
if (!Array.isArray(options.params.diagnostics)) return false;
|
|
41
|
+
if (!acceptUnversionedPush(options, openDocument)) return false;
|
|
42
|
+
options.store.set(options.params.uri, buildPushCacheEntry(options, openDocument));
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Gate one unversioned push publication against the sync-moment policy. */
|
|
47
|
+
function acceptUnversionedPush(
|
|
48
|
+
options: ApplyPushOptions,
|
|
49
|
+
openDocument: OpenDocumentState | undefined,
|
|
50
|
+
): boolean {
|
|
51
|
+
if (options.params.version !== undefined) return true;
|
|
52
|
+
if (!openDocument) return false;
|
|
53
|
+
return !(
|
|
54
|
+
options.unversionedSyncMoment !== undefined && Date.now() < options.unversionedSyncMoment
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Build the stored entry for one accepted push publication.
|
|
60
|
+
*
|
|
61
|
+
* Time-gated unversioned acceptance re-stamps the entry with the open
|
|
62
|
+
* document's current synchronization state, so the push proves that
|
|
63
|
+
* document's synchronization. Versioned pushes keep the current-revision
|
|
64
|
+
* check: a version match alone does not prove a current generation.
|
|
65
|
+
*/
|
|
66
|
+
function buildPushCacheEntry(
|
|
67
|
+
options: ApplyPushOptions,
|
|
68
|
+
openDocument: OpenDocumentState | undefined,
|
|
69
|
+
): DiagnosticCacheEntry {
|
|
70
|
+
const currentRevision = openDocument?.evidenceRevision === options.evidenceRevision;
|
|
71
|
+
const unversioned = options.params.version === undefined && openDocument !== undefined;
|
|
72
|
+
return {
|
|
73
|
+
diagnostics: options.params.diagnostics,
|
|
74
|
+
receivedAt: Date.now(),
|
|
75
|
+
source: "push",
|
|
76
|
+
synchronizationId: unversioned || currentRevision ? openDocument?.synchronizationId : undefined,
|
|
77
|
+
evidenceRevision: openDocument?.evidenceRevision,
|
|
78
|
+
version: options.params.version,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Build one client snapshot without dropping empty-cache freshness. */
|
|
83
|
+
export function buildClientDiagnosticSnapshot(options: {
|
|
84
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>;
|
|
85
|
+
openDocuments: ReadonlyMap<string, OpenDocumentState>;
|
|
86
|
+
failedDocuments: ReadonlySet<string>;
|
|
87
|
+
evidenceRevision: number;
|
|
88
|
+
}): ClientDiagnosticSnapshot {
|
|
89
|
+
const documents = collectDocumentFreshness(options);
|
|
90
|
+
const cache = collectCachedDiagnostics(options);
|
|
91
|
+
return {
|
|
92
|
+
entries: cache.entries,
|
|
93
|
+
documents,
|
|
94
|
+
current: cache.current && documents.every((document) => document.current),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function collectDocumentFreshness(options: {
|
|
99
|
+
openDocuments: ReadonlyMap<string, OpenDocumentState>;
|
|
100
|
+
failedDocuments: ReadonlySet<string>;
|
|
101
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>;
|
|
102
|
+
evidenceRevision: number;
|
|
103
|
+
}): ClientDiagnosticSnapshot["documents"] {
|
|
104
|
+
const open = collectOpenDocumentFreshness(options);
|
|
105
|
+
return [
|
|
106
|
+
...open.documents,
|
|
107
|
+
...collectFailedDocumentFreshness(options.failedDocuments, open.uris),
|
|
108
|
+
...collectUntrackedCacheFreshness(options.store, open.uris, options.failedDocuments),
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function collectOpenDocumentFreshness(options: {
|
|
113
|
+
openDocuments: ReadonlyMap<string, OpenDocumentState>;
|
|
114
|
+
failedDocuments: ReadonlySet<string>;
|
|
115
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>;
|
|
116
|
+
evidenceRevision: number;
|
|
117
|
+
}): { documents: ClientDiagnosticSnapshot["documents"]; uris: ReadonlySet<string> } {
|
|
118
|
+
const documents: ClientDiagnosticSnapshot["documents"] = [];
|
|
119
|
+
const uris = new Set<string>();
|
|
120
|
+
for (const [uri, document] of options.openDocuments) {
|
|
121
|
+
uris.add(uri);
|
|
122
|
+
const fileState = getDiagnosticFileState(uriToFile(uri));
|
|
123
|
+
const current = hasCurrentDiagnosticEvidence(
|
|
124
|
+
document,
|
|
125
|
+
options.store.get(uri),
|
|
126
|
+
options.evidenceRevision,
|
|
127
|
+
);
|
|
128
|
+
const failed = options.failedDocuments.has(uri);
|
|
129
|
+
documents.push({
|
|
130
|
+
uri,
|
|
131
|
+
current: fileState === "present" && current && !failed,
|
|
132
|
+
status: statusForDocument(fileState, current, failed),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return { documents, uris };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function collectFailedDocumentFreshness(
|
|
139
|
+
failedDocuments: ReadonlySet<string>,
|
|
140
|
+
openUris: ReadonlySet<string>,
|
|
141
|
+
): ClientDiagnosticSnapshot["documents"] {
|
|
142
|
+
return Array.from(failedDocuments)
|
|
143
|
+
.filter((uri) => !openUris.has(uri))
|
|
144
|
+
.map((uri) => ({
|
|
145
|
+
uri,
|
|
146
|
+
current: false,
|
|
147
|
+
status:
|
|
148
|
+
getDiagnosticFileState(uriToFile(uri)) === "removed"
|
|
149
|
+
? ("removed" as const)
|
|
150
|
+
: ("failed" as const),
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function collectUntrackedCacheFreshness(
|
|
155
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>,
|
|
156
|
+
openUris: ReadonlySet<string>,
|
|
157
|
+
failedDocuments: ReadonlySet<string>,
|
|
158
|
+
): ClientDiagnosticSnapshot["documents"] {
|
|
159
|
+
return Array.from(store.keys())
|
|
160
|
+
.filter((uri) => !openUris.has(uri) && !failedDocuments.has(uri))
|
|
161
|
+
.map((uri) => ({
|
|
162
|
+
uri,
|
|
163
|
+
current: false,
|
|
164
|
+
status:
|
|
165
|
+
getDiagnosticFileState(uriToFile(uri)) === "removed"
|
|
166
|
+
? ("removed" as const)
|
|
167
|
+
: getDiagnosticFileState(uriToFile(uri)) === "unreadable"
|
|
168
|
+
? ("failed" as const)
|
|
169
|
+
: ("unconfirmed" as const),
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function statusForDocument(
|
|
174
|
+
fileState: ReturnType<typeof getDiagnosticFileState>,
|
|
175
|
+
current: boolean,
|
|
176
|
+
failed: boolean,
|
|
177
|
+
): "confirmed" | "unconfirmed" | "failed" | "removed" {
|
|
178
|
+
if (fileState === "removed") return "removed";
|
|
179
|
+
if (fileState === "unreadable" || failed) return "failed";
|
|
180
|
+
return current ? "confirmed" : "unconfirmed";
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function collectCachedDiagnostics(options: {
|
|
184
|
+
store: ReadonlyMap<string, DiagnosticCacheEntry>;
|
|
185
|
+
openDocuments: ReadonlyMap<string, OpenDocumentState>;
|
|
186
|
+
evidenceRevision: number;
|
|
187
|
+
}): {
|
|
188
|
+
entries: ClientDiagnosticSnapshot["entries"];
|
|
189
|
+
current: boolean;
|
|
190
|
+
} {
|
|
191
|
+
const entries: ClientDiagnosticSnapshot["entries"] = [];
|
|
192
|
+
let current = true;
|
|
193
|
+
for (const [uri, entry] of options.store) {
|
|
194
|
+
if (getDiagnosticFileState(uriToFile(uri)) === "removed") continue;
|
|
195
|
+
const entryCurrent = hasCurrentDiagnosticEvidence(
|
|
196
|
+
options.openDocuments.get(uri),
|
|
197
|
+
entry,
|
|
198
|
+
options.evidenceRevision,
|
|
199
|
+
);
|
|
200
|
+
current &&= entryCurrent;
|
|
201
|
+
if (entry.diagnostics.length > 0) {
|
|
202
|
+
entries.push({ uri, diagnostics: entry.diagnostics, current: entryCurrent });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return { entries, current };
|
|
206
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Client-side diagnostic capability state — static (initialize result) and
|
|
2
|
+
// dynamic (client/registerCapability) tracking with fail-closed validation.
|
|
3
|
+
//
|
|
4
|
+
// Static state lives on the client's initialize-result capabilities; dynamic
|
|
5
|
+
// state lives in the per-instance `ClientDynamicRegistrations` below. Pull
|
|
6
|
+
// support is enabled only while the static provider shape is valid or the
|
|
7
|
+
// dynamic set for `textDocument/diagnostic` is non-empty.
|
|
8
|
+
|
|
9
|
+
/** LSP method for document diagnostic pulls. */
|
|
10
|
+
export const DOCUMENT_DIAGNOSTIC_METHOD = "textDocument/diagnostic";
|
|
11
|
+
|
|
12
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
13
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validate one untrusted `DiagnosticOptions` / `DiagnosticRegistrationOptions`
|
|
18
|
+
* value from a static initialize result or a dynamic registration.
|
|
19
|
+
*
|
|
20
|
+
* Required per the LSP specification: boolean `interFileDependencies` and
|
|
21
|
+
* `workspaceDiagnostics`. Optional: string `identifier`, and `documentSelector`
|
|
22
|
+
* (absent, null, or an array of string selectors / document-filter records).
|
|
23
|
+
* Unknown fields such as kotlin-lsp's `workDoneProgress` are tolerated.
|
|
24
|
+
*/
|
|
25
|
+
export function isValidDiagnosticOptions(value: unknown): boolean {
|
|
26
|
+
if (!isRecord(value)) return false;
|
|
27
|
+
if (typeof value.interFileDependencies !== "boolean") return false;
|
|
28
|
+
if (typeof value.workspaceDiagnostics !== "boolean") return false;
|
|
29
|
+
if (value.identifier !== undefined && typeof value.identifier !== "string") return false;
|
|
30
|
+
if (value.documentSelector !== undefined && value.documentSelector !== null) {
|
|
31
|
+
if (!Array.isArray(value.documentSelector)) return false;
|
|
32
|
+
if (!value.documentSelector.every((entry) => typeof entry === "string" || isRecord(entry))) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Dynamic capability registrations for one client instance.
|
|
41
|
+
*
|
|
42
|
+
* A replacement client starts with an empty instance, so late registrations
|
|
43
|
+
* on a superseded instance never affect the replacement. Unregistration
|
|
44
|
+
* removes one id; pull stays enabled until the last id is removed.
|
|
45
|
+
*/
|
|
46
|
+
export class ClientDynamicRegistrations {
|
|
47
|
+
private readonly idsByMethod = new Map<string, Set<string>>();
|
|
48
|
+
|
|
49
|
+
/** Record one registration id for a method. Duplicate ids are harmless. */
|
|
50
|
+
register(method: string, id: string): void {
|
|
51
|
+
let ids = this.idsByMethod.get(method);
|
|
52
|
+
if (ids === undefined) {
|
|
53
|
+
ids = new Set();
|
|
54
|
+
this.idsByMethod.set(method, ids);
|
|
55
|
+
}
|
|
56
|
+
ids.add(id);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Remove one registration id; unknown ids are harmless no-ops. */
|
|
60
|
+
unregister(method: string, id: string): void {
|
|
61
|
+
this.idsByMethod.get(method)?.delete(id);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Whether any registration id remains active for a method. */
|
|
65
|
+
has(method: string): boolean {
|
|
66
|
+
return (this.idsByMethod.get(method)?.size ?? 0) > 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Snapshot of the active registration ids for a method. */
|
|
70
|
+
ids(method: string): readonly string[] {
|
|
71
|
+
return [...(this.idsByMethod.get(method) ?? [])];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Drop all registrations — capability loss on shutdown, crash, or disposal. */
|
|
75
|
+
clear(): void {
|
|
76
|
+
this.idsByMethod.clear();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CodeQueryResult,
|
|
3
|
+
type CodeRequestControl,
|
|
4
|
+
completedCodeQuery,
|
|
5
|
+
isCodeRequestInterruption,
|
|
6
|
+
throwIfCodeRequestInterrupted,
|
|
7
|
+
unavailableCodeQuery,
|
|
8
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
9
|
+
import type { Diagnostic } from "../config/types.ts";
|
|
10
|
+
import {
|
|
11
|
+
type DiagnosticSynchronization,
|
|
12
|
+
incompleteDiagnosticResult,
|
|
13
|
+
raceDiagnosticPull,
|
|
14
|
+
} from "./client-diagnostic-evidence.ts";
|
|
15
|
+
import type { DiagnosticObserver, DiagnosticPushWaitOutcome } from "./client-diagnostic-timing.ts";
|
|
16
|
+
import type { DiagnosticWaitRegistry } from "./client-diagnostic-waiters.ts";
|
|
17
|
+
|
|
18
|
+
interface FileDiagnosticCollectionOptions {
|
|
19
|
+
readonly supportsPull: boolean;
|
|
20
|
+
readonly syncStart: number;
|
|
21
|
+
readonly maxWaitMs: number;
|
|
22
|
+
readonly request: DiagnosticSynchronization;
|
|
23
|
+
readonly cachedDiagnostics: Diagnostic[] | null;
|
|
24
|
+
readonly observer: DiagnosticObserver;
|
|
25
|
+
readonly waiters: DiagnosticWaitRegistry;
|
|
26
|
+
readonly current: () => boolean;
|
|
27
|
+
readonly freshPush: () => boolean;
|
|
28
|
+
readonly diagnostics: () => Diagnostic[];
|
|
29
|
+
readonly pullDiagnostics: (timeoutMs: number, signal: AbortSignal) => Promise<boolean>;
|
|
30
|
+
/** Observe the push wait outcome without finishing the observer; the caller finishes once. */
|
|
31
|
+
readonly onPushWait?: (outcome: DiagnosticPushWaitOutcome) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type PullCollectionOutcome = "failed" | "pull" | "push" | "released";
|
|
35
|
+
|
|
36
|
+
/** Collect fresh pull or push evidence for one synchronized document. */
|
|
37
|
+
export async function collectSynchronizedFileDiagnostics(
|
|
38
|
+
options: FileDiagnosticCollectionOptions,
|
|
39
|
+
control?: CodeRequestControl,
|
|
40
|
+
): Promise<CodeQueryResult<Diagnostic[]>> {
|
|
41
|
+
throwIfCodeRequestInterrupted(control);
|
|
42
|
+
if (options.supportsPull) {
|
|
43
|
+
const pullOutcome = await collectPullEvidence(options, control);
|
|
44
|
+
if (pullOutcome === "pull" || pullOutcome === "push") {
|
|
45
|
+
return completedCodeQuery(options.diagnostics());
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
throwIfCodeRequestInterrupted(control);
|
|
50
|
+
if (!options.current()) {
|
|
51
|
+
options.observer.pushWaitCompleted(1, "released");
|
|
52
|
+
return unavailableCodeQuery(
|
|
53
|
+
"Diagnostic collection ended before the current document synchronization was confirmed.",
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (options.freshPush()) {
|
|
57
|
+
options.observer.pushWaitCompleted(1, "published");
|
|
58
|
+
return completedCodeQuery(options.diagnostics());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const push = await options.waiters.waitForPush(
|
|
62
|
+
options.request.uri,
|
|
63
|
+
Math.max(0, options.maxWaitMs - (Date.now() - options.syncStart)),
|
|
64
|
+
control,
|
|
65
|
+
);
|
|
66
|
+
if (options.onPushWait) options.onPushWait(push);
|
|
67
|
+
else options.observer.pushWaitCompleted(1, push);
|
|
68
|
+
if (push === "published" && options.freshPush()) {
|
|
69
|
+
return completedCodeQuery(options.diagnostics());
|
|
70
|
+
}
|
|
71
|
+
return incompleteDiagnosticResult(
|
|
72
|
+
options.cachedDiagnostics,
|
|
73
|
+
push === "released" ? "released" : "timed-out",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function collectPullEvidence(
|
|
78
|
+
options: FileDiagnosticCollectionOptions,
|
|
79
|
+
control?: CodeRequestControl,
|
|
80
|
+
): Promise<PullCollectionOutcome> {
|
|
81
|
+
const remaining = options.maxWaitMs - (Date.now() - options.syncStart);
|
|
82
|
+
if (remaining <= 0) {
|
|
83
|
+
options.observer.pullTimedOut();
|
|
84
|
+
return "failed";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const controller = new AbortController();
|
|
89
|
+
// Link the caller's cancellation to the local pull controller so an
|
|
90
|
+
// in-flight pull receives protocol cancellation and stops promptly.
|
|
91
|
+
const onAbort = () => controller.abort(control?.signal?.reason);
|
|
92
|
+
if (control?.signal?.aborted) onAbort();
|
|
93
|
+
else control?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
94
|
+
try {
|
|
95
|
+
const outcome = await raceDiagnosticPull({
|
|
96
|
+
pull: options.pullDiagnostics(remaining, controller.signal),
|
|
97
|
+
waitForChange: () => options.waiters.waitForChange(),
|
|
98
|
+
freshPush: options.freshPush,
|
|
99
|
+
current: options.current,
|
|
100
|
+
});
|
|
101
|
+
if (outcome !== "pull") controller.abort();
|
|
102
|
+
if (outcome === "pull") options.observer.pullCompleted(1);
|
|
103
|
+
else options.observer.pullFailed(undefined);
|
|
104
|
+
if (outcome === "push") options.observer.pushWaitCompleted(1, "published");
|
|
105
|
+
return outcome;
|
|
106
|
+
} finally {
|
|
107
|
+
control?.signal?.removeEventListener("abort", onAbort);
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (isCodeRequestInterruption(error, control)) throw error;
|
|
111
|
+
options.observer.pullFailed(error);
|
|
112
|
+
return "failed";
|
|
113
|
+
}
|
|
114
|
+
}
|