@mrclrchtr/supi-code-intelligence 6.4.0 → 7.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 +164 -82
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/README.md +35 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +43 -35
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +83 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +108 -92
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +44 -82
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +3 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +9 -68
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +45 -58
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +230 -331
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +201 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +129 -70
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-typescript.ts +395 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +8 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +685 -183
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +60 -60
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-request-enrollment.ts +75 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-barrier.ts +443 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-errors.ts +30 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-file.ts +15 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +290 -37
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +72 -16
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +30 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +608 -45
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +19 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/package.json +1 -1
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +1 -2
- package/src/analysis/health/file-scope.ts +4 -1
- package/src/analysis/health/server-status.ts +1 -1
- package/src/analysis/relations/callees.ts +17 -8
- package/src/analysis/relations/implementations.ts +1 -2
- package/src/analysis/relations/references.ts +8 -4
- package/src/analysis/relations/types.ts +6 -30
- package/src/analysis/target/anchored.ts +9 -1
- package/src/analysis/target/symbol.ts +29 -11
- package/src/analysis/target/types.ts +14 -6
- package/src/api.ts +2 -0
- package/src/session/graph/collect.ts +18 -3
- package/src/session/health-refresh.ts +12 -1
- package/src/session/health-types.ts +18 -6
- package/src/session/health-workflow.ts +21 -19
- package/src/session/orientation-types.ts +5 -6
- package/src/session/orientation-workflow.ts +10 -2
- package/src/session/target-workflow.ts +9 -6
- package/src/substrate/lsp/source-tracking.ts +11 -6
- package/src/tool/code_find/guidance.ts +2 -2
- package/src/tool/code_find/spec.ts +5 -4
- package/src/tool/code_find/tui.ts +7 -1
- package/src/tool/code_graph/details.ts +32 -0
- package/src/tool/code_graph/display.ts +54 -0
- package/src/tool/code_graph/format.ts +60 -0
- package/src/tool/code_graph/guidance.ts +1 -1
- package/src/tool/code_graph/markdown.ts +76 -198
- package/src/tool/code_graph/read-next.ts +65 -0
- package/src/tool/code_graph/result.ts +118 -83
- package/src/tool/code_graph/tui.ts +216 -54
- package/src/tool/code_health/guidance.ts +2 -4
- package/src/tool/code_health/markdown.ts +75 -58
- package/src/tool/code_health/refresh-outcome.ts +41 -1
- package/src/tool/code_health/refresh-status.ts +114 -48
- package/src/tool/code_health/scope.ts +78 -0
- package/src/tool/code_health/spec.ts +1 -1
- package/src/tool/code_health/tui.ts +25 -38
- package/src/tool/code_inspect/tui.ts +7 -1
- package/src/tool/code_orientation/result.ts +31 -8
- package/src/tool/code_orientation/tui.ts +26 -5
- package/src/tool/code_refactor_apply/guidance.ts +1 -1
- package/src/tool/code_refactor_apply/tui.ts +7 -3
- package/src/tool/code_refactor_plan/guidance.ts +1 -1
- package/src/tool/code_refactor_plan/tui.ts +7 -1
- package/src/tool/code_resolve/guidance.ts +2 -4
- package/src/tool/code_resolve/result.ts +13 -10
- package/src/tool/code_resolve/tui.ts +18 -1
- package/src/tool/infra/truncate.ts +39 -13
- package/src/tool/result/candidate-row.ts +27 -0
- package/src/tool/result/errors.ts +21 -6
- package/src/tool/result/types.ts +7 -3
- package/src/tool/schemas.ts +5 -0
- package/src/types/execution.ts +2 -0
- package/src/types/index.ts +2 -0
- package/src/ui/tui/candidate-legacy-body.ts +69 -0
- package/src/ui/tui/candidate-selection.ts +366 -0
- package/src/ui/tui/common.ts +26 -31
- package/src/ui/tui/execution-error.ts +92 -0
|
@@ -45,14 +45,31 @@ import { raceRequestControl } from "../session/readiness.ts";
|
|
|
45
45
|
import {
|
|
46
46
|
ClientDynamicRegistrations,
|
|
47
47
|
DOCUMENT_DIAGNOSTIC_METHOD,
|
|
48
|
+
isDocumentSelectorApplicable,
|
|
48
49
|
isValidDiagnosticOptions,
|
|
49
50
|
} from "./client-diagnostic-capabilities.ts";
|
|
51
|
+
import type {
|
|
52
|
+
DiagnosticPullRequest,
|
|
53
|
+
DiagnosticRequestAdapter,
|
|
54
|
+
DiagnosticRequestExecution,
|
|
55
|
+
} from "./client-diagnostic-request.ts";
|
|
56
|
+
import { createPriorityDiagnosticRequestAdapter } from "./client-diagnostic-request.ts";
|
|
57
|
+
import { createTypeScriptDiagnosticRequestAdapter } from "./client-diagnostic-typescript.ts";
|
|
50
58
|
import { ClientDiagnostics } from "./client-diagnostics.ts";
|
|
51
59
|
import type { ClientDiagnosticSnapshot, DiagnosticEntry } from "./client-document-state.ts";
|
|
60
|
+
import { SemanticInputRequestRetryGuard } from "./client-request-enrollment.ts";
|
|
61
|
+
import {
|
|
62
|
+
isSemanticInputEnrollmentError,
|
|
63
|
+
SemanticInputSynchronizationError,
|
|
64
|
+
} from "./client-semantic-input-errors.ts";
|
|
52
65
|
import { JsonRpcClient, JsonRpcRequestError } from "./transport.ts";
|
|
53
66
|
|
|
54
67
|
const SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
55
68
|
|
|
69
|
+
interface SemanticRequestAttemptState {
|
|
70
|
+
inputRevision: number | undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
56
73
|
/**
|
|
57
74
|
* Fixed bound after which a running client that never became ready is
|
|
58
75
|
* considered readiness-stalled and eligible for a recovery restart.
|
|
@@ -176,6 +193,7 @@ export class LspClient {
|
|
|
176
193
|
private rpc: JsonRpcClient | null = null;
|
|
177
194
|
private _status: ClientStatus = "initializing";
|
|
178
195
|
private capabilities: ServerCapabilities | null = null;
|
|
196
|
+
private readonly diagnosticRequestAdapter: DiagnosticRequestAdapter;
|
|
179
197
|
private readonly diagnostics: ClientDiagnostics;
|
|
180
198
|
/** Dynamic capability registrations for this client instance only. */
|
|
181
199
|
private readonly dynamicRegistrations = new ClientDynamicRegistrations();
|
|
@@ -205,36 +223,41 @@ export class LspClient {
|
|
|
205
223
|
) {
|
|
206
224
|
this.name = name;
|
|
207
225
|
this.root = root;
|
|
226
|
+
const nativeDiagnosticAdapter: DiagnosticRequestAdapter = {
|
|
227
|
+
supports: (uri) => this.hasApplicableDiagnosticProvider(uri),
|
|
228
|
+
sourceFor: (uri) => (this.hasApplicableDiagnosticProvider(uri) ? "pull" : undefined),
|
|
229
|
+
collect: (request) => this.startNativeDiagnosticRequest(request),
|
|
230
|
+
};
|
|
231
|
+
const typescriptDiagnosticAdapter = createTypeScriptDiagnosticRequestAdapter({
|
|
232
|
+
fileTypes: config.fileTypes,
|
|
233
|
+
cwd: cwd ?? root,
|
|
234
|
+
isSupportedRoute: () => this.isSupportedTypeScriptRoute(),
|
|
235
|
+
hasCommand: () => this.hasTypeScriptRequestCommand(),
|
|
236
|
+
getReady: () => this.getReady(),
|
|
237
|
+
sendRequestOwned: (method, params, options) => {
|
|
238
|
+
const rpc = this.rpc;
|
|
239
|
+
if (!rpc || this._status !== "running") {
|
|
240
|
+
return {
|
|
241
|
+
result: Promise.reject(new Error("client not running")),
|
|
242
|
+
settled: Promise.resolve(),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return rpc.sendRequestOwned(method, params, options);
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
this.diagnosticRequestAdapter = createPriorityDiagnosticRequestAdapter([
|
|
249
|
+
nativeDiagnosticAdapter,
|
|
250
|
+
typescriptDiagnosticAdapter,
|
|
251
|
+
]);
|
|
208
252
|
this.diagnostics = new ClientDiagnostics({
|
|
209
253
|
server: name,
|
|
210
254
|
cwd: cwd,
|
|
211
255
|
isOperational: () => this.rpc !== null && this._status === "running",
|
|
212
|
-
|
|
256
|
+
diagnosticRequestAdapter: this.diagnosticRequestAdapter,
|
|
213
257
|
usesIncrementalDocumentSync: () => this.usesIncrementalDocumentSync,
|
|
214
258
|
sendNotification: (method, params) => {
|
|
215
259
|
if (this.rpc) void this.rpc.sendNotification(method, params);
|
|
216
260
|
},
|
|
217
|
-
pullDocumentDiagnostics: async (request) => {
|
|
218
|
-
const rpc = this.rpc;
|
|
219
|
-
if (!rpc || this._status !== "running") throw new Error("client not running");
|
|
220
|
-
await this.getReady({
|
|
221
|
-
signal: request.signal,
|
|
222
|
-
deadline: request.deadline,
|
|
223
|
-
});
|
|
224
|
-
return rpc.sendRequest(
|
|
225
|
-
DOCUMENT_DIAGNOSTIC_METHOD,
|
|
226
|
-
{
|
|
227
|
-
textDocument: { uri: request.uri },
|
|
228
|
-
previousResultId: request.previousResultId,
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
timeoutMs: request.timeoutMs,
|
|
232
|
-
signal: request.signal,
|
|
233
|
-
deadline: request.deadline,
|
|
234
|
-
operationId: request.operationId,
|
|
235
|
-
},
|
|
236
|
-
) as Promise<DocumentDiagnosticReport>;
|
|
237
|
-
},
|
|
238
261
|
});
|
|
239
262
|
}
|
|
240
263
|
|
|
@@ -552,18 +575,108 @@ export class LspClient {
|
|
|
552
575
|
this.diagnostics.clearPullResultIds();
|
|
553
576
|
}
|
|
554
577
|
|
|
555
|
-
/** Check if server
|
|
578
|
+
/** Check if the server advertises a valid native pull provider. */
|
|
556
579
|
get hasDiagnosticProvider(): boolean {
|
|
557
|
-
// Static state: a valid `diagnosticProvider` in the initialize result.
|
|
558
|
-
// Dynamic state: an active registration for the diagnostic method. A
|
|
559
|
-
// malformed static shape or an empty dynamic set fails closed, so an
|
|
560
|
-
// unsupported server never gets pull requests.
|
|
561
580
|
return (
|
|
562
581
|
isValidDiagnosticOptions(this.capabilities?.diagnosticProvider) ||
|
|
563
582
|
this.dynamicRegistrations.has(DOCUMENT_DIAGNOSTIC_METHOD)
|
|
564
583
|
);
|
|
565
584
|
}
|
|
566
585
|
|
|
586
|
+
/** Whether this route has either native pull or the tested TypeScript adapter. */
|
|
587
|
+
get hasDiagnosticRequestAdapter(): boolean {
|
|
588
|
+
return this.hasDiagnosticProvider || this.isSupportedTypeScriptRouteAndCommand();
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** Select a native provider that applies to one document. */
|
|
592
|
+
private getDiagnosticProviderOptions(uri: string): Record<string, unknown> | undefined {
|
|
593
|
+
const staticOptions = this.capabilities?.diagnosticProvider;
|
|
594
|
+
const staticRecord = staticOptions as Record<string, unknown> | undefined;
|
|
595
|
+
if (
|
|
596
|
+
staticRecord !== undefined &&
|
|
597
|
+
isValidDiagnosticOptions(staticRecord) &&
|
|
598
|
+
isDocumentSelectorApplicable(staticRecord.documentSelector, uri)
|
|
599
|
+
) {
|
|
600
|
+
return staticRecord;
|
|
601
|
+
}
|
|
602
|
+
for (const registration of this.dynamicRegistrations.get(DOCUMENT_DIAGNOSTIC_METHOD)) {
|
|
603
|
+
if (isDocumentSelectorApplicable(registration.options.documentSelector, uri)) {
|
|
604
|
+
return registration.options;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return undefined;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
private hasApplicableDiagnosticProvider(uri: string): boolean {
|
|
611
|
+
return this.getDiagnosticProviderOptions(uri) !== undefined;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
private isSupportedTypeScriptRoute(): boolean {
|
|
615
|
+
const command = path.basename(this.config.command).replace(/\.(?:cmd|exe)$/i, "");
|
|
616
|
+
return command === "typescript-language-server";
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
private isSupportedTypeScriptRouteAndCommand(): boolean {
|
|
620
|
+
return (
|
|
621
|
+
this.isSupportedTypeScriptRoute() &&
|
|
622
|
+
Array.isArray(this.capabilities?.executeCommandProvider?.commands) &&
|
|
623
|
+
this.capabilities.executeCommandProvider.commands.includes("typescript.tsserverRequest")
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private hasTypeScriptRequestCommand(): boolean {
|
|
628
|
+
return this.isSupportedTypeScriptRouteAndCommand();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/** Start one native pull request without transferring caller ownership. */
|
|
632
|
+
private startNativeDiagnosticRequest(request: DiagnosticPullRequest): DiagnosticRequestExecution<{
|
|
633
|
+
source: "pull";
|
|
634
|
+
report: DocumentDiagnosticReport;
|
|
635
|
+
}> {
|
|
636
|
+
let activeSettled = Promise.resolve();
|
|
637
|
+
const result = (async () => {
|
|
638
|
+
const ownerDeadline = Number.isFinite(request.timeoutMs)
|
|
639
|
+
? Date.now() + request.timeoutMs
|
|
640
|
+
: undefined;
|
|
641
|
+
await raceRequestControl(this.getReady(), {
|
|
642
|
+
signal: request.signal,
|
|
643
|
+
deadline: ownerDeadline,
|
|
644
|
+
});
|
|
645
|
+
const rpc = this.rpc;
|
|
646
|
+
if (!rpc || this._status !== "running") throw new Error("client not running");
|
|
647
|
+
const provider = this.getDiagnosticProviderOptions(request.uri);
|
|
648
|
+
if (!provider) throw new Error("No native diagnostic provider applies to this document.");
|
|
649
|
+
const owned = rpc.sendRequestOwned(
|
|
650
|
+
DOCUMENT_DIAGNOSTIC_METHOD,
|
|
651
|
+
{
|
|
652
|
+
textDocument: { uri: request.uri },
|
|
653
|
+
previousResultId: request.previousResultId,
|
|
654
|
+
...(typeof provider.identifier === "string" ? { identifier: provider.identifier } : {}),
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
timeoutMs: request.timeoutMs,
|
|
658
|
+
deadline: ownerDeadline,
|
|
659
|
+
...(request.operationId !== undefined ? { operationId: request.operationId } : {}),
|
|
660
|
+
},
|
|
661
|
+
);
|
|
662
|
+
activeSettled = owned.settled;
|
|
663
|
+
const report = (await raceRequestControl(owned.result, {
|
|
664
|
+
signal: request.signal,
|
|
665
|
+
deadline: ownerDeadline,
|
|
666
|
+
})) as DocumentDiagnosticReport;
|
|
667
|
+
return { source: "pull" as const, report };
|
|
668
|
+
})();
|
|
669
|
+
const settled = result
|
|
670
|
+
.then(
|
|
671
|
+
() => activeSettled,
|
|
672
|
+
() => activeSettled,
|
|
673
|
+
)
|
|
674
|
+
.then(() => undefined);
|
|
675
|
+
result.catch(() => {});
|
|
676
|
+
settled.catch(() => {});
|
|
677
|
+
return { result, settled };
|
|
678
|
+
}
|
|
679
|
+
|
|
567
680
|
/** Notify the server that watched workspace files changed. */
|
|
568
681
|
notifyWorkspaceFileChanges(changes: FileEvent[]): void {
|
|
569
682
|
if (!this.rpc || this._status !== "running" || changes.length === 0) return;
|
|
@@ -580,13 +693,18 @@ export class LspClient {
|
|
|
580
693
|
return this.diagnostics.refreshOpenDiagnostics(options);
|
|
581
694
|
}
|
|
582
695
|
|
|
583
|
-
/**
|
|
696
|
+
/**
|
|
697
|
+
* Sync one file and return diagnostics with explicit evidence availability.
|
|
698
|
+
* The manager disables content authority so this barrier can detect a disk
|
|
699
|
+
* deletion between the manager read and the client request.
|
|
700
|
+
*/
|
|
584
701
|
async syncAndWaitForDiagnostics(
|
|
585
702
|
filePath: string,
|
|
586
703
|
content: string,
|
|
587
704
|
control?: CodeRequestControl,
|
|
705
|
+
options: { contentIsAuthoritative?: boolean } = {},
|
|
588
706
|
): Promise<CodeQueryResult<Diagnostic[]>> {
|
|
589
|
-
return this.diagnostics.syncAndWaitForDiagnostics(filePath, content, control);
|
|
707
|
+
return this.diagnostics.syncAndWaitForDiagnostics(filePath, content, control, options);
|
|
590
708
|
}
|
|
591
709
|
|
|
592
710
|
// ── LSP Requests ───────────────────────────────────────────────────
|
|
@@ -707,9 +825,7 @@ export class LspClient {
|
|
|
707
825
|
);
|
|
708
826
|
}
|
|
709
827
|
try {
|
|
710
|
-
await this.
|
|
711
|
-
const data = (await this.rpc.sendRequest(method, params, control)) as T | null | undefined;
|
|
712
|
-
return completedCodeQuery(data ?? null);
|
|
828
|
+
return await this.queryWithEnrollmentRetry(method, params, control);
|
|
713
829
|
} catch (error) {
|
|
714
830
|
// Cancellation and absolute-deadline expiry propagate as interruptions:
|
|
715
831
|
// the caller no longer awaits a result, so no unavailable outcome may
|
|
@@ -720,6 +836,125 @@ export class LspClient {
|
|
|
720
836
|
}
|
|
721
837
|
}
|
|
722
838
|
|
|
839
|
+
private async queryWithEnrollmentRetry<T>(
|
|
840
|
+
method: string,
|
|
841
|
+
params: unknown,
|
|
842
|
+
control?: CodeRequestControl,
|
|
843
|
+
): Promise<CodeQueryResult<T | null>> {
|
|
844
|
+
const retryGuard = new SemanticInputRequestRetryGuard((revision) =>
|
|
845
|
+
this.diagnostics.getSemanticInputChangeSince(revision),
|
|
846
|
+
);
|
|
847
|
+
for (;;) {
|
|
848
|
+
const attempt: SemanticRequestAttemptState = { inputRevision: undefined };
|
|
849
|
+
try {
|
|
850
|
+
const data = await this.runSemanticRequestAttempt<T>({
|
|
851
|
+
method,
|
|
852
|
+
params,
|
|
853
|
+
control,
|
|
854
|
+
retryGuard,
|
|
855
|
+
attempt,
|
|
856
|
+
});
|
|
857
|
+
if (retryGuard.hasRetried) {
|
|
858
|
+
this.recordSemanticRetry({ method, control, retryGuard, outcome: "completed" });
|
|
859
|
+
}
|
|
860
|
+
return completedCodeQuery(data);
|
|
861
|
+
} catch (error) {
|
|
862
|
+
this.prepareSemanticRetry({ method, control, retryGuard, attempt, error });
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/** Return only when another attempt is permitted; retain the cause when retry stops. */
|
|
868
|
+
private prepareSemanticRetry(options: {
|
|
869
|
+
method: string;
|
|
870
|
+
control?: CodeRequestControl;
|
|
871
|
+
retryGuard: SemanticInputRequestRetryGuard;
|
|
872
|
+
attempt: SemanticRequestAttemptState;
|
|
873
|
+
error: unknown;
|
|
874
|
+
}): void {
|
|
875
|
+
const { method, control, retryGuard, attempt, error } = options;
|
|
876
|
+
if (isCodeRequestInterruption(error, control)) {
|
|
877
|
+
if (retryGuard.hasRetried) {
|
|
878
|
+
this.recordSemanticRetry({ method, control, retryGuard, outcome: "interrupted" });
|
|
879
|
+
}
|
|
880
|
+
throwIfCodeRequestInterrupted(control);
|
|
881
|
+
throw error;
|
|
882
|
+
}
|
|
883
|
+
throwIfCodeRequestInterrupted(control);
|
|
884
|
+
const decision = retryGuard.decide(error, attempt.inputRevision);
|
|
885
|
+
if (decision.kind === "retry") {
|
|
886
|
+
throwIfCodeRequestInterrupted(control);
|
|
887
|
+
this.recordSemanticRetry({ method, control, retryGuard, outcome: "retry", error });
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
if (retryGuard.hasRetried) {
|
|
891
|
+
const outcome = isSemanticInputEnrollmentError(decision.error) ? "exhausted" : "failed";
|
|
892
|
+
this.recordSemanticRetry({ method, control, retryGuard, outcome, error: decision.error });
|
|
893
|
+
const detail =
|
|
894
|
+
decision.error instanceof Error ? decision.error.message : String(decision.error);
|
|
895
|
+
const count = retryGuard.retryCount;
|
|
896
|
+
throw new Error(
|
|
897
|
+
`${detail} Enrollment retry ${outcome} after ${count} ${count === 1 ? "retry" : "retries"}.`,
|
|
898
|
+
{
|
|
899
|
+
cause: decision.error,
|
|
900
|
+
},
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
throw decision.error;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/** Record request acceptance separately from transport completion, without request content. */
|
|
907
|
+
private recordSemanticRetry(options: {
|
|
908
|
+
method: string;
|
|
909
|
+
control?: CodeRequestControl;
|
|
910
|
+
retryGuard: SemanticInputRequestRetryGuard;
|
|
911
|
+
outcome: "retry" | "completed" | "exhausted" | "failed" | "interrupted";
|
|
912
|
+
error?: unknown;
|
|
913
|
+
}): void {
|
|
914
|
+
recordDebugEvent({
|
|
915
|
+
source: "lsp",
|
|
916
|
+
level: "debug",
|
|
917
|
+
category: "semantic-request.enrollment-retry",
|
|
918
|
+
message: "LSP semantic request enrollment retry",
|
|
919
|
+
cwd: boundCwd(this.cwd),
|
|
920
|
+
...(options.control?.operationId ? { operationId: options.control.operationId } : {}),
|
|
921
|
+
data: {
|
|
922
|
+
server: truncateIdentity(this.name),
|
|
923
|
+
method: truncateIdentity(options.method),
|
|
924
|
+
outcome: options.outcome,
|
|
925
|
+
retryCount: options.retryGuard.retryCount,
|
|
926
|
+
changeKind:
|
|
927
|
+
options.error instanceof SemanticInputSynchronizationError
|
|
928
|
+
? options.error.changeKind
|
|
929
|
+
: null,
|
|
930
|
+
},
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
private async runSemanticRequestAttempt<T>(options: {
|
|
935
|
+
method: string;
|
|
936
|
+
params: unknown;
|
|
937
|
+
control?: CodeRequestControl;
|
|
938
|
+
retryGuard: SemanticInputRequestRetryGuard;
|
|
939
|
+
attempt: SemanticRequestAttemptState;
|
|
940
|
+
}): Promise<T | null> {
|
|
941
|
+
const { method, params, control, retryGuard, attempt } = options;
|
|
942
|
+
throwIfCodeRequestInterrupted(control);
|
|
943
|
+
retryGuard.assertCurrent();
|
|
944
|
+
await this.getReady(control);
|
|
945
|
+
const inputSnapshot = await this.diagnostics.synchronizeSemanticInputs(control);
|
|
946
|
+
attempt.inputRevision = inputSnapshot.revision;
|
|
947
|
+
throwIfCodeRequestInterrupted(control);
|
|
948
|
+
retryGuard.assertCurrent();
|
|
949
|
+
const rpc = this.rpc;
|
|
950
|
+
if (!rpc || this._status !== "running") {
|
|
951
|
+
throw new Error(`LSP request ${method} is unavailable because the client is not running.`);
|
|
952
|
+
}
|
|
953
|
+
const data = (await rpc.sendRequest(method, params, control)) as T | null | undefined;
|
|
954
|
+
await this.diagnostics.assertSemanticInputsCurrent(inputSnapshot, control);
|
|
955
|
+
return data ?? null;
|
|
956
|
+
}
|
|
957
|
+
|
|
723
958
|
private async request<T>(
|
|
724
959
|
method: string,
|
|
725
960
|
params: unknown,
|
|
@@ -758,15 +993,15 @@ export class LspClient {
|
|
|
758
993
|
}
|
|
759
994
|
}
|
|
760
995
|
|
|
761
|
-
/** Run a server-requested refresh through the
|
|
996
|
+
/** Run a server-requested refresh through the diagnostic-only path. */
|
|
762
997
|
private refreshForServerRequest(): Promise<DiagnosticEvidenceSummary> {
|
|
763
998
|
return this.diagnostics.refreshForServerRequest();
|
|
764
999
|
}
|
|
765
1000
|
|
|
766
1001
|
private handleServerDiagnosticRefreshRequest(): null {
|
|
767
|
-
// Defer the pass before invoking it. Its setup
|
|
768
|
-
// tracked documents, so even an async method can
|
|
769
|
-
// response on the JSON-RPC request stack.
|
|
1002
|
+
// Defer the pass before invoking it. Its setup can read and synchronize
|
|
1003
|
+
// genuinely changed tracked documents, so even an async method can delay
|
|
1004
|
+
// the null response on the JSON-RPC request stack.
|
|
770
1005
|
void Promise.resolve()
|
|
771
1006
|
.then(() => this.refreshForServerRequest())
|
|
772
1007
|
.then(
|
|
@@ -820,6 +1055,10 @@ export class LspClient {
|
|
|
820
1055
|
*/
|
|
821
1056
|
private handleRegisterCapability(params: unknown): null {
|
|
822
1057
|
const registrations = readRegistrations(params, "client/registerCapability");
|
|
1058
|
+
const diagnosticRegistrations: Array<{
|
|
1059
|
+
id: string;
|
|
1060
|
+
options: Record<string, unknown>;
|
|
1061
|
+
}> = [];
|
|
823
1062
|
for (const registration of registrations) {
|
|
824
1063
|
if (registration.method !== DOCUMENT_DIAGNOSTIC_METHOD) continue;
|
|
825
1064
|
if (!isValidDiagnosticOptions(registration.registerOptions)) {
|
|
@@ -828,7 +1067,17 @@ export class LspClient {
|
|
|
828
1067
|
"Malformed textDocument/diagnostic registration options.",
|
|
829
1068
|
);
|
|
830
1069
|
}
|
|
831
|
-
|
|
1070
|
+
diagnosticRegistrations.push({
|
|
1071
|
+
id: registration.id,
|
|
1072
|
+
options: registration.registerOptions as Record<string, unknown>,
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
for (const registration of diagnosticRegistrations) {
|
|
1076
|
+
this.dynamicRegistrations.register(
|
|
1077
|
+
DOCUMENT_DIAGNOSTIC_METHOD,
|
|
1078
|
+
registration.id,
|
|
1079
|
+
registration.options,
|
|
1080
|
+
);
|
|
832
1081
|
}
|
|
833
1082
|
return null;
|
|
834
1083
|
}
|
|
@@ -844,10 +1093,14 @@ export class LspClient {
|
|
|
844
1093
|
if (!isRecord(params) || !Array.isArray(params.unregisterations)) {
|
|
845
1094
|
throw new JsonRpcRequestError(-32602, "Malformed client/unregisterCapability params.");
|
|
846
1095
|
}
|
|
1096
|
+
const unregistrations: Array<{ id: string; method: string }> = [];
|
|
847
1097
|
for (const entry of params.unregisterations) {
|
|
848
1098
|
if (!isRecord(entry) || typeof entry.id !== "string" || typeof entry.method !== "string") {
|
|
849
1099
|
throw new JsonRpcRequestError(-32602, "Malformed client/unregisterCapability entry.");
|
|
850
1100
|
}
|
|
1101
|
+
unregistrations.push({ id: entry.id, method: entry.method });
|
|
1102
|
+
}
|
|
1103
|
+
for (const entry of unregistrations) {
|
|
851
1104
|
if (entry.method !== DOCUMENT_DIAGNOSTIC_METHOD) continue;
|
|
852
1105
|
this.dynamicRegistrations.unregister(entry.method, entry.id);
|
|
853
1106
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// JSON-RPC 2.0 transport — thin wrapper around vscode-jsonrpc.
|
|
2
2
|
// Handles Content-Length framing, request/response correlation, timeouts,
|
|
3
3
|
// and notification/request dispatching through vscode-jsonrpc's MessageConnection.
|
|
4
|
+
// biome-ignore-all lint/style/noExcessiveLinesPerFile: Transport lifetime and protocol handling stay together.
|
|
4
5
|
|
|
5
6
|
import type { Readable, Writable } from "node:stream";
|
|
6
7
|
import {
|
|
@@ -103,13 +104,44 @@ export class JsonRpcClient {
|
|
|
103
104
|
this.requestHandler = handler;
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Send one request while exposing its underlying transport lifetime.
|
|
109
|
+
* Callers that time out can stop waiting on `result` and keep the route
|
|
110
|
+
* occupied until `settled` resolves.
|
|
111
|
+
*/
|
|
112
|
+
sendRequestOwned(
|
|
113
|
+
method: string,
|
|
114
|
+
params?: unknown,
|
|
115
|
+
options?: { timeoutMs?: number } & CodeRequestControl,
|
|
116
|
+
): { result: Promise<unknown>; settled: Promise<void> } {
|
|
117
|
+
let settle!: () => void;
|
|
118
|
+
const settled = new Promise<void>((resolve) => {
|
|
119
|
+
settle = resolve;
|
|
120
|
+
});
|
|
121
|
+
let result: Promise<unknown>;
|
|
122
|
+
try {
|
|
123
|
+
result = this.sendRequest(method, params, {
|
|
124
|
+
...options,
|
|
125
|
+
onSettled: settle,
|
|
126
|
+
owned: true,
|
|
127
|
+
});
|
|
128
|
+
} catch (error) {
|
|
129
|
+
settle();
|
|
130
|
+
result = Promise.reject(error);
|
|
131
|
+
}
|
|
132
|
+
result.catch(() => {});
|
|
133
|
+
return { result, settled };
|
|
134
|
+
}
|
|
135
|
+
|
|
106
136
|
/** Send a request and wait for the correlated response, optionally overriding the timeout. */
|
|
137
|
+
// biome-ignore lint/complexity/noExcessiveLinesPerFunction: Request timeout and cancellation races stay together.
|
|
107
138
|
sendRequest(
|
|
108
139
|
method: string,
|
|
109
140
|
params?: unknown,
|
|
110
|
-
options?: { timeoutMs?: number } & CodeRequestControl,
|
|
141
|
+
options?: { timeoutMs?: number; onSettled?: () => void; owned?: boolean } & CodeRequestControl,
|
|
111
142
|
): Promise<unknown> {
|
|
112
143
|
const timeoutMs = options?.timeoutMs ?? this.timeoutMs;
|
|
144
|
+
const owned = options?.owned === true;
|
|
113
145
|
const signal = options?.signal;
|
|
114
146
|
const deadlineMs = options?.deadline === undefined ? undefined : options.deadline - Date.now();
|
|
115
147
|
const methodClass = classifyRequestMethod(method);
|
|
@@ -117,7 +149,7 @@ export class JsonRpcClient {
|
|
|
117
149
|
const timer = startDebugTimer();
|
|
118
150
|
// A request cancelled before dispatch must not produce protocol traffic.
|
|
119
151
|
if (signal?.aborted) {
|
|
120
|
-
|
|
152
|
+
const result = rejectUndispatchedRequest(
|
|
121
153
|
signal.reason ?? new Error(`Request ${method} was cancelled`),
|
|
122
154
|
{
|
|
123
155
|
timer,
|
|
@@ -126,11 +158,13 @@ export class JsonRpcClient {
|
|
|
126
158
|
identity: { server: this.server, cwd: this.cwd },
|
|
127
159
|
},
|
|
128
160
|
);
|
|
161
|
+
options?.onSettled?.();
|
|
162
|
+
return result;
|
|
129
163
|
}
|
|
130
164
|
// An expired absolute deadline must not even send the request: the caller
|
|
131
165
|
// no longer awaits a result, so no protocol traffic may start.
|
|
132
166
|
if (deadlineMs !== undefined && deadlineMs <= 0) {
|
|
133
|
-
|
|
167
|
+
const result = rejectUndispatchedRequest(new CodeRequestDeadlineError(), {
|
|
134
168
|
timer,
|
|
135
169
|
operationId: options?.operationId,
|
|
136
170
|
observation: {
|
|
@@ -141,18 +175,24 @@ export class JsonRpcClient {
|
|
|
141
175
|
},
|
|
142
176
|
identity: { server: this.server, cwd: this.cwd },
|
|
143
177
|
});
|
|
178
|
+
options?.onSettled?.();
|
|
179
|
+
return result;
|
|
144
180
|
}
|
|
145
181
|
if (this.closed || !this.connection) {
|
|
146
|
-
|
|
182
|
+
const result = rejectUndispatchedRequest(new Error("JSON-RPC client is closed"), {
|
|
147
183
|
timer,
|
|
148
184
|
operationId: options?.operationId,
|
|
149
185
|
observation: { method: boundedMethod, methodClass, outcome: "cancelled" },
|
|
150
186
|
identity: { server: this.server, cwd: this.cwd },
|
|
151
187
|
});
|
|
188
|
+
options?.onSettled?.();
|
|
189
|
+
return result;
|
|
152
190
|
}
|
|
153
191
|
const tokenSource = new CancellationTokenSource();
|
|
154
192
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
193
|
+
let transportTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
155
194
|
let timedOut = false;
|
|
195
|
+
let transportTimedOut = false;
|
|
156
196
|
let aborted = false;
|
|
157
197
|
const timerDelayMs = deadlineMs === undefined ? timeoutMs : Math.min(timeoutMs, deadlineMs);
|
|
158
198
|
const timeoutError = Object.assign(
|
|
@@ -165,16 +205,34 @@ export class JsonRpcClient {
|
|
|
165
205
|
// Catch the raw request promise to prevent unhandled rejections when
|
|
166
206
|
// dispose() cancels the token without a preceding timeout.
|
|
167
207
|
request.catch(() => {});
|
|
208
|
+
request.then(
|
|
209
|
+
() => {
|
|
210
|
+
if (transportTimeout !== undefined) clearTimeout(transportTimeout);
|
|
211
|
+
options?.onSettled?.();
|
|
212
|
+
},
|
|
213
|
+
() => {
|
|
214
|
+
if (transportTimeout !== undefined) clearTimeout(transportTimeout);
|
|
215
|
+
options?.onSettled?.();
|
|
216
|
+
},
|
|
217
|
+
);
|
|
218
|
+
if (owned) {
|
|
219
|
+
const transportTimeoutMs = Number.isFinite(timeoutMs)
|
|
220
|
+
? Math.max(this.timeoutMs, timeoutMs)
|
|
221
|
+
: this.timeoutMs;
|
|
222
|
+
transportTimeout = setTimeout(() => {
|
|
223
|
+
transportTimedOut = true;
|
|
224
|
+
tokenSource.cancel();
|
|
225
|
+
}, transportTimeoutMs);
|
|
226
|
+
}
|
|
168
227
|
|
|
169
|
-
// Race the request against
|
|
170
|
-
// the JSON-RPC token
|
|
171
|
-
//
|
|
172
|
-
// after a successful response.
|
|
228
|
+
// Race the request against the caller's timeout. Ordinary requests cancel
|
|
229
|
+
// the JSON-RPC token; owned requests leave it active so the route stays
|
|
230
|
+
// occupied until the raw request settles.
|
|
173
231
|
let abortHandler: (() => void) | undefined;
|
|
174
232
|
const abort = new Promise<never>((_resolve, reject) => {
|
|
175
233
|
abortHandler = () => {
|
|
176
234
|
aborted = true;
|
|
177
|
-
tokenSource.cancel();
|
|
235
|
+
if (!owned) tokenSource.cancel();
|
|
178
236
|
// Reject with the caller's abort reason when one exists, matching the
|
|
179
237
|
// canonical throwIfCodeRequestInterrupted() behavior.
|
|
180
238
|
reject(signal?.reason ?? abortError);
|
|
@@ -187,7 +245,7 @@ export class JsonRpcClient {
|
|
|
187
245
|
new Promise<never>((_resolve, reject) => {
|
|
188
246
|
timeout = setTimeout(() => {
|
|
189
247
|
timedOut = true;
|
|
190
|
-
tokenSource.cancel();
|
|
248
|
+
if (!owned) tokenSource.cancel();
|
|
191
249
|
// A deadline that binds earlier than the timeout is a deadline
|
|
192
250
|
// outcome, distinct from an ordinary per-request timeout.
|
|
193
251
|
reject(
|
|
@@ -214,12 +272,10 @@ export class JsonRpcClient {
|
|
|
214
272
|
return result;
|
|
215
273
|
},
|
|
216
274
|
(error: unknown) => {
|
|
217
|
-
const cancelled =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
: cancelled
|
|
221
|
-
? "cancelled"
|
|
222
|
-
: "failed";
|
|
275
|
+
const cancelled =
|
|
276
|
+
timedOut || transportTimedOut || aborted || this.closed || isCancellationError(error);
|
|
277
|
+
const outcome: RequestOutcome =
|
|
278
|
+
timedOut || transportTimedOut ? "timed-out" : cancelled ? "cancelled" : "failed";
|
|
223
279
|
const errorCode = requestErrorCode(outcome, error);
|
|
224
280
|
this.countProtocolStallFailure(outcome, errorCode, error);
|
|
225
281
|
recordRequestTiming(
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// tsconfig-aware file scope detection.
|
|
2
2
|
//
|
|
3
|
-
// Determines whether a file is within the
|
|
4
|
-
// tsconfig.json or jsconfig.json using the
|
|
5
|
-
// parsing APIs. Used by the diagnostic filter
|
|
6
|
-
// that TypeScript itself would not include
|
|
3
|
+
// Determines whether a TypeScript or JavaScript-family file is within the
|
|
4
|
+
// compilation scope of its nearest tsconfig.json or jsconfig.json using the
|
|
5
|
+
// TypeScript compiler's own config parsing APIs. Used by the diagnostic filter
|
|
6
|
+
// to suppress LSP errors on files that TypeScript itself would not include.
|
|
7
7
|
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import ts from "typescript";
|
|
@@ -54,6 +54,23 @@ export interface FileScopeDecision {
|
|
|
54
54
|
caseSensitiveFileNames: boolean;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
const TYPESCRIPT_JAVASCRIPT_EXTENSIONS = new Set([
|
|
58
|
+
".ts",
|
|
59
|
+
".tsx",
|
|
60
|
+
".mts",
|
|
61
|
+
".cts",
|
|
62
|
+
".js",
|
|
63
|
+
".jsx",
|
|
64
|
+
".mjs",
|
|
65
|
+
".cjs",
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
/** Whether tsconfig/jsconfig filtering applies to a TypeScript or JavaScript-family file. */
|
|
69
|
+
export function isTsconfigApplicableFile(filePath: string): boolean {
|
|
70
|
+
const normalizedPath = filePath.replaceAll("\\", "/");
|
|
71
|
+
return TYPESCRIPT_JAVASCRIPT_EXTENSIONS.has(path.extname(normalizedPath).toLowerCase());
|
|
72
|
+
}
|
|
73
|
+
|
|
57
74
|
const nearestConfigCache = new Map<string, string | null>();
|
|
58
75
|
const parsedConfigCache = new Map<string, ParsedProjectConfigCacheEntry>();
|
|
59
76
|
|
|
@@ -82,6 +99,10 @@ const tsInternal = ts as typeof ts & {
|
|
|
82
99
|
* @returns `true` if the file is excluded from compilation scope
|
|
83
100
|
*/
|
|
84
101
|
export function isFileExcludedByTsconfig(filePath: string, cwd: string): boolean {
|
|
102
|
+
// TypeScript project scope does not decide whether another language's LSP
|
|
103
|
+
// diagnostics belong in the automatic workspace summary.
|
|
104
|
+
if (!isTsconfigApplicableFile(filePath)) return false;
|
|
105
|
+
|
|
85
106
|
// Legacy filter semantics: only a resolved "excluded" decision filters a
|
|
86
107
|
// file. Out-of-tree and no-config files stay unfiltered; the decision API
|
|
87
108
|
// still reports those statuses honestly for consumers that want them.
|
|
@@ -91,6 +112,11 @@ export function isFileExcludedByTsconfig(filePath: string, cwd: string): boolean
|
|
|
91
112
|
/**
|
|
92
113
|
* Compute the explainable tsconfig scope decision for one file.
|
|
93
114
|
*
|
|
115
|
+
* This low-level TypeScript compilation-membership query returns an
|
|
116
|
+
* extension-based decision for other languages. Diagnostic filtering and
|
|
117
|
+
* display first use {@link isTsconfigApplicableFile}; language-neutral
|
|
118
|
+
* consumers should use that helper or {@link isFileExcludedByTsconfig} instead.
|
|
119
|
+
*
|
|
94
120
|
* The decision carries the mechanism that produced it (the scope decision
|
|
95
121
|
* basis) so consumers can report why a file is inside or outside the nearest
|
|
96
122
|
* project config without re-deriving the scope logic.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Reason for a partial or unavailable single-file result when the push stays tentative. */
|
|
2
2
|
export const TENTATIVE_PUSH_UNAVAILABLE_REASON =
|
|
3
|
-
"The current
|
|
3
|
+
"The current diagnostic publication is ambient evidence and cannot confirm the document synchronization.";
|
|
4
4
|
|
|
5
5
|
/** A final evidence state for one tracked document. */
|
|
6
6
|
export type DiagnosticEvidenceStatus = "confirmed" | "unconfirmed" | "failed" | "removed";
|