@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
|
@@ -3,15 +3,22 @@
|
|
|
3
3
|
// and notification/request dispatching through vscode-jsonrpc's MessageConnection.
|
|
4
4
|
|
|
5
5
|
import type { Readable, Writable } from "node:stream";
|
|
6
|
+
import {
|
|
7
|
+
type CodeRequestControl,
|
|
8
|
+
CodeRequestDeadlineError,
|
|
9
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
10
|
+
import { startDebugTimer } from "@mrclrchtr/supi-core/debug";
|
|
6
11
|
import {
|
|
7
12
|
CancellationTokenSource,
|
|
8
13
|
createMessageConnection,
|
|
14
|
+
ErrorCodes,
|
|
9
15
|
type MessageConnection,
|
|
10
16
|
NullLogger,
|
|
11
17
|
ResponseError,
|
|
12
18
|
StreamMessageReader,
|
|
13
19
|
StreamMessageWriter,
|
|
14
20
|
} from "vscode-jsonrpc/node";
|
|
21
|
+
import { boundCwd, LSP_REQUEST_TIMEOUT_ERROR_CODE, truncateIdentity } from "../debug-telemetry.ts";
|
|
15
22
|
|
|
16
23
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
17
24
|
|
|
@@ -20,6 +27,9 @@ const DEFAULT_TIMEOUT_MS = 30_000;
|
|
|
20
27
|
export type NotificationHandler = (method: string, params: unknown) => void;
|
|
21
28
|
export type RequestHandler = (method: string, params: unknown) => Promise<unknown> | unknown;
|
|
22
29
|
|
|
30
|
+
type RequestMethodClass = "diagnostic" | "lifecycle" | "other" | "refactor" | "semantic";
|
|
31
|
+
type RequestOutcome = "cancelled" | "completed" | "failed" | "timed-out";
|
|
32
|
+
|
|
23
33
|
/** Re-export ResponseError so callers don't need a separate vscode-jsonrpc import. */
|
|
24
34
|
const JsonRpcRequestError = ResponseError;
|
|
25
35
|
|
|
@@ -33,13 +43,19 @@ export class JsonRpcClient {
|
|
|
33
43
|
private requestHandler: RequestHandler | null = null;
|
|
34
44
|
private closed = false;
|
|
35
45
|
private readonly timeoutMs: number;
|
|
46
|
+
private readonly server: string | undefined;
|
|
47
|
+
private readonly cwd: string | undefined;
|
|
48
|
+
/** Failures that signal a stalled protocol: repeated errors and local timeouts. */
|
|
49
|
+
private protocolFailureCount = 0;
|
|
36
50
|
|
|
37
51
|
constructor(
|
|
38
52
|
private readonly input: Readable,
|
|
39
53
|
private readonly output: Writable,
|
|
40
|
-
options?: { timeoutMs?: number },
|
|
54
|
+
options?: { timeoutMs?: number; server?: string; cwd?: string },
|
|
41
55
|
) {
|
|
42
56
|
this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
57
|
+
this.server = options?.server;
|
|
58
|
+
this.cwd = options?.cwd;
|
|
43
59
|
|
|
44
60
|
const reader = new StreamMessageReader(this.input);
|
|
45
61
|
const writer = new StreamMessageWriter(this.output);
|
|
@@ -51,6 +67,13 @@ export class JsonRpcClient {
|
|
|
51
67
|
this.notificationHandler?.(method, params);
|
|
52
68
|
});
|
|
53
69
|
|
|
70
|
+
// vscode-jsonrpc registers an internal $/progress handler that never
|
|
71
|
+
// reaches the catch-all handler. Route it explicitly so LSP progress
|
|
72
|
+
// notifications reach the client's readiness state machine.
|
|
73
|
+
this.connection.onNotification("$/progress", (params) => {
|
|
74
|
+
this.notificationHandler?.("$/progress", params);
|
|
75
|
+
});
|
|
76
|
+
|
|
54
77
|
// Register catch-all request handler for server-initiated requests
|
|
55
78
|
this.connection.onRequest(async (method, params, _token) => {
|
|
56
79
|
if (!this.requestHandler) {
|
|
@@ -81,17 +104,53 @@ export class JsonRpcClient {
|
|
|
81
104
|
sendRequest(
|
|
82
105
|
method: string,
|
|
83
106
|
params?: unknown,
|
|
84
|
-
options?: { timeoutMs?: number },
|
|
107
|
+
options?: { timeoutMs?: number } & CodeRequestControl,
|
|
85
108
|
): Promise<unknown> {
|
|
109
|
+
const timeoutMs = options?.timeoutMs ?? this.timeoutMs;
|
|
110
|
+
const signal = options?.signal;
|
|
111
|
+
const deadlineMs = options?.deadline === undefined ? undefined : options.deadline - Date.now();
|
|
112
|
+
const methodClass = classifyRequestMethod(method);
|
|
113
|
+
const boundedMethod = truncateIdentity(method);
|
|
114
|
+
const timer = startDebugTimer();
|
|
115
|
+
// An expired absolute deadline must not even send the request: the caller
|
|
116
|
+
// no longer awaits a result, so no protocol traffic may start.
|
|
117
|
+
if (deadlineMs !== undefined && deadlineMs <= 0) {
|
|
118
|
+
recordRequestTiming(
|
|
119
|
+
timer,
|
|
120
|
+
options?.operationId,
|
|
121
|
+
{
|
|
122
|
+
method: boundedMethod,
|
|
123
|
+
methodClass,
|
|
124
|
+
outcome: "timed-out",
|
|
125
|
+
errorCode: LSP_REQUEST_TIMEOUT_ERROR_CODE,
|
|
126
|
+
},
|
|
127
|
+
{ server: this.server, cwd: this.cwd },
|
|
128
|
+
);
|
|
129
|
+
return Promise.reject(new CodeRequestDeadlineError());
|
|
130
|
+
}
|
|
86
131
|
if (this.closed || !this.connection) {
|
|
132
|
+
recordRequestTiming(
|
|
133
|
+
timer,
|
|
134
|
+
options?.operationId,
|
|
135
|
+
{
|
|
136
|
+
method: boundedMethod,
|
|
137
|
+
methodClass,
|
|
138
|
+
outcome: "cancelled",
|
|
139
|
+
},
|
|
140
|
+
{ server: this.server, cwd: this.cwd },
|
|
141
|
+
);
|
|
87
142
|
return Promise.reject(new Error("JSON-RPC client is closed"));
|
|
88
143
|
}
|
|
89
|
-
|
|
90
|
-
const timeoutMs = options?.timeoutMs ?? this.timeoutMs;
|
|
91
144
|
const tokenSource = new CancellationTokenSource();
|
|
92
|
-
|
|
93
145
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
94
|
-
|
|
146
|
+
let timedOut = false;
|
|
147
|
+
let aborted = false;
|
|
148
|
+
const timerDelayMs = deadlineMs === undefined ? timeoutMs : Math.min(timeoutMs, deadlineMs);
|
|
149
|
+
const timeoutError = Object.assign(
|
|
150
|
+
new Error(`Request ${method} timed out after ${timerDelayMs}ms`),
|
|
151
|
+
{ code: LSP_REQUEST_TIMEOUT_ERROR_CODE },
|
|
152
|
+
);
|
|
153
|
+
const abortError = new Error(`Request ${method} was cancelled`);
|
|
95
154
|
|
|
96
155
|
const request = this.connection.sendRequest(method, params, tokenSource.token);
|
|
97
156
|
// Catch the raw request promise to prevent unhandled rejections when
|
|
@@ -102,17 +161,80 @@ export class JsonRpcClient {
|
|
|
102
161
|
// the JSON-RPC token and rejects the caller. Using one timer avoids
|
|
103
162
|
// a leak where the rejecting timer in a second Promise stays alive
|
|
104
163
|
// after a successful response.
|
|
164
|
+
let abortHandler: (() => void) | undefined;
|
|
165
|
+
const abort = new Promise<never>((_resolve, reject) => {
|
|
166
|
+
abortHandler = () => {
|
|
167
|
+
aborted = true;
|
|
168
|
+
tokenSource.cancel();
|
|
169
|
+
// Reject with the caller's abort reason when one exists, matching the
|
|
170
|
+
// canonical throwIfCodeRequestInterrupted() behavior.
|
|
171
|
+
reject(signal?.reason ?? abortError);
|
|
172
|
+
};
|
|
173
|
+
if (signal?.aborted) abortHandler();
|
|
174
|
+
else signal?.addEventListener("abort", abortHandler, { once: true });
|
|
175
|
+
});
|
|
105
176
|
const promise = Promise.race([
|
|
106
177
|
request,
|
|
107
178
|
new Promise<never>((_resolve, reject) => {
|
|
108
179
|
timeout = setTimeout(() => {
|
|
180
|
+
timedOut = true;
|
|
109
181
|
tokenSource.cancel();
|
|
110
|
-
|
|
111
|
-
|
|
182
|
+
// A deadline that binds earlier than the timeout is a deadline
|
|
183
|
+
// outcome, distinct from an ordinary per-request timeout.
|
|
184
|
+
reject(
|
|
185
|
+
deadlineMs !== undefined && deadlineMs < timeoutMs
|
|
186
|
+
? new CodeRequestDeadlineError()
|
|
187
|
+
: timeoutError,
|
|
188
|
+
);
|
|
189
|
+
}, timerDelayMs);
|
|
112
190
|
}),
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
191
|
+
abort,
|
|
192
|
+
])
|
|
193
|
+
.then(
|
|
194
|
+
(result) => {
|
|
195
|
+
recordRequestTiming(
|
|
196
|
+
timer,
|
|
197
|
+
options?.operationId,
|
|
198
|
+
{
|
|
199
|
+
method: boundedMethod,
|
|
200
|
+
methodClass,
|
|
201
|
+
outcome: "completed",
|
|
202
|
+
},
|
|
203
|
+
{ server: this.server, cwd: this.cwd },
|
|
204
|
+
);
|
|
205
|
+
return result;
|
|
206
|
+
},
|
|
207
|
+
(error: unknown) => {
|
|
208
|
+
const cancelled = timedOut || aborted || this.closed || isCancellationError(error);
|
|
209
|
+
const outcome: RequestOutcome = timedOut
|
|
210
|
+
? "timed-out"
|
|
211
|
+
: cancelled
|
|
212
|
+
? "cancelled"
|
|
213
|
+
: "failed";
|
|
214
|
+
const errorCode = requestErrorCode(outcome, error);
|
|
215
|
+
this.countProtocolStallFailure(outcome, errorCode, error);
|
|
216
|
+
recordRequestTiming(
|
|
217
|
+
timer,
|
|
218
|
+
options?.operationId,
|
|
219
|
+
{
|
|
220
|
+
method: boundedMethod,
|
|
221
|
+
methodClass,
|
|
222
|
+
outcome,
|
|
223
|
+
// Failed requests record the server-reported JSON-RPC error code
|
|
224
|
+
// when the error carries one; timed-out requests record the
|
|
225
|
+
// defined timeout code (also for deadline expiries, whose error
|
|
226
|
+
// carries none); cancellations carry no error code.
|
|
227
|
+
errorCode,
|
|
228
|
+
},
|
|
229
|
+
{ server: this.server, cwd: this.cwd },
|
|
230
|
+
);
|
|
231
|
+
throw error;
|
|
232
|
+
},
|
|
233
|
+
)
|
|
234
|
+
.finally(() => {
|
|
235
|
+
if (timeout !== undefined) clearTimeout(timeout);
|
|
236
|
+
if (abortHandler) signal?.removeEventListener("abort", abortHandler);
|
|
237
|
+
});
|
|
116
238
|
|
|
117
239
|
// Prevent unhandled rejection when dispose() cancels requests
|
|
118
240
|
promise.catch(() => {});
|
|
@@ -141,4 +263,109 @@ export class JsonRpcClient {
|
|
|
141
263
|
this.connection = null;
|
|
142
264
|
}
|
|
143
265
|
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Count one protocol-stall failure: a local timeout (not a caller-imposed
|
|
269
|
+
* deadline expiry) or a repeated server-reported protocol error. A deadline
|
|
270
|
+
* expiry is not a stall: the request may be perfectly healthy (ADR 0020).
|
|
271
|
+
*/
|
|
272
|
+
private countProtocolStallFailure(
|
|
273
|
+
outcome: RequestOutcome,
|
|
274
|
+
errorCode: number | undefined,
|
|
275
|
+
error: unknown,
|
|
276
|
+
): void {
|
|
277
|
+
if (
|
|
278
|
+
(outcome === "timed-out" && !(error instanceof CodeRequestDeadlineError)) ||
|
|
279
|
+
errorCode === ErrorCodes.ServerNotInitialized ||
|
|
280
|
+
errorCode === ErrorCodes.InvalidRequest
|
|
281
|
+
) {
|
|
282
|
+
this.protocolFailureCount++;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Number of protocol-stall failures observed on this connection. */
|
|
287
|
+
getProtocolFailureCount(): number {
|
|
288
|
+
return this.protocolFailureCount;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const SEMANTIC_REQUESTS = new Set([
|
|
293
|
+
"textDocument/definition",
|
|
294
|
+
"textDocument/documentSymbol",
|
|
295
|
+
"textDocument/hover",
|
|
296
|
+
"textDocument/implementation",
|
|
297
|
+
"textDocument/references",
|
|
298
|
+
"workspace/symbol",
|
|
299
|
+
]);
|
|
300
|
+
|
|
301
|
+
/** Classify requests into bounded groups without retaining the raw method. */
|
|
302
|
+
function classifyRequestMethod(method: string): RequestMethodClass {
|
|
303
|
+
if (method === "initialize" || method === "shutdown") return "lifecycle";
|
|
304
|
+
if (method === "textDocument/diagnostic" || method === "workspace/diagnostic") {
|
|
305
|
+
return "diagnostic";
|
|
306
|
+
}
|
|
307
|
+
if (method === "textDocument/codeAction" || method === "textDocument/rename") {
|
|
308
|
+
return "refactor";
|
|
309
|
+
}
|
|
310
|
+
return SEMANTIC_REQUESTS.has(method) ? "semantic" : "other";
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function isCancellationError(error: unknown): boolean {
|
|
314
|
+
if (!(error instanceof Error)) return false;
|
|
315
|
+
const code = (error as Error & { code?: unknown }).code;
|
|
316
|
+
return (
|
|
317
|
+
code === -32_800 ||
|
|
318
|
+
code === -32_802 ||
|
|
319
|
+
error.name === "CancellationError" ||
|
|
320
|
+
/\bcancell?ed\b/i.test(error.message)
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** One bounded request-timing observation with exact method identity. */
|
|
325
|
+
interface RequestTimingObservation {
|
|
326
|
+
readonly method: string;
|
|
327
|
+
readonly methodClass: RequestMethodClass;
|
|
328
|
+
readonly outcome: RequestOutcome;
|
|
329
|
+
/** JSON-RPC error code reported by the server for a failed request. */
|
|
330
|
+
readonly errorCode?: number;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/** Return the JSON-RPC error code for one request outcome. */
|
|
334
|
+
function requestErrorCode(outcome: RequestOutcome, error: unknown): number | undefined {
|
|
335
|
+
if (outcome === "failed") return jsonRpcErrorCode(error);
|
|
336
|
+
if (outcome === "timed-out") return jsonRpcErrorCode(error) ?? LSP_REQUEST_TIMEOUT_ERROR_CODE;
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** Return the numeric JSON-RPC error code carried by an error, if any. */
|
|
341
|
+
function jsonRpcErrorCode(error: unknown): number | undefined {
|
|
342
|
+
if (typeof error !== "object" || error === null) return undefined;
|
|
343
|
+
const code = (error as { code?: unknown }).code;
|
|
344
|
+
return typeof code === "number" && Number.isInteger(code) ? code : undefined;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function recordRequestTiming(
|
|
348
|
+
timer: ReturnType<typeof startDebugTimer>,
|
|
349
|
+
operationId: string | undefined,
|
|
350
|
+
observation: RequestTimingObservation,
|
|
351
|
+
identity: { server?: string; cwd?: string },
|
|
352
|
+
): void {
|
|
353
|
+
timer.finish(
|
|
354
|
+
() => ({
|
|
355
|
+
operationId,
|
|
356
|
+
source: "lsp",
|
|
357
|
+
level: "debug",
|
|
358
|
+
category: "request.timing",
|
|
359
|
+
message: `LSP ${observation.methodClass} request ${observation.outcome} for ${observation.method}`,
|
|
360
|
+
cwd: boundCwd(identity.cwd),
|
|
361
|
+
data: {
|
|
362
|
+
method: observation.method,
|
|
363
|
+
methodClass: observation.methodClass,
|
|
364
|
+
outcome: observation.outcome,
|
|
365
|
+
...(identity.server !== undefined ? { server: identity.server } : {}),
|
|
366
|
+
...(observation.errorCode !== undefined ? { errorCode: observation.errorCode } : {}),
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
"request",
|
|
370
|
+
);
|
|
144
371
|
}
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
import type { ClientCapabilities } from "./types.ts";
|
|
4
4
|
|
|
5
5
|
export const CLIENT_CAPABILITIES: ClientCapabilities = {
|
|
6
|
+
general: {
|
|
7
|
+
positionEncodings: ["utf-16"],
|
|
8
|
+
},
|
|
6
9
|
textDocument: {
|
|
7
10
|
synchronization: {
|
|
8
11
|
didSave: true,
|
|
@@ -49,7 +52,10 @@ export const CLIENT_CAPABILITIES: ClientCapabilities = {
|
|
|
49
52
|
versionSupport: true,
|
|
50
53
|
},
|
|
51
54
|
diagnostic: {
|
|
52
|
-
|
|
55
|
+
// Dynamic registration lets servers such as pyright-langserver register
|
|
56
|
+
// `textDocument/diagnostic` support after initialization. Probes show
|
|
57
|
+
// only Pyright acts on this global flag among the built-in servers.
|
|
58
|
+
dynamicRegistration: true,
|
|
53
59
|
relatedDocumentSupport: true,
|
|
54
60
|
},
|
|
55
61
|
},
|
|
@@ -58,6 +64,9 @@ export const CLIENT_CAPABILITIES: ClientCapabilities = {
|
|
|
58
64
|
},
|
|
59
65
|
workspace: {
|
|
60
66
|
workspaceFolders: false,
|
|
67
|
+
workspaceEdit: {
|
|
68
|
+
documentChanges: true,
|
|
69
|
+
},
|
|
61
70
|
diagnostics: {
|
|
62
71
|
refreshSupport: false,
|
|
63
72
|
},
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Bounded identity vocabulary for LSP debug telemetry.
|
|
2
|
+
//
|
|
3
|
+
// LSP debug events may carry server, workspace, file, method, and root
|
|
4
|
+
// identity for local protocol diagnosis. Every emitted identity string is
|
|
5
|
+
// bounded to MAX_IDENTITY_STRING UTF-16 code units (truncation marker
|
|
6
|
+
// included) and every identity list is bounded to MAX_SERVERS entries.
|
|
7
|
+
|
|
8
|
+
/** Maximum number of server entries in one bounded LSP debug payload. */
|
|
9
|
+
export const MAX_SERVERS = 16;
|
|
10
|
+
|
|
11
|
+
/** Maximum UTF-16 code-unit length of one emitted identity string in an LSP debug payload. */
|
|
12
|
+
export const MAX_IDENTITY_STRING = 512;
|
|
13
|
+
|
|
14
|
+
/** Marker appended to an identity string truncated to MAX_IDENTITY_STRING. */
|
|
15
|
+
export const IDENTITY_TRUNCATION_MARKER = "…";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* JSON-RPC error code recorded for client-local request timeouts.
|
|
19
|
+
*
|
|
20
|
+
* The JSON-RPC and LSP vocabularies define no timeout code. This constant
|
|
21
|
+
* uses the JSON-RPC implementation-defined error range (-32099..-32000) at a
|
|
22
|
+
* value that vscode-jsonrpc transport codes (-32099, -32098, -32097, -32096,
|
|
23
|
+
* -32002, -32001) and LSP error codes never use.
|
|
24
|
+
*/
|
|
25
|
+
export const LSP_REQUEST_TIMEOUT_ERROR_CODE = -32_095;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Truncate one identity string so the emitted value (marker included) never
|
|
29
|
+
* exceeds MAX_IDENTITY_STRING UTF-16 code units.
|
|
30
|
+
*/
|
|
31
|
+
export function truncateIdentity(value: string): string {
|
|
32
|
+
if (value.length <= MAX_IDENTITY_STRING) return value;
|
|
33
|
+
return `${value.slice(0, MAX_IDENTITY_STRING - 1)}${IDENTITY_TRUNCATION_MARKER}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Bound an optional workspace root for event-level identity; undefined stays absent. */
|
|
37
|
+
export function boundCwd(cwd: string | undefined): string | undefined {
|
|
38
|
+
if (cwd === undefined) return undefined;
|
|
39
|
+
return truncateIdentity(cwd);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Bound a server-name list to MAX_SERVERS entries, truncating each name. */
|
|
43
|
+
export function boundServerNames(names: readonly string[]): string[] {
|
|
44
|
+
return names.slice(0, MAX_SERVERS).map(truncateIdentity);
|
|
45
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** A final evidence state for one tracked document. */
|
|
2
|
+
export type DiagnosticEvidenceStatus = "confirmed" | "unconfirmed" | "failed" | "removed";
|
|
3
|
+
|
|
4
|
+
/** Freshness evidence for one tracked document. */
|
|
5
|
+
export interface DiagnosticEvidenceDocument {
|
|
6
|
+
readonly file: string;
|
|
7
|
+
readonly status: DiagnosticEvidenceStatus;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Exact document coverage from a diagnostic snapshot or refresh attempt. */
|
|
11
|
+
export interface DiagnosticEvidenceSummary {
|
|
12
|
+
readonly requested: number;
|
|
13
|
+
readonly confirmed: number;
|
|
14
|
+
readonly unconfirmed: number;
|
|
15
|
+
readonly failed: number;
|
|
16
|
+
readonly removed: number;
|
|
17
|
+
readonly documents: readonly DiagnosticEvidenceDocument[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Build exact coverage counts from one document outcome per requested document. */
|
|
21
|
+
export function summarizeDiagnosticEvidence(
|
|
22
|
+
documents: readonly DiagnosticEvidenceDocument[],
|
|
23
|
+
): DiagnosticEvidenceSummary {
|
|
24
|
+
const counts = {
|
|
25
|
+
requested: documents.length,
|
|
26
|
+
confirmed: 0,
|
|
27
|
+
unconfirmed: 0,
|
|
28
|
+
failed: 0,
|
|
29
|
+
removed: 0,
|
|
30
|
+
};
|
|
31
|
+
for (const document of documents) counts[document.status]++;
|
|
32
|
+
return { ...counts, documents: [...documents] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Return an empty evidence summary for a refresh with no tracked documents. */
|
|
36
|
+
export function emptyDiagnosticEvidence(): DiagnosticEvidenceSummary {
|
|
37
|
+
return summarizeDiagnosticEvidence([]);
|
|
38
|
+
}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
type CodeRequestControl,
|
|
4
|
+
isCodeRequestInterruption,
|
|
5
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
2
6
|
import type { LspClient } from "../client/client.ts";
|
|
7
|
+
import { getDiagnosticFileState } from "../client/client-file-state.ts";
|
|
8
|
+
import {
|
|
9
|
+
type DiagnosticEvidenceDocument,
|
|
10
|
+
type DiagnosticEvidenceSummary,
|
|
11
|
+
summarizeDiagnosticEvidence,
|
|
12
|
+
} from "../diagnostics/evidence.ts";
|
|
13
|
+
import { uriToFile } from "../utils.ts";
|
|
3
14
|
|
|
4
15
|
export function closeFileAcrossClients(clients: Iterable<LspClient>, filePath: string): void {
|
|
5
16
|
const resolvedPath = path.resolve(filePath);
|
|
@@ -21,14 +32,73 @@ export function pruneMissingFilesFromClients(clients: Iterable<LspClient>): stri
|
|
|
21
32
|
|
|
22
33
|
export async function refreshOpenDiagnosticsForClients(
|
|
23
34
|
clients: Iterable<LspClient>,
|
|
24
|
-
options?: { maxWaitMs?: number; quietMs?: number },
|
|
25
|
-
): Promise<
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
options?: { maxWaitMs?: number; quietMs?: number } & CodeRequestControl,
|
|
36
|
+
): Promise<DiagnosticEvidenceSummary> {
|
|
37
|
+
const documents: DiagnosticEvidenceDocument[] = [];
|
|
38
|
+
const refreshes = Array.from(clients).map(async (client) => {
|
|
39
|
+
const trackedFiles = [...client.openFiles];
|
|
40
|
+
if (client.status !== "running") {
|
|
41
|
+
documents.push(...failedCoverageForClient(client, trackedFiles));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const result = await client.refreshOpenDiagnostics(options);
|
|
47
|
+
const reportedFiles = new Set(result.documents.map((document) => document.file));
|
|
48
|
+
documents.push(...result.documents);
|
|
49
|
+
documents.push(
|
|
50
|
+
...trackedFiles
|
|
51
|
+
.filter((file) => !reportedFiles.has(file))
|
|
52
|
+
.map((file) => ({ file, status: "failed" as const })),
|
|
53
|
+
);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// A cancelled refresh must not degrade into failed coverage evidence.
|
|
56
|
+
if (isCodeRequestInterruption(error, options)) throw error;
|
|
57
|
+
// Keep failed coverage for every document requested by the client.
|
|
58
|
+
documents.push(...failedCoverageForClient(client, trackedFiles));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
33
61
|
await Promise.all(refreshes);
|
|
62
|
+
return summarizeDiagnosticEvidence(deduplicateEvidenceDocuments(documents));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function failedCoverageForClient(
|
|
66
|
+
client: LspClient,
|
|
67
|
+
trackedFiles: readonly string[],
|
|
68
|
+
): DiagnosticEvidenceDocument[] {
|
|
69
|
+
const snapshot = client.getDiagnosticSnapshot();
|
|
70
|
+
const files = new Set([
|
|
71
|
+
...trackedFiles,
|
|
72
|
+
...snapshot.documents.map((document) => uriToFile(document.uri)),
|
|
73
|
+
]);
|
|
74
|
+
return Array.from(files).map((file) => ({
|
|
75
|
+
file,
|
|
76
|
+
status: getDiagnosticFileState(file) === "removed" ? ("removed" as const) : ("failed" as const),
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function deduplicateEvidenceDocuments(
|
|
81
|
+
documents: readonly DiagnosticEvidenceDocument[],
|
|
82
|
+
): DiagnosticEvidenceDocument[] {
|
|
83
|
+
const byFile = new Map<string, DiagnosticEvidenceDocument>();
|
|
84
|
+
for (const document of documents) {
|
|
85
|
+
const existing = byFile.get(document.file);
|
|
86
|
+
if (!existing || evidenceStatusRank(document.status) > evidenceStatusRank(existing.status)) {
|
|
87
|
+
byFile.set(document.file, document);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return Array.from(byFile.values()).sort((a, b) => a.file.localeCompare(b.file));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function evidenceStatusRank(status: DiagnosticEvidenceDocument["status"]): number {
|
|
94
|
+
switch (status) {
|
|
95
|
+
case "confirmed":
|
|
96
|
+
return 1;
|
|
97
|
+
case "unconfirmed":
|
|
98
|
+
return 2;
|
|
99
|
+
case "failed":
|
|
100
|
+
return 3;
|
|
101
|
+
case "removed":
|
|
102
|
+
return 4;
|
|
103
|
+
}
|
|
34
104
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
type CodeQueryResult,
|
|
4
|
+
type CodeRequestControl,
|
|
5
|
+
mapCodeQueryResult,
|
|
6
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
2
7
|
import type { LspClient } from "../client/client.ts";
|
|
3
8
|
import type { Diagnostic } from "../config/types.ts";
|
|
4
9
|
import { relativeFilePathFromUri } from "../diagnostics/diagnostic-summary.ts";
|
|
@@ -10,10 +15,14 @@ export async function syncClientFileAndGetDiagnostics(
|
|
|
10
15
|
client: Pick<LspClient, "syncAndWaitForDiagnostics">,
|
|
11
16
|
filePath: string,
|
|
12
17
|
maxSeverity: number,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
control?: CodeRequestControl,
|
|
19
|
+
): Promise<CodeQueryResult<Diagnostic[]>> {
|
|
20
|
+
const content = readFileSync(filePath, "utf-8");
|
|
21
|
+
const result = control
|
|
22
|
+
? await client.syncAndWaitForDiagnostics(filePath, content, control)
|
|
23
|
+
: await client.syncAndWaitForDiagnostics(filePath, content);
|
|
24
|
+
return mapCodeQueryResult(result, (diagnostics) =>
|
|
25
|
+
filterDiagnosticsBySeverity(diagnostics, maxSeverity),
|
|
17
26
|
);
|
|
18
27
|
}
|
|
19
28
|
|