@mrclrchtr/supi-code-intelligence 6.0.1 → 6.1.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 +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +5 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/README.md +5 -1
- 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/capability/types.ts +5 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-jsonrpc/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.colorProvider.d.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.d.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.fileOperations.d.ts +9 -9
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/esm/main.d.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/esm/main.js +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/umd/main.d.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/umd/main.js +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +6 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +582 -26
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +2 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +10 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +36 -9
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +4 -2
- 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/capability/types.ts +5 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -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/package.json +5 -5
- package/src/analysis/health/recovery.ts +4 -0
- package/src/analysis/health/server-status.ts +64 -0
- package/src/analysis/health/signals.ts +1 -0
- package/src/analysis/provider.ts +2 -1
- package/src/analysis/target/symbol.ts +28 -16
- package/src/analysis/target/types.ts +1 -0
- package/src/session/find-workflow.ts +5 -12
- package/src/session/health-refresh.ts +6 -2
- package/src/session/health-types.ts +8 -2
- package/src/session/health-workflow.ts +5 -1
- package/src/session/semantic-demand.ts +36 -0
- package/src/session/target-workflow.ts +9 -12
- package/src/tool/code_health/guidance.ts +2 -2
- package/src/tool/code_health/markdown.ts +15 -3
- package/src/tool/code_health/result.ts +14 -5
- package/src/tool/code_health/spec.ts +2 -1
- package/src/tool/code_health/tui.ts +24 -3
- package/src/tool/result/errors.ts +1 -0
- package/src/tool/result/types.ts +6 -0
- package/src/ui/footer.ts +7 -5
- package/src/ui/status-command.ts +31 -0
- package/src/ui/status-overlay.ts +69 -23
|
@@ -92,10 +92,9 @@ export function createLspSemanticProvider(lsp: WorkspaceLspRuntime): SemanticPro
|
|
|
92
92
|
async workspaceSymbols(
|
|
93
93
|
query: string,
|
|
94
94
|
control?: CodeRequestControl,
|
|
95
|
+
scopes?: readonly string[],
|
|
95
96
|
): Promise<CodeQueryResult<CodeSymbol[]>> {
|
|
96
|
-
const result = control
|
|
97
|
-
? await lsp.workspaceSymbol(query, control)
|
|
98
|
-
: await lsp.workspaceSymbol(query);
|
|
97
|
+
const result = await lsp.workspaceSymbol(query, control, scopes);
|
|
99
98
|
return mapCodeQueryResult(result, (results) =>
|
|
100
99
|
results.map((symbol) => toCodeSymbol(symbol as SymbolInformation)),
|
|
101
100
|
);
|
|
@@ -8,6 +8,14 @@ import type {
|
|
|
8
8
|
WorkspaceDiagnosticSummaryEntry,
|
|
9
9
|
} from "./runtime-diagnostics.ts";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Explicit diagnostic demand that can recover previously running crashed
|
|
13
|
+
* routes. Omitted scopes include every route with retained tracked files.
|
|
14
|
+
*/
|
|
15
|
+
export interface ProcessCrashDiagnosticDemand {
|
|
16
|
+
readonly scopes?: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
/** Diagnostic and recovery operations owned by a workspace LSP runtime. */
|
|
12
20
|
export interface WorkspaceLspDiagnosticSurface {
|
|
13
21
|
fileDiagnostics(
|
|
@@ -37,6 +45,8 @@ export interface WorkspaceLspDiagnosticSurface {
|
|
|
37
45
|
quietMs?: number;
|
|
38
46
|
/** Evidence from a refresh the caller already completed; skips this pass's own refresh when no watched-file changes apply. */
|
|
39
47
|
initialEvidence?: DiagnosticEvidenceSummary;
|
|
48
|
+
/** Explicit demand to recover crashed routes with tracked files in scope. */
|
|
49
|
+
processCrashDemand?: ProcessCrashDiagnosticDemand;
|
|
40
50
|
control?: CodeRequestControl;
|
|
41
51
|
}): Promise<RecoverDiagnosticsResult>;
|
|
42
52
|
}
|
|
@@ -38,7 +38,10 @@ import type {
|
|
|
38
38
|
WorkspaceLspRuntimeState,
|
|
39
39
|
} from "./workspace-lsp-runtime.ts";
|
|
40
40
|
|
|
41
|
-
export type {
|
|
41
|
+
export type {
|
|
42
|
+
ProcessCrashDiagnosticDemand,
|
|
43
|
+
WorkspaceLspDiagnosticSurface,
|
|
44
|
+
} from "./runtime-diagnostic-surface.ts";
|
|
42
45
|
export type {
|
|
43
46
|
DiagnosticEvidenceDocument,
|
|
44
47
|
DiagnosticEvidenceStatus,
|
|
@@ -107,7 +110,10 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
107
110
|
control?: CodeRequestControl,
|
|
108
111
|
): Promise<CodeQueryResult<Hover | null>> {
|
|
109
112
|
const resolvedPath = this.resolveFilePath(filePath);
|
|
110
|
-
const client = await this.manager.ensureFileOpen(resolvedPath
|
|
113
|
+
const client = await this.manager.ensureFileOpen(resolvedPath, {
|
|
114
|
+
recoverProcessCrash: true,
|
|
115
|
+
control,
|
|
116
|
+
});
|
|
111
117
|
if (!client) return unavailableFileQuery("hover", resolvedPath);
|
|
112
118
|
return client.hover(resolvedPath, position, control);
|
|
113
119
|
}
|
|
@@ -118,7 +124,10 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
118
124
|
control?: CodeRequestControl,
|
|
119
125
|
): Promise<CodeQueryResult<Location | Location[] | LocationLink[] | null>> {
|
|
120
126
|
const resolvedPath = this.resolveFilePath(filePath);
|
|
121
|
-
const client = await this.manager.ensureFileOpen(resolvedPath
|
|
127
|
+
const client = await this.manager.ensureFileOpen(resolvedPath, {
|
|
128
|
+
recoverProcessCrash: true,
|
|
129
|
+
control,
|
|
130
|
+
});
|
|
122
131
|
if (!client) return unavailableFileQuery("definition", resolvedPath);
|
|
123
132
|
return client.definition(resolvedPath, position, control);
|
|
124
133
|
}
|
|
@@ -129,7 +138,10 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
129
138
|
control?: CodeRequestControl,
|
|
130
139
|
): Promise<CodeQueryResult<Location[]>> {
|
|
131
140
|
const resolvedPath = this.resolveFilePath(filePath);
|
|
132
|
-
const client = await this.manager.ensureFileOpen(resolvedPath
|
|
141
|
+
const client = await this.manager.ensureFileOpen(resolvedPath, {
|
|
142
|
+
recoverProcessCrash: true,
|
|
143
|
+
control,
|
|
144
|
+
});
|
|
133
145
|
if (!client) return unavailableFileQuery("references", resolvedPath);
|
|
134
146
|
return mapCodeQueryResult(
|
|
135
147
|
await client.references(resolvedPath, position, control),
|
|
@@ -143,7 +155,10 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
143
155
|
control?: CodeRequestControl,
|
|
144
156
|
): Promise<CodeQueryResult<Location | Location[] | LocationLink[] | null>> {
|
|
145
157
|
const resolvedPath = this.resolveFilePath(filePath);
|
|
146
|
-
const client = await this.manager.ensureFileOpen(resolvedPath
|
|
158
|
+
const client = await this.manager.ensureFileOpen(resolvedPath, {
|
|
159
|
+
recoverProcessCrash: true,
|
|
160
|
+
control,
|
|
161
|
+
});
|
|
147
162
|
if (!client) return unavailableFileQuery("implementation", resolvedPath);
|
|
148
163
|
return client.implementation(resolvedPath, position, control);
|
|
149
164
|
}
|
|
@@ -153,7 +168,10 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
153
168
|
control?: CodeRequestControl,
|
|
154
169
|
): Promise<CodeQueryResult<DocumentSymbol[] | SymbolInformation[]>> {
|
|
155
170
|
const resolvedPath = this.resolveFilePath(filePath);
|
|
156
|
-
const client = await this.manager.ensureFileOpen(resolvedPath
|
|
171
|
+
const client = await this.manager.ensureFileOpen(resolvedPath, {
|
|
172
|
+
recoverProcessCrash: true,
|
|
173
|
+
control,
|
|
174
|
+
});
|
|
157
175
|
if (!client) return unavailableFileQuery("document symbols", resolvedPath);
|
|
158
176
|
return mapCodeQueryResult(
|
|
159
177
|
await client.documentSymbols(resolvedPath, control),
|
|
@@ -164,8 +182,9 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
164
182
|
async workspaceSymbol(
|
|
165
183
|
query: string,
|
|
166
184
|
control?: CodeRequestControl,
|
|
185
|
+
scopes?: readonly string[],
|
|
167
186
|
): Promise<CodeQueryResult<SymbolInformation[] | WorkspaceSymbol[]>> {
|
|
168
|
-
return this.manager.workspaceSymbol(query, control);
|
|
187
|
+
return this.manager.workspaceSymbol(query, control, scopes);
|
|
169
188
|
}
|
|
170
189
|
|
|
171
190
|
async rename(
|
|
@@ -175,7 +194,10 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
175
194
|
control?: CodeRequestControl,
|
|
176
195
|
): Promise<RoutedMutationResponse<WorkspaceEdit | null> | null> {
|
|
177
196
|
const resolvedPath = this.resolveFilePath(filePath);
|
|
178
|
-
const client = await this.manager.ensureFileOpen(resolvedPath
|
|
197
|
+
const client = await this.manager.ensureFileOpen(resolvedPath, {
|
|
198
|
+
recoverProcessCrash: true,
|
|
199
|
+
control,
|
|
200
|
+
});
|
|
179
201
|
if (!client) return null;
|
|
180
202
|
return {
|
|
181
203
|
value: await client.rename(resolvedPath, position, newName, control),
|
|
@@ -193,7 +215,10 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
193
215
|
control?: CodeRequestControl,
|
|
194
216
|
): Promise<RoutedMutationResponse<CodeAction[] | null> | null> {
|
|
195
217
|
const resolvedPath = this.resolveFilePath(filePath);
|
|
196
|
-
const client = await this.manager.ensureFileOpen(resolvedPath
|
|
218
|
+
const client = await this.manager.ensureFileOpen(resolvedPath, {
|
|
219
|
+
recoverProcessCrash: true,
|
|
220
|
+
control,
|
|
221
|
+
});
|
|
197
222
|
if (!client) return null;
|
|
198
223
|
|
|
199
224
|
const range = isRange(positionOrRange)
|
|
@@ -345,6 +370,8 @@ class DefaultWorkspaceLspRuntime implements WorkspaceLspRuntime {
|
|
|
345
370
|
quietMs?: number;
|
|
346
371
|
/** Evidence from a refresh the caller already completed; skips this pass's own refresh when no watched-file changes apply. */
|
|
347
372
|
initialEvidence?: DiagnosticEvidenceSummary;
|
|
373
|
+
/** Explicit demand to recover crashed routes with tracked files in scope. */
|
|
374
|
+
processCrashDemand?: import("./runtime-diagnostic-surface.ts").ProcessCrashDiagnosticDemand;
|
|
348
375
|
control?: CodeRequestControl;
|
|
349
376
|
}): Promise<RecoverDiagnosticsResult> {
|
|
350
377
|
const recoveryStartedAt = Date.now();
|
|
@@ -8,6 +8,7 @@ export interface TransitionServerEntry {
|
|
|
8
8
|
readonly name: string;
|
|
9
9
|
readonly status: ProjectServerInfo["status"];
|
|
10
10
|
readonly ready: boolean;
|
|
11
|
+
readonly statusReason?: NonNullable<ProjectServerInfo["statusReason"]>;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/** Bound a project-server snapshot to MAX_SERVERS name/status/ready entries. */
|
|
@@ -18,6 +19,7 @@ function boundedServerEntries(
|
|
|
18
19
|
name: truncateIdentity(server.name),
|
|
19
20
|
status: server.status,
|
|
20
21
|
ready: server.ready,
|
|
22
|
+
...(server.statusReason ? { statusReason: server.statusReason } : {}),
|
|
21
23
|
}));
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -40,8 +40,8 @@ export interface RoutedMutationResponse<T> {
|
|
|
40
40
|
* diagnostics, and recovery without exposing clients or the mutable manager.
|
|
41
41
|
* File paths can be absolute or session-cwd-relative. A leading `@` is removed to
|
|
42
42
|
* match Pi's built-in path-tool convention. Positions use raw 0-based LSP coordinates.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* Request control is forwarded to the routed client and limits the caller's
|
|
44
|
+
* wait without cancelling shared route recovery.
|
|
45
45
|
*/
|
|
46
46
|
export interface WorkspaceLspRuntime extends WorkspaceLspDiagnosticSurface {
|
|
47
47
|
hover(
|
|
@@ -68,9 +68,11 @@ export interface WorkspaceLspRuntime extends WorkspaceLspDiagnosticSurface {
|
|
|
68
68
|
filePath: string,
|
|
69
69
|
control?: CodeRequestControl,
|
|
70
70
|
): Promise<CodeQueryResult<DocumentSymbol[] | SymbolInformation[]>>;
|
|
71
|
+
/** Collect symbols from routes that can contribute within the optional path scopes. */
|
|
71
72
|
workspaceSymbol(
|
|
72
73
|
query: string,
|
|
73
74
|
control?: CodeRequestControl,
|
|
75
|
+
scopes?: readonly string[],
|
|
74
76
|
): Promise<CodeQueryResult<SymbolInformation[] | WorkspaceSymbol[]>>;
|
|
75
77
|
rename(
|
|
76
78
|
filePath: string,
|
|
@@ -75,9 +75,14 @@ export interface SemanticProvider {
|
|
|
75
75
|
filePath: string,
|
|
76
76
|
control?: CodeRequestControl,
|
|
77
77
|
): Promise<CodeQueryResult<DocumentCodeSymbol[]>>;
|
|
78
|
+
/**
|
|
79
|
+
* Collect workspace symbols from routes that can contribute within the
|
|
80
|
+
* optional canonical path scopes.
|
|
81
|
+
*/
|
|
78
82
|
workspaceSymbols(
|
|
79
83
|
query: string,
|
|
80
84
|
control?: CodeRequestControl,
|
|
85
|
+
scopes?: readonly string[],
|
|
81
86
|
): Promise<CodeQueryResult<CodeSymbol[]>>;
|
|
82
87
|
|
|
83
88
|
/** Optional definition capability with explicit completed-empty semantics. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-tree-sitter",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "Structural AST analysis for SuPi code intelligence",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"jiti": "^2.7.0",
|
|
38
38
|
"web-tree-sitter": "^0.26.8",
|
|
39
|
-
"@mrclrchtr/supi-code-runtime": "6.0
|
|
40
|
-
"@mrclrchtr/supi-core": "6.0
|
|
39
|
+
"@mrclrchtr/supi-code-runtime": "6.1.0",
|
|
40
|
+
"@mrclrchtr/supi-core": "6.1.0"
|
|
41
41
|
},
|
|
42
42
|
"bundledDependencies": [
|
|
43
43
|
"@mrclrchtr/supi-code-runtime",
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"releaseAsset": "https://github.com/fwcd/tree-sitter-kotlin/releases/download/0.3.8/tree-sitter-kotlin.wasm"
|
|
7
7
|
},
|
|
8
8
|
"generatedWith": {
|
|
9
|
-
"treeSitterCli": "0.26.
|
|
9
|
+
"treeSitterCli": "0.26.13"
|
|
10
10
|
},
|
|
11
11
|
"sha256": "e08447e36ad5d86f39f91f13defce0636aea1490b8d65dafde6fa72649422061"
|
|
12
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-code-intelligence",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "LSP and AST navigation, search, diagnostics, and refactoring",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"yaml": "^2.9.0",
|
|
38
|
-
"@mrclrchtr/supi-
|
|
39
|
-
"@mrclrchtr/supi-
|
|
40
|
-
"@mrclrchtr/supi-
|
|
41
|
-
"@mrclrchtr/supi-
|
|
38
|
+
"@mrclrchtr/supi-lsp": "6.1.0",
|
|
39
|
+
"@mrclrchtr/supi-core": "6.1.0",
|
|
40
|
+
"@mrclrchtr/supi-code-runtime": "6.1.0",
|
|
41
|
+
"@mrclrchtr/supi-tree-sitter": "6.1.0"
|
|
42
42
|
},
|
|
43
43
|
"bundledDependencies": [
|
|
44
44
|
"@mrclrchtr/supi-code-runtime",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { CapabilityState, CodeRequestControl } from "@mrclrchtr/supi-code-runtime/api";
|
|
5
5
|
import type {
|
|
6
6
|
DiagnosticEvidenceSummary,
|
|
7
|
+
ProcessCrashDiagnosticDemand,
|
|
7
8
|
RecoverDiagnosticsResult,
|
|
8
9
|
WorkspaceLspRuntime,
|
|
9
10
|
} from "@mrclrchtr/supi-lsp/api";
|
|
@@ -16,6 +17,8 @@ interface RecoverOptions {
|
|
|
16
17
|
control?: CodeRequestControl;
|
|
17
18
|
/** Evidence from the maintenance refresh, so the recovery pass skips its own refresh. */
|
|
18
19
|
initialEvidence?: DiagnosticEvidenceSummary;
|
|
20
|
+
/** Explicit demand for crashed diagnostic routes before diagnostic recovery. */
|
|
21
|
+
processCrashDemand?: ProcessCrashDiagnosticDemand;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
/** Run the runtime's best-effort recovery and preserve its established outcome. */
|
|
@@ -26,6 +29,7 @@ export async function recoverDiagnosticRuntime(
|
|
|
26
29
|
return opts.service.recoverDiagnostics({
|
|
27
30
|
restartIfStillStale: true,
|
|
28
31
|
...(opts.initialEvidence !== undefined ? { initialEvidence: opts.initialEvidence } : {}),
|
|
32
|
+
...(opts.processCrashDemand ? { processCrashDemand: opts.processCrashDemand } : {}),
|
|
29
33
|
control: opts.control,
|
|
30
34
|
});
|
|
31
35
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import type { ProjectServerInfo, ProjectServerStatusReason } from "@mrclrchtr/supi-lsp/api";
|
|
3
|
+
|
|
4
|
+
/** Workspace-wide counts for non-running LSP routes. */
|
|
5
|
+
export interface ProjectServerRouteStatusCounts {
|
|
6
|
+
recovering: number;
|
|
7
|
+
error: number;
|
|
8
|
+
unavailable: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Count typed route states without changing semantic readiness. */
|
|
12
|
+
export function countProjectServerRouteStatuses(
|
|
13
|
+
servers: readonly Pick<ProjectServerInfo, "status" | "statusReason">[],
|
|
14
|
+
): ProjectServerRouteStatusCounts {
|
|
15
|
+
return {
|
|
16
|
+
recovering: servers.filter((server) => server.statusReason === "process-crash-recovery-pending")
|
|
17
|
+
.length,
|
|
18
|
+
error: servers.filter(
|
|
19
|
+
(server) =>
|
|
20
|
+
server.status === "error" && server.statusReason !== "process-crash-recovery-pending",
|
|
21
|
+
).length,
|
|
22
|
+
unavailable: servers.filter((server) => server.status === "unavailable").length,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Render nonzero workspace route counts for an aggregate status surface. */
|
|
27
|
+
export function formatProjectServerRouteSummary(
|
|
28
|
+
servers: readonly Pick<ProjectServerInfo, "status" | "statusReason">[],
|
|
29
|
+
): string | null {
|
|
30
|
+
return formatProjectServerRouteStatusCounts(countProjectServerRouteStatuses(servers));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Render one structured workspace route-count value. */
|
|
34
|
+
export function formatProjectServerRouteStatusCounts(
|
|
35
|
+
counts: ProjectServerRouteStatusCounts,
|
|
36
|
+
): string | null {
|
|
37
|
+
const parts: string[] = [];
|
|
38
|
+
if (counts.recovering > 0) parts.push(`${counts.recovering} recovering`);
|
|
39
|
+
if (counts.error > 0) parts.push(`${counts.error} error${counts.error === 1 ? "" : "s"}`);
|
|
40
|
+
if (counts.unavailable > 0) parts.push(`${counts.unavailable} unavailable`);
|
|
41
|
+
return parts.length > 0 ? `workspace routes: ${parts.join(", ")}` : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Render an LSP route root relative to its workspace. */
|
|
45
|
+
export function formatProjectServerRoot(cwd: string, root: string): string {
|
|
46
|
+
const relative = path.relative(cwd, root);
|
|
47
|
+
return (relative || ".").replaceAll("\\", "/");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Render a concise explanation for a process-crash recovery state. */
|
|
51
|
+
export function formatProjectServerStatusReason(
|
|
52
|
+
reason: ProjectServerStatusReason | undefined,
|
|
53
|
+
): string | null {
|
|
54
|
+
switch (reason) {
|
|
55
|
+
case "process-crashed":
|
|
56
|
+
return "process crashed; next evidence operation will recover";
|
|
57
|
+
case "process-crash-recovery-pending":
|
|
58
|
+
return "process recovery in progress";
|
|
59
|
+
case "process-crash-recovery-exhausted":
|
|
60
|
+
return "process recovery exhausted; reload required";
|
|
61
|
+
default:
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/analysis/provider.ts
CHANGED
|
@@ -97,7 +97,8 @@ export function withSemanticRequestControl<T extends SemanticProvider>(
|
|
|
97
97
|
references: (file, position) => provider.references(file, position, control),
|
|
98
98
|
implementation: (file, position) => provider.implementation(file, position, control),
|
|
99
99
|
documentSymbols: (file) => provider.documentSymbols(file, control),
|
|
100
|
-
workspaceSymbols: (query
|
|
100
|
+
workspaceSymbols: (query, _requestControl, scopes) =>
|
|
101
|
+
provider.workspaceSymbols(query, control, scopes),
|
|
101
102
|
hover: provider.hover
|
|
102
103
|
? (file, position) => provider.hover?.(file, position, control)
|
|
103
104
|
: undefined,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import * as path from "node:path";
|
|
10
10
|
import {
|
|
11
|
+
type CodeQueryResult,
|
|
11
12
|
type CodeRequestControl,
|
|
12
13
|
type CodeSymbol,
|
|
13
14
|
isCodeRequestInterruption,
|
|
@@ -114,33 +115,27 @@ export async function resolveSymbolTarget(
|
|
|
114
115
|
control?: CodeRequestControl;
|
|
115
116
|
},
|
|
116
117
|
): Promise<TargetOutcome> {
|
|
117
|
-
const
|
|
118
|
+
const scopePath = options?.path ? normalizePath(options.path, cwd) : null;
|
|
119
|
+
const result = await semantic.workspaceSymbols(
|
|
120
|
+
symbol,
|
|
121
|
+
options?.control,
|
|
122
|
+
scopePath ? [scopePath] : undefined,
|
|
123
|
+
);
|
|
118
124
|
if (result.kind === "unavailable") {
|
|
119
125
|
return {
|
|
120
|
-
kind: "
|
|
121
|
-
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
if (result.data.length === 0) {
|
|
125
|
-
return {
|
|
126
|
-
kind: "error",
|
|
127
|
-
message: `Symbol not found: \`${symbol}\`. Try \`code_resolve\` to search for it, or use an anchor target (file/line/character) if you know the location.`,
|
|
126
|
+
kind: "unavailable",
|
|
127
|
+
reason: `Symbol discovery for \`${symbol}\` is unavailable: ${result.reason}`,
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
|
+
if (result.data.length === 0) return emptySymbolOutcome(symbol, result, options?.path);
|
|
130
131
|
|
|
131
|
-
const scopePath = options?.path ? normalizePath(options.path, cwd) : null;
|
|
132
132
|
const scoped = result.data.filter((candidate) =>
|
|
133
133
|
scopePath ? isWithinOrEqual(scopePath, candidate.file) : true,
|
|
134
134
|
);
|
|
135
135
|
const eligible = options?.exportedOnly
|
|
136
136
|
? scoped.filter((candidate) => !NON_EXPORTED_KINDS.has(candidate.kind))
|
|
137
137
|
: scoped;
|
|
138
|
-
if (eligible.length === 0)
|
|
139
|
-
return {
|
|
140
|
-
kind: "error",
|
|
141
|
-
message: `Symbol not found: \`${symbol}\`${scopePath ? ` in path \`${options?.path}\`` : ""}. Try \`code_resolve\` to search for it, or use an anchor target (file/line/character) if you know the location.`,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
138
|
+
if (eligible.length === 0) return emptySymbolOutcome(symbol, result, options?.path);
|
|
144
139
|
|
|
145
140
|
const requestedKind = options?.kind;
|
|
146
141
|
if (requestedKind) {
|
|
@@ -248,6 +243,23 @@ type CandidateOutcomeOptions = CandidateOutcomeBase &
|
|
|
248
243
|
* exact provider result count for omission disclosure. No candidate is
|
|
249
244
|
* promoted when the requested provider kind did not match.
|
|
250
245
|
*/
|
|
246
|
+
function emptySymbolOutcome(
|
|
247
|
+
symbol: string,
|
|
248
|
+
result: Exclude<CodeQueryResult<CodeSymbol[]>, { kind: "unavailable" }>,
|
|
249
|
+
scope?: string,
|
|
250
|
+
): TargetOutcome {
|
|
251
|
+
if (result.kind === "partial") {
|
|
252
|
+
return {
|
|
253
|
+
kind: "unavailable",
|
|
254
|
+
reason: `Symbol discovery for \`${symbol}\` is incomplete: ${result.reason}`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
kind: "error",
|
|
259
|
+
message: `Symbol not found: \`${symbol}\`${scope ? ` in path \`${scope}\`` : ""}. Try \`code_resolve\` to search for it, or use an anchor target (file/line/character) if you know the location.`,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
251
263
|
async function buildCandidateOutcome(
|
|
252
264
|
options: CandidateOutcomeOptions,
|
|
253
265
|
): Promise<Extract<TargetOutcome, { kind: "disambiguation" | "kind-mismatch" }>> {
|
|
@@ -9,6 +9,7 @@ import { isCodeFindAstKind } from "../tool/code_find/ast-kinds.ts";
|
|
|
9
9
|
import type { CapabilityAdapter } from "./capability-adapter.ts";
|
|
10
10
|
import type { FindWorkflowInput, FindWorkflowOutcome } from "./find-types.ts";
|
|
11
11
|
import { parseFindWorkflowInput } from "./input/workflows.ts";
|
|
12
|
+
import { resolveWorkspaceSemanticDemand } from "./semantic-demand.ts";
|
|
12
13
|
import { reportProgress, throwIfAborted, type WorkflowControl } from "./workflow-control.ts";
|
|
13
14
|
|
|
14
15
|
export interface FindWorkflowDeps {
|
|
@@ -71,22 +72,14 @@ async function runSemanticSearch(options: {
|
|
|
71
72
|
control?: WorkflowControl;
|
|
72
73
|
}): Promise<FindWorkflowOutcome> {
|
|
73
74
|
const { query, scopePaths, scopeLabel, maxResults, deps, control } = options;
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
{ kind: "workspace" },
|
|
77
|
-
control,
|
|
78
|
-
);
|
|
79
|
-
if (readiness.kind === "timeout") {
|
|
75
|
+
const semanticDemand = await resolveWorkspaceSemanticDemand(deps.capability, deps.cwd, control);
|
|
76
|
+
if (semanticDemand.kind === "timeout") {
|
|
80
77
|
return { kind: "unavailable", reason: SEMANTIC_READINESS_TIMEOUT_REASON };
|
|
81
78
|
}
|
|
82
|
-
if (
|
|
79
|
+
if (semanticDemand.kind === "unavailable") return semanticDemand;
|
|
83
80
|
throwIfAborted(control);
|
|
84
81
|
|
|
85
|
-
const
|
|
86
|
-
if (!provider?.workspaceSymbols) {
|
|
87
|
-
return { kind: "unavailable", reason: "No semantic workspace-symbol provider is active." };
|
|
88
|
-
}
|
|
89
|
-
const result = await provider.workspaceSymbols(query, control);
|
|
82
|
+
const result = await semanticDemand.provider.workspaceSymbols(query, control, scopePaths);
|
|
90
83
|
// A cancellation that landed during the request must not publish symbols
|
|
91
84
|
// the caller no longer awaits.
|
|
92
85
|
throwIfAborted(control);
|
|
@@ -105,9 +105,13 @@ async function collectWorkspaceRefreshAttempt(
|
|
|
105
105
|
service: options.runtime,
|
|
106
106
|
control: options.control,
|
|
107
107
|
progress: options.reportRecoveryProgress,
|
|
108
|
-
// The maintenance pass already refreshed every
|
|
109
|
-
// evidence
|
|
108
|
+
// The maintenance pass already refreshed every running route. Reuse its
|
|
109
|
+
// evidence unless process-crash demand restores a required route; the
|
|
110
|
+
// runtime then performs a fresh pass that includes the replacement.
|
|
110
111
|
initialEvidence: maintenance.diagnosticEvidence,
|
|
112
|
+
processCrashDemand: {
|
|
113
|
+
...(workspaceScope?.filter ? { scopes: [workspaceScope.filter] } : {}),
|
|
114
|
+
},
|
|
111
115
|
});
|
|
112
116
|
const diagnosticEvidence = mergeDiagnosticEvidence(
|
|
113
117
|
maintenance.diagnosticEvidence,
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
DiagnosticEvidenceSummary,
|
|
3
|
+
FileScopeDecision,
|
|
4
|
+
ProjectServerInfo,
|
|
5
|
+
ProjectServerStatusReason,
|
|
6
|
+
} from "@mrclrchtr/supi-lsp/api";
|
|
2
7
|
import type { CapabilityWarningReport } from "../analysis/capability/capability-warnings.ts";
|
|
3
8
|
|
|
4
9
|
export type HealthSection = "diagnostics" | "servers";
|
|
@@ -22,7 +27,8 @@ export interface HealthServerInfo {
|
|
|
22
27
|
readonly name: string;
|
|
23
28
|
readonly root: string;
|
|
24
29
|
readonly fileTypes: readonly string[];
|
|
25
|
-
readonly status:
|
|
30
|
+
readonly status: ProjectServerInfo["status"];
|
|
31
|
+
readonly statusReason?: ProjectServerStatusReason;
|
|
26
32
|
readonly ready: boolean;
|
|
27
33
|
}
|
|
28
34
|
|
|
@@ -59,6 +59,7 @@ export async function runHealthWorkflow(
|
|
|
59
59
|
if (prepared.kind === "invalid-input") return prepared;
|
|
60
60
|
const { request, scopeFilter, included, level } = prepared.value;
|
|
61
61
|
const semanticRequested = healthNeedsSemantic(included);
|
|
62
|
+
const fileReadinessRequested = included.includes("diagnostics");
|
|
62
63
|
throwIfAborted(control);
|
|
63
64
|
|
|
64
65
|
reportProgress(control, {
|
|
@@ -85,6 +86,7 @@ export async function runHealthWorkflow(
|
|
|
85
86
|
});
|
|
86
87
|
const semanticState = await establishSemanticHealthState({
|
|
87
88
|
requested: semanticRequested,
|
|
89
|
+
fileReadinessRequested,
|
|
88
90
|
runtime,
|
|
89
91
|
scopeFilter,
|
|
90
92
|
lspState,
|
|
@@ -139,6 +141,8 @@ function healthNeedsSemantic(included: readonly HealthSection[]): boolean {
|
|
|
139
141
|
|
|
140
142
|
interface SemanticHealthStateOptions {
|
|
141
143
|
requested: boolean;
|
|
144
|
+
/** Whether this call requests file-routed evidence rather than passive inventory. */
|
|
145
|
+
fileReadinessRequested: boolean;
|
|
142
146
|
runtime: WorkspaceLspRuntime | null;
|
|
143
147
|
scopeFilter: string | null;
|
|
144
148
|
lspState: WorkspaceLspRuntimeState;
|
|
@@ -150,7 +154,7 @@ async function establishSemanticHealthState(
|
|
|
150
154
|
options: SemanticHealthStateOptions,
|
|
151
155
|
): Promise<SemanticHealthState | null> {
|
|
152
156
|
if (!options.requested) return null;
|
|
153
|
-
if (options.runtime && isScopedFile(options.scopeFilter)) {
|
|
157
|
+
if (options.fileReadinessRequested && options.runtime && isScopedFile(options.scopeFilter)) {
|
|
154
158
|
const readiness = await options.runtime.waitUntilReadyForFile(
|
|
155
159
|
options.scopeFilter,
|
|
156
160
|
undefined,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CodeRequestControl, SemanticProvider } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
|
+
import type { CapabilityAdapter, ReadinessOutcome } from "./capability-adapter.ts";
|
|
3
|
+
|
|
4
|
+
export type WorkspaceSemanticDemandState =
|
|
5
|
+
| { kind: "ready"; provider: SemanticProvider }
|
|
6
|
+
| Exclude<ReadinessOutcome, { kind: "ready" }>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wait for normal workspace startup, but let a registered provider proceed
|
|
10
|
+
* when known crashed routes make readiness recoverable only through demand.
|
|
11
|
+
*/
|
|
12
|
+
export async function resolveWorkspaceSemanticDemand(
|
|
13
|
+
capability: CapabilityAdapter,
|
|
14
|
+
cwd: string,
|
|
15
|
+
control?: CodeRequestControl,
|
|
16
|
+
): Promise<WorkspaceSemanticDemandState> {
|
|
17
|
+
let provider = capability.getSemanticProvider(cwd);
|
|
18
|
+
const readiness = await capability.ensureSemanticReadiness(cwd, { kind: "workspace" }, control);
|
|
19
|
+
if (readiness.kind === "timeout") return readiness;
|
|
20
|
+
if (readiness.kind === "unavailable" && !hasProcessCrashRoute(capability, cwd)) {
|
|
21
|
+
return readiness;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
provider ??= capability.getSemanticProvider(cwd);
|
|
25
|
+
return provider
|
|
26
|
+
? { kind: "ready", provider }
|
|
27
|
+
: { kind: "unavailable", reason: "No semantic workspace-symbol provider is active." };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function hasProcessCrashRoute(capability: CapabilityAdapter, cwd: string): boolean {
|
|
31
|
+
const lspState = capability.getLspRuntimeState(cwd);
|
|
32
|
+
return (
|
|
33
|
+
lspState.kind === "ready" &&
|
|
34
|
+
lspState.runtime.getProjectServers().some((server) => server.statusReason !== undefined)
|
|
35
|
+
);
|
|
36
|
+
}
|