@mrclrchtr/supi-lsp 6.2.0 → 6.3.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 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/ignore/README.md +52 -0
- package/node_modules/ignore/index.js +785 -129
- package/node_modules/ignore/legacy.js +660 -98
- package/node_modules/ignore/package.json +10 -4
- package/package.json +3 -3
- package/src/api.ts +6 -2
- package/src/manager/manager-process-crash-report.ts +106 -0
- package/src/manager/manager-workspace-recovery.ts +4 -4
- package/src/manager/manager.ts +348 -99
- package/src/session/runtime-diagnostics.ts +43 -15
- package/src/session/runtime-registry.ts +23 -32
- package/src/session/workspace-lsp-runtime.ts +4 -4
- package/src/summary.ts +1 -1
- package/src/workspace-path-policy.ts +18 -9
|
@@ -45,26 +45,54 @@ export interface OutstandingDiagnosticSummaryEntry {
|
|
|
45
45
|
hints: number;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
/** Stable outcome for one route in an explicit process-crash report. */
|
|
49
|
+
export type ProcessCrashRecoveryOutcome =
|
|
50
|
+
| "recovered"
|
|
51
|
+
| "skipped-no-retained-file"
|
|
52
|
+
| "recovery-failed"
|
|
53
|
+
| "recovery-exhausted";
|
|
54
|
+
|
|
55
|
+
/** Typed next action for a non-recovered process-crash route. */
|
|
56
|
+
export type ProcessCrashRecoveryNextAction = "use-exact-file" | "reload-workspace";
|
|
57
|
+
|
|
58
|
+
/** One bounded route entry in a process-crash recovery report. */
|
|
59
|
+
export interface ProcessCrashRecoveryEntry {
|
|
60
|
+
/** Configured server name. */
|
|
61
|
+
readonly name: string;
|
|
62
|
+
/** Workspace-relative route root. */
|
|
63
|
+
readonly root: string;
|
|
64
|
+
readonly outcome: ProcessCrashRecoveryOutcome;
|
|
65
|
+
/** Available action when the route was not recovered. */
|
|
66
|
+
readonly nextAction?: ProcessCrashRecoveryNextAction;
|
|
67
|
+
/** Bounded caught Error.message for a failed recovery, when available. */
|
|
68
|
+
readonly failureMessage?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Maximum number of route entries retained in a process-crash report. */
|
|
72
|
+
export const MAX_PROCESS_CRASH_RECOVERY_ENTRIES = 16;
|
|
73
|
+
|
|
74
|
+
/** Bounded, route-specific result of one explicit process-crash demand. */
|
|
75
|
+
export interface ProcessCrashRecoveryReport {
|
|
76
|
+
/** Exact counts include entries omitted from the visible route list. */
|
|
77
|
+
readonly recoveredRoutes: number;
|
|
78
|
+
readonly skippedRoutes: number;
|
|
79
|
+
readonly failedRoutes: number;
|
|
80
|
+
readonly exhaustedRoutes: number;
|
|
81
|
+
/** At most 16 entries, ordered by action priority, root, and server name. */
|
|
82
|
+
readonly entries: readonly ProcessCrashRecoveryEntry[];
|
|
83
|
+
/** Exact number of route entries not included in {@link entries}. */
|
|
84
|
+
readonly omittedEntries: number;
|
|
60
85
|
}
|
|
61
86
|
|
|
62
87
|
/** Create the empty outcome for a pass with no process-crash demand. */
|
|
63
|
-
export function
|
|
88
|
+
export function emptyProcessCrashRecoveryReport(): ProcessCrashRecoveryReport {
|
|
64
89
|
return {
|
|
65
|
-
attemptedRoutes: 0,
|
|
66
90
|
recoveredRoutes: 0,
|
|
91
|
+
skippedRoutes: 0,
|
|
67
92
|
failedRoutes: 0,
|
|
93
|
+
exhaustedRoutes: 0,
|
|
94
|
+
entries: [],
|
|
95
|
+
omittedEntries: 0,
|
|
68
96
|
};
|
|
69
97
|
}
|
|
70
98
|
|
|
@@ -75,7 +103,7 @@ export interface RecoverDiagnosticsResult {
|
|
|
75
103
|
/** Clients restarted by stale-diagnostic recovery, not process-crash recovery. */
|
|
76
104
|
restartedClients: number;
|
|
77
105
|
/** Separate outcome for process-crash route recovery selected by this pass. */
|
|
78
|
-
processCrashRecovery:
|
|
106
|
+
processCrashRecovery: ProcessCrashRecoveryReport;
|
|
79
107
|
/** Evidence collected by the refresh and recovery operations. */
|
|
80
108
|
diagnosticEvidence: DiagnosticEvidenceSummary;
|
|
81
109
|
/** Final diagnostic report captured after all refresh and recovery work. */
|
|
@@ -39,7 +39,7 @@ import type { LspManager } from "../manager/manager.ts";
|
|
|
39
39
|
import { raceReadinessValue } from "./readiness.ts";
|
|
40
40
|
import type {
|
|
41
41
|
DiagnosticEvidenceSummary,
|
|
42
|
-
|
|
42
|
+
ProcessCrashRecoveryReport,
|
|
43
43
|
RecoverDiagnosticsResult,
|
|
44
44
|
} from "./runtime-diagnostics.ts";
|
|
45
45
|
import type {
|
|
@@ -57,9 +57,13 @@ export {
|
|
|
57
57
|
type DiagnosticEvidenceDocument,
|
|
58
58
|
type DiagnosticEvidenceStatus,
|
|
59
59
|
type DiagnosticEvidenceSummary,
|
|
60
|
-
|
|
60
|
+
emptyProcessCrashRecoveryReport,
|
|
61
|
+
MAX_PROCESS_CRASH_RECOVERY_ENTRIES,
|
|
61
62
|
type OutstandingDiagnosticSummaryEntry,
|
|
62
|
-
type
|
|
63
|
+
type ProcessCrashRecoveryEntry,
|
|
64
|
+
type ProcessCrashRecoveryNextAction,
|
|
65
|
+
type ProcessCrashRecoveryOutcome,
|
|
66
|
+
type ProcessCrashRecoveryReport,
|
|
63
67
|
type RecoverDiagnosticsResult,
|
|
64
68
|
type WorkspaceDiagnosticReport,
|
|
65
69
|
type WorkspaceDiagnosticSnapshot,
|
|
@@ -80,27 +84,13 @@ function unavailableFileQuery<T>(operation: string, file: string): CodeQueryResu
|
|
|
80
84
|
return unavailableCodeQuery(`No routed LSP client could complete ${operation} for ${file}.`);
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
function hasProcessCrashRecovery(
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
* one route when both views describe the same recovery attempt.
|
|
91
|
-
*/
|
|
92
|
-
function mergeProcessCrashRecoverySummaries(
|
|
93
|
-
eager: ProcessCrashRecoverySummary | undefined,
|
|
94
|
-
current: ProcessCrashRecoverySummary | null,
|
|
95
|
-
): ProcessCrashRecoverySummary | undefined {
|
|
96
|
-
if (!eager) return current ?? undefined;
|
|
97
|
-
if (!current) return eager;
|
|
98
|
-
if (current.failedRoutes > 0) return current;
|
|
99
|
-
return {
|
|
100
|
-
attemptedRoutes: Math.max(eager.attemptedRoutes, current.attemptedRoutes),
|
|
101
|
-
recoveredRoutes: Math.max(eager.recoveredRoutes, current.recoveredRoutes),
|
|
102
|
-
failedRoutes: Math.max(eager.failedRoutes, current.failedRoutes),
|
|
103
|
-
};
|
|
87
|
+
function hasProcessCrashRecovery(report: ProcessCrashRecoveryReport): boolean {
|
|
88
|
+
return (
|
|
89
|
+
report.recoveredRoutes > 0 ||
|
|
90
|
+
report.skippedRoutes > 0 ||
|
|
91
|
+
report.failedRoutes > 0 ||
|
|
92
|
+
report.exhaustedRoutes > 0
|
|
93
|
+
);
|
|
104
94
|
}
|
|
105
95
|
|
|
106
96
|
/** Emit one aggregate tsconfig scope-decision event after a recovery pass. */
|
|
@@ -282,7 +272,7 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
282
272
|
): Promise<SemanticReadinessResult> {
|
|
283
273
|
const resolvedPath = this.resolveFilePath(filePath);
|
|
284
274
|
if (!this.manager.canServeFile(resolvedPath)) {
|
|
285
|
-
const processCrashRecovery = this.manager.
|
|
275
|
+
const processCrashRecovery = this.manager.getProcessCrashRecoveryReportForFile(resolvedPath);
|
|
286
276
|
return {
|
|
287
277
|
kind: "unavailable",
|
|
288
278
|
reason: "No LSP client can serve this file",
|
|
@@ -290,19 +280,20 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
290
280
|
};
|
|
291
281
|
}
|
|
292
282
|
|
|
293
|
-
let eagerProcessCrashRecovery:
|
|
283
|
+
let eagerProcessCrashRecovery: ProcessCrashRecoveryReport | undefined;
|
|
294
284
|
const readiness = await raceReadinessValue(
|
|
295
|
-
this.manager.waitUntilFileReady(resolvedPath, control, (
|
|
296
|
-
eagerProcessCrashRecovery =
|
|
285
|
+
this.manager.waitUntilFileReady(resolvedPath, control, (report) => {
|
|
286
|
+
eagerProcessCrashRecovery = report;
|
|
297
287
|
}),
|
|
298
288
|
options.timeoutMs,
|
|
299
289
|
control,
|
|
300
290
|
);
|
|
301
291
|
if (readiness.kind !== "resolved") {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
292
|
+
// A timeout can happen before shared recovery settles. Do not create a
|
|
293
|
+
// route outcome for work that has no final result yet.
|
|
294
|
+
const currentProcessCrashRecovery =
|
|
295
|
+
this.manager.getProcessCrashRecoveryReportForFile(resolvedPath);
|
|
296
|
+
const processCrashRecovery = currentProcessCrashRecovery ?? eagerProcessCrashRecovery;
|
|
306
297
|
return processCrashRecovery ? { ...readiness, processCrashRecovery } : readiness;
|
|
307
298
|
}
|
|
308
299
|
const { client, processCrashRecovery } = readiness.value;
|
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
WorkspaceSentinelSyncResult,
|
|
19
19
|
} from "../diagnostics/workspace-sentinels.ts";
|
|
20
20
|
import type { WorkspaceLspDiagnosticSurface } from "./runtime-diagnostic-surface.ts";
|
|
21
|
-
import type {
|
|
21
|
+
import type { ProcessCrashRecoveryReport } from "./runtime-diagnostics.ts";
|
|
22
22
|
|
|
23
23
|
export type WorkspaceLspRuntimeState =
|
|
24
24
|
| { kind: "ready"; runtime: WorkspaceLspRuntime }
|
|
@@ -31,18 +31,18 @@ export type SemanticReadinessResult =
|
|
|
31
31
|
| {
|
|
32
32
|
kind: "ready";
|
|
33
33
|
/** Process-crash route recovery observed while establishing file readiness. */
|
|
34
|
-
processCrashRecovery?:
|
|
34
|
+
processCrashRecovery?: ProcessCrashRecoveryReport;
|
|
35
35
|
}
|
|
36
36
|
| {
|
|
37
37
|
kind: "timeout";
|
|
38
38
|
/** Process-crash route recovery observed before the readiness timeout. */
|
|
39
|
-
processCrashRecovery?:
|
|
39
|
+
processCrashRecovery?: ProcessCrashRecoveryReport;
|
|
40
40
|
}
|
|
41
41
|
| {
|
|
42
42
|
kind: "unavailable";
|
|
43
43
|
reason: string;
|
|
44
44
|
/** Process-crash route recovery observed before readiness became unavailable. */
|
|
45
|
-
processCrashRecovery?:
|
|
45
|
+
processCrashRecovery?: ProcessCrashRecoveryReport;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
/** One mutation response and the exact provider roots from its semantic route. */
|
package/src/summary.ts
CHANGED
|
@@ -94,7 +94,7 @@ import { isFileExcludedByTsconfig } from "./config/tsconfig-scope.ts";
|
|
|
94
94
|
|
|
95
95
|
/** Check whether the supplied automatic path policy allows a file path. */
|
|
96
96
|
export function isInProjectTree(filePath: string, policy: AutomaticLspPathPolicy): boolean {
|
|
97
|
-
return policy.isEligible(filePath);
|
|
97
|
+
return policy.isEligible(filePath, "file");
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/** Check automatic and tsconfig diagnostic scope for one file path. */
|
|
@@ -26,7 +26,10 @@ const BUILT_IN_EXCLUSIONS = new Set(AUTOMATIC_LSP_EXCLUDED_DIRECTORIES);
|
|
|
26
26
|
export interface AutomaticLspPathPolicy {
|
|
27
27
|
/** Canonical absolute workspace root used for all matching. */
|
|
28
28
|
readonly workspaceRoot: string;
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* True when automatic LSP work can use the path.
|
|
31
|
+
* The policy reads the path kind when the caller does not supply it.
|
|
32
|
+
*/
|
|
30
33
|
isEligible(candidate: string, kind?: "file" | "directory"): boolean;
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -40,17 +43,14 @@ export function createAutomaticLspPathPolicy(
|
|
|
40
43
|
const repositoryRules = compileRepositoryRules(canonicalRoot, configuredExclusions);
|
|
41
44
|
return Object.freeze({
|
|
42
45
|
workspaceRoot: canonicalRoot,
|
|
43
|
-
isEligible(candidate: string, kind
|
|
46
|
+
isEligible(candidate: string, kind?: "file" | "directory"): boolean {
|
|
44
47
|
const relativePath = normalizeCandidate(canonicalRoot, workspaceRoot, candidate);
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
48
|
+
if (relativePath === null) return false;
|
|
49
|
+
const candidateKind = kind ?? readCandidateKind(canonicalRoot, relativePath);
|
|
50
|
+
if (!isAutomaticCandidateAllowed(workspaceRoot, relativePath, candidateKind)) return false;
|
|
51
51
|
return isEligibleRelativePath(
|
|
52
52
|
relativePath,
|
|
53
|
-
|
|
53
|
+
candidateKind === "directory",
|
|
54
54
|
configuredExclusions,
|
|
55
55
|
repositoryRules,
|
|
56
56
|
);
|
|
@@ -78,6 +78,7 @@ export function walkAutomaticLspTree(
|
|
|
78
78
|
depth: number,
|
|
79
79
|
onDirectory: (directory: string, entries: readonly Dirent[]) => boolean | undefined,
|
|
80
80
|
): void {
|
|
81
|
+
if (!policy.isEligible(directory, "directory")) return;
|
|
81
82
|
walkDirectoryTree({
|
|
82
83
|
directory,
|
|
83
84
|
depth,
|
|
@@ -220,6 +221,14 @@ function relativeToRuleBase(base: string, relativePath: string): string | null {
|
|
|
220
221
|
return relativePath.startsWith(`${base}/`) ? relativePath.slice(base.length + 1) : null;
|
|
221
222
|
}
|
|
222
223
|
|
|
224
|
+
function readCandidateKind(canonicalRoot: string, relativePath: string): "file" | "directory" {
|
|
225
|
+
try {
|
|
226
|
+
return statSync(path.resolve(canonicalRoot, relativePath)).isDirectory() ? "directory" : "file";
|
|
227
|
+
} catch {
|
|
228
|
+
return "file";
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
223
232
|
function isAutomaticCandidateAllowed(
|
|
224
233
|
suppliedRoot: string,
|
|
225
234
|
relativePath: string,
|