@mrclrchtr/supi-lsp 3.2.0 → 4.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 +72 -62
- package/node_modules/@mrclrchtr/supi-code-runtime/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +2 -4
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/api.ts +0 -4
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/evidence-badge.ts +3 -2
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/index.ts +0 -4
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/llm.ts +0 -10
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/path-utils.ts +19 -15
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-action-menu.ts +1 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-schema.ts +9 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings.ts +1 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +10 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +19 -29
- package/node_modules/@mrclrchtr/supi-code-runtime/src/index.ts +7 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/query-result.ts +42 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +15 -10
- package/node_modules/@mrclrchtr/supi-core/package.json +2 -4
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +0 -4
- package/node_modules/@mrclrchtr/supi-core/src/evidence-badge.ts +3 -2
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +0 -4
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +0 -10
- package/node_modules/@mrclrchtr/supi-core/src/path-utils.ts +19 -15
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-action-menu.ts +1 -0
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-schema.ts +9 -0
- package/node_modules/@mrclrchtr/supi-core/src/settings.ts +1 -0
- package/package.json +4 -5
- package/src/api.ts +9 -9
- package/src/client/client.ts +70 -41
- package/src/client/transport.ts +16 -13
- package/src/config/config.ts +2 -2
- package/src/config/lsp-settings.ts +0 -2
- package/src/index.ts +6 -6
- package/src/manager/manager-diagnostics.ts +5 -95
- package/src/manager/manager-workspace-recovery.ts +4 -3
- package/src/manager/manager-workspace-symbol.ts +44 -6
- package/src/manager/manager.ts +47 -65
- package/src/provider/lsp-semantic-provider.ts +132 -79
- package/src/provider/refactor-planning.ts +6 -14
- package/src/session/readiness.ts +38 -0
- package/src/session/runtime-controller.ts +87 -75
- package/src/session/runtime-registration.ts +5 -5
- package/src/session/{service-registry.ts → runtime-registry.ts} +163 -71
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +0 -31
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/substrate-types.ts +0 -11
- package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/types.ts +0 -2
- package/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +0 -31
- package/node_modules/@mrclrchtr/supi-core/src/substrate-types.ts +0 -11
- package/node_modules/@mrclrchtr/supi-core/src/types.ts +0 -2
- package/src/diagnostics/diagnostic-augmentation.ts +0 -82
- package/src/diagnostics/diagnostic-context.ts +0 -116
- package/src/diagnostics/diagnostic-display.ts +0 -69
- package/src/manager/capability-index.ts +0 -24
- package/src/manager/client-pool.ts +0 -33
- package/src/manager/diagnostic-store.ts +0 -51
- package/src/manager/manager-stale-resync.ts +0 -47
- package/src/manager/recovery-coordinator.ts +0 -37
- package/src/manager/workspace-router.ts +0 -44
|
@@ -1,16 +1,24 @@
|
|
|
1
|
-
// LSP semantic provider adapter — wraps
|
|
1
|
+
// LSP semantic provider adapter — wraps WorkspaceLspRuntime into the shared
|
|
2
2
|
// SemanticProvider contract from supi-code-runtime.
|
|
3
3
|
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
4
5
|
import {
|
|
5
6
|
type CodeLocation,
|
|
6
7
|
type CodePosition,
|
|
8
|
+
type CodeQueryResult,
|
|
7
9
|
type CodeSymbol,
|
|
8
|
-
|
|
10
|
+
completedCodeQuery,
|
|
11
|
+
type DeclarationNesting,
|
|
12
|
+
type DocumentCodeSymbol,
|
|
13
|
+
mapCodeQueryResult,
|
|
14
|
+
partialCodeQuery,
|
|
9
15
|
type RefactorRequest,
|
|
10
16
|
type RefactorResult,
|
|
11
17
|
type SemanticProvider,
|
|
12
18
|
type SourceRange,
|
|
19
|
+
unavailableCodeQuery,
|
|
13
20
|
} from "@mrclrchtr/supi-code-runtime/api";
|
|
21
|
+
import { uriToFile } from "@mrclrchtr/supi-core/path";
|
|
14
22
|
import type {
|
|
15
23
|
CodeAction,
|
|
16
24
|
DocumentSymbol,
|
|
@@ -20,7 +28,7 @@ import type {
|
|
|
20
28
|
MarkupContent,
|
|
21
29
|
SymbolInformation,
|
|
22
30
|
} from "../config/types.ts";
|
|
23
|
-
import type {
|
|
31
|
+
import type { WorkspaceLspRuntime } from "../session/runtime-registry.ts";
|
|
24
32
|
import {
|
|
25
33
|
collectCodeActionResults,
|
|
26
34
|
isDeleteDeadCodeCodeAction,
|
|
@@ -32,74 +40,62 @@ import {
|
|
|
32
40
|
} from "./refactor-planning.ts";
|
|
33
41
|
|
|
34
42
|
/**
|
|
35
|
-
* Create a SemanticProvider backed by a
|
|
43
|
+
* Create a SemanticProvider backed by a WorkspaceLspRuntime.
|
|
36
44
|
* Maps LSP types into the shared code-runtime types.
|
|
37
45
|
*/
|
|
38
|
-
export function createLspSemanticProvider(lsp:
|
|
46
|
+
export function createLspSemanticProvider(lsp: WorkspaceLspRuntime): SemanticProvider {
|
|
39
47
|
return {
|
|
40
|
-
async definition(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
for (const item of normalized) {
|
|
46
|
-
const loc = toCodeLocation(item);
|
|
47
|
-
if (loc) mapped.push(loc);
|
|
48
|
-
}
|
|
49
|
-
return mapped;
|
|
48
|
+
async definition(
|
|
49
|
+
filePath: string,
|
|
50
|
+
position: CodePosition,
|
|
51
|
+
): Promise<CodeQueryResult<CodeLocation[]>> {
|
|
52
|
+
return mapCodeQueryResult(await lsp.definition(filePath, position), mapLocations);
|
|
50
53
|
},
|
|
51
54
|
|
|
52
55
|
async hover(
|
|
53
56
|
filePath: string,
|
|
54
57
|
position: CodePosition,
|
|
55
|
-
): Promise<{ contents: string; range?: SourceRange } | null
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
): Promise<CodeQueryResult<{ contents: string; range?: SourceRange } | null>> {
|
|
59
|
+
return mapCodeQueryResult(await lsp.hover(filePath, position), (hover) =>
|
|
60
|
+
hover ? convertLspHover(hover) : null,
|
|
61
|
+
);
|
|
59
62
|
},
|
|
60
63
|
|
|
61
|
-
async references(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const loc = toCodeLocation(item);
|
|
67
|
-
if (loc) mapped.push(loc);
|
|
68
|
-
}
|
|
69
|
-
return mapped;
|
|
64
|
+
async references(
|
|
65
|
+
filePath: string,
|
|
66
|
+
position: CodePosition,
|
|
67
|
+
): Promise<CodeQueryResult<CodeLocation[]>> {
|
|
68
|
+
return mapCodeQueryResult(await lsp.references(filePath, position), mapLocations);
|
|
70
69
|
},
|
|
71
70
|
|
|
72
|
-
async implementation(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
for (const item of normalized) {
|
|
78
|
-
const loc = toCodeLocation(item);
|
|
79
|
-
if (loc) mapped.push(loc);
|
|
80
|
-
}
|
|
81
|
-
return mapped;
|
|
71
|
+
async implementation(
|
|
72
|
+
filePath: string,
|
|
73
|
+
position: CodePosition,
|
|
74
|
+
): Promise<CodeQueryResult<CodeLocation[]>> {
|
|
75
|
+
return mapCodeQueryResult(await lsp.implementation(filePath, position), mapLocations);
|
|
82
76
|
},
|
|
83
77
|
|
|
84
|
-
async documentSymbols(filePath: string): Promise<
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
|
|
78
|
+
async documentSymbols(filePath: string): Promise<CodeQueryResult<DocumentCodeSymbol[]>> {
|
|
79
|
+
const result = await lsp.documentSymbols(filePath);
|
|
80
|
+
if (result.kind === "unavailable") return unavailableCodeQuery(result.reason);
|
|
81
|
+
const sourceLines = await readSourceLines(filePath);
|
|
82
|
+
const symbols = flattenDocumentSymbols(result.data, filePath, null, {
|
|
83
|
+
sourceLines,
|
|
84
|
+
nesting: "top-level",
|
|
85
|
+
});
|
|
86
|
+
return result.kind === "partial"
|
|
87
|
+
? partialCodeQuery(symbols, result.reason)
|
|
88
|
+
: completedCodeQuery(symbols);
|
|
88
89
|
},
|
|
89
90
|
|
|
90
|
-
async workspaceSymbols(query: string): Promise<CodeSymbol[]
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
async workspaceSymbols(query: string): Promise<CodeQueryResult<CodeSymbol[]>> {
|
|
92
|
+
return mapCodeQueryResult(await lsp.workspaceSymbol(query), (results) =>
|
|
93
|
+
results.map((symbol) => toCodeSymbol(symbol as SymbolInformation)),
|
|
94
|
+
);
|
|
94
95
|
},
|
|
95
96
|
|
|
96
97
|
async refactor(request: RefactorRequest): Promise<RefactorResult> {
|
|
97
|
-
|
|
98
|
-
// code-intelligence. RefactorRequest still permits the legacy `rename`
|
|
99
|
-
// alias, and the provider may be invoked directly outside the public tool.
|
|
100
|
-
const operation = normalizeRefactorOperation(request.operation);
|
|
101
|
-
|
|
102
|
-
switch (operation) {
|
|
98
|
+
switch (request.operation) {
|
|
103
99
|
case "rename_symbol":
|
|
104
100
|
if (!request.newName) {
|
|
105
101
|
return {
|
|
@@ -134,13 +130,13 @@ export function createLspSemanticProvider(lsp: SessionLspService): SemanticProvi
|
|
|
134
130
|
// shared file/resource edits and rollback semantics exist.
|
|
135
131
|
return {
|
|
136
132
|
kind: "unavailable",
|
|
137
|
-
reason: `Refactor operation "${operation}" is not supported yet. File/resource operations are deferred.`,
|
|
133
|
+
reason: `Refactor operation "${request.operation}" is not supported yet. File/resource operations are deferred.`,
|
|
138
134
|
};
|
|
139
135
|
}
|
|
140
136
|
|
|
141
137
|
return {
|
|
142
138
|
kind: "unavailable",
|
|
143
|
-
reason: `Refactor operation "${operation}" is not supported by the active semantic provider.`,
|
|
139
|
+
reason: `Refactor operation "${request.operation}" is not supported by the active semantic provider.`,
|
|
144
140
|
};
|
|
145
141
|
},
|
|
146
142
|
|
|
@@ -153,22 +149,11 @@ export function createLspSemanticProvider(lsp: SessionLspService): SemanticProvi
|
|
|
153
149
|
if (!actions) return [];
|
|
154
150
|
return collectCodeActionResults(actions);
|
|
155
151
|
},
|
|
156
|
-
|
|
157
|
-
async codeActionTitles(
|
|
158
|
-
file: string,
|
|
159
|
-
position: CodePosition,
|
|
160
|
-
): Promise<Array<{ title: string; kind?: string }> | null> {
|
|
161
|
-
const actions = await lsp.codeActions(file, position);
|
|
162
|
-
if (!actions) return null;
|
|
163
|
-
return actions
|
|
164
|
-
.filter((a) => a.title)
|
|
165
|
-
.map((a) => ({ title: a.title, kind: a.kind ?? undefined }));
|
|
166
|
-
},
|
|
167
152
|
};
|
|
168
153
|
}
|
|
169
154
|
|
|
170
155
|
function runExtractRefactor(
|
|
171
|
-
lsp:
|
|
156
|
+
lsp: WorkspaceLspRuntime,
|
|
172
157
|
request: RefactorRequest,
|
|
173
158
|
operation: "extract_function" | "extract_variable",
|
|
174
159
|
matches: (action: CodeAction) => boolean,
|
|
@@ -192,6 +177,15 @@ function runExtractRefactor(
|
|
|
192
177
|
|
|
193
178
|
// ── Type conversion helpers ───────────────────────────────────────────
|
|
194
179
|
|
|
180
|
+
function mapLocations(value: Location | Location[] | LocationLink[] | null): CodeLocation[] {
|
|
181
|
+
if (!value) return [];
|
|
182
|
+
const locations = Array.isArray(value) ? value : [value];
|
|
183
|
+
return locations.flatMap((item) => {
|
|
184
|
+
const location = toCodeLocation(item);
|
|
185
|
+
return location ? [location] : [];
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
195
189
|
function toCodeLocation(item: Location | LocationLink): CodeLocation | null {
|
|
196
190
|
const loc = item as Record<string, unknown>;
|
|
197
191
|
const uri = loc.uri ?? loc.targetUri;
|
|
@@ -251,26 +245,35 @@ function symbolKindName(kind: number): string {
|
|
|
251
245
|
function flattenDocumentSymbols(
|
|
252
246
|
symbols: DocumentSymbol[] | SymbolInformation[],
|
|
253
247
|
filePath: string,
|
|
254
|
-
container: string | null
|
|
255
|
-
|
|
256
|
-
|
|
248
|
+
container: string | null,
|
|
249
|
+
context: { sourceLines: readonly string[] | null; nesting: DeclarationNesting },
|
|
250
|
+
): DocumentCodeSymbol[] {
|
|
251
|
+
const result: DocumentCodeSymbol[] = [];
|
|
257
252
|
|
|
258
253
|
for (const sym of symbols) {
|
|
259
254
|
// DocumentSymbol.range = full defining node (declaration anchor);
|
|
260
255
|
// .selectionRange = identifier token (name anchor). SymbolInformation has
|
|
261
256
|
// only location.range (declaration) and no selectionRange.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
257
|
+
let document: DocumentSymbol | null = null;
|
|
258
|
+
let information: SymbolInformation | null = null;
|
|
259
|
+
if (isSymbolInformation(sym)) information = sym;
|
|
260
|
+
else document = sym;
|
|
261
|
+
const declStart = document?.range.start ?? information?.location.range.start;
|
|
265
262
|
if (!declStart) continue;
|
|
266
|
-
const nameStart =
|
|
267
|
-
|
|
268
|
-
|
|
263
|
+
const nameStart = document
|
|
264
|
+
? resolveDocumentSymbolNameStart(document, context.sourceLines)
|
|
265
|
+
: null;
|
|
266
|
+
const reportedContainer = information?.containerName ?? container;
|
|
267
|
+
// SymbolInformation is flat: containerName remains metadata, not hierarchy evidence.
|
|
268
|
+
const nesting: DeclarationNesting = information ? "unknown" : context.nesting;
|
|
269
|
+
|
|
270
|
+
const symbol: DocumentCodeSymbol = {
|
|
269
271
|
name: sym.name,
|
|
270
272
|
kind: symbolKindName(sym.kind),
|
|
271
273
|
file: filePath,
|
|
272
274
|
declarationAnchor: { line: declStart.line + 1, character: declStart.character + 1 },
|
|
273
|
-
container,
|
|
275
|
+
container: reportedContainer,
|
|
276
|
+
nesting,
|
|
274
277
|
};
|
|
275
278
|
if (nameStart) {
|
|
276
279
|
symbol.nameAnchor = { line: nameStart.line + 1, character: nameStart.character + 1 };
|
|
@@ -278,21 +281,71 @@ function flattenDocumentSymbols(
|
|
|
278
281
|
|
|
279
282
|
result.push(symbol);
|
|
280
283
|
|
|
281
|
-
if (
|
|
282
|
-
result.push(
|
|
284
|
+
if (document?.children && document.children.length > 0) {
|
|
285
|
+
result.push(
|
|
286
|
+
...flattenDocumentSymbols(document.children, filePath, sym.name, {
|
|
287
|
+
sourceLines: context.sourceLines,
|
|
288
|
+
nesting: "nested",
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
283
291
|
}
|
|
284
292
|
}
|
|
285
293
|
|
|
286
294
|
return result;
|
|
287
295
|
}
|
|
288
296
|
|
|
297
|
+
function isSymbolInformation(
|
|
298
|
+
symbol: DocumentSymbol | SymbolInformation,
|
|
299
|
+
): symbol is SymbolInformation {
|
|
300
|
+
return "location" in symbol;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function readSourceLines(filePath: string): Promise<readonly string[] | null> {
|
|
304
|
+
try {
|
|
305
|
+
return (await readFile(filePath, "utf-8")).split(/\r?\n/);
|
|
306
|
+
} catch {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Validate the LSP selection range against source text. Some servers return
|
|
313
|
+
* the full overload declaration as selectionRange; recover the exact symbol
|
|
314
|
+
* token on that line rather than labeling the declaration start as a name.
|
|
315
|
+
*/
|
|
316
|
+
function resolveDocumentSymbolNameStart(
|
|
317
|
+
symbol: DocumentSymbol,
|
|
318
|
+
sourceLines: readonly string[] | null,
|
|
319
|
+
): { line: number; character: number } | null {
|
|
320
|
+
const selectionStart = symbol.selectionRange?.start;
|
|
321
|
+
if (!selectionStart) return null;
|
|
322
|
+
if (!sourceLines) return selectionStart;
|
|
323
|
+
|
|
324
|
+
const sourceLine = sourceLines[selectionStart.line];
|
|
325
|
+
if (sourceLine === undefined) return null;
|
|
326
|
+
if (
|
|
327
|
+
sourceLine.slice(selectionStart.character, selectionStart.character + symbol.name.length) ===
|
|
328
|
+
symbol.name
|
|
329
|
+
) {
|
|
330
|
+
return selectionStart;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const rangeStart = symbol.range?.start;
|
|
334
|
+
const rangeEnd = symbol.range?.end;
|
|
335
|
+
const from = rangeStart?.line === selectionStart.line ? rangeStart.character : 0;
|
|
336
|
+
const until = rangeEnd?.line === selectionStart.line ? rangeEnd.character : sourceLine.length;
|
|
337
|
+
const repairedCharacter = sourceLine.indexOf(symbol.name, from);
|
|
338
|
+
if (repairedCharacter < 0 || repairedCharacter + symbol.name.length > until) return null;
|
|
339
|
+
return { line: selectionStart.line, character: repairedCharacter };
|
|
340
|
+
}
|
|
341
|
+
|
|
289
342
|
function toCodeSymbol(sym: SymbolInformation): CodeSymbol {
|
|
290
343
|
const uri = sym.location?.uri ?? "";
|
|
291
344
|
const start = sym.location?.range?.start;
|
|
292
345
|
return {
|
|
293
346
|
name: sym.name,
|
|
294
347
|
kind: symbolKindName(sym.kind),
|
|
295
|
-
file:
|
|
348
|
+
file: uriToFile(uri),
|
|
296
349
|
// SymbolInformation has no selectionRange — nameAnchor is derived later
|
|
297
350
|
// by the orchestration layer's refine, or left absent.
|
|
298
351
|
declarationAnchor: {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { CodePosition, RefactorResult, SourceRange } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
|
+
import { uriToFile } from "@mrclrchtr/supi-core/path";
|
|
2
3
|
import type { CodeAction, TextDocumentEdit, TextEdit, WorkspaceEdit } from "../config/types.ts";
|
|
3
|
-
import type {
|
|
4
|
+
import type { WorkspaceLspRuntime } from "../session/runtime-registry.ts";
|
|
4
5
|
|
|
5
6
|
export async function runRenameRefactor(
|
|
6
|
-
lsp:
|
|
7
|
+
lsp: WorkspaceLspRuntime,
|
|
7
8
|
file: string,
|
|
8
9
|
position: CodePosition,
|
|
9
10
|
newName: string,
|
|
@@ -37,7 +38,7 @@ export function collectCodeActionResults(actions: CodeAction[]): RefactorResult[
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export async function runFilteredCodeActionRefactor(options: {
|
|
40
|
-
lsp:
|
|
41
|
+
lsp: WorkspaceLspRuntime;
|
|
41
42
|
file: string;
|
|
42
43
|
position: CodePosition;
|
|
43
44
|
operation: "update_imports" | "delete_dead_code" | "extract_function" | "extract_variable";
|
|
@@ -146,15 +147,6 @@ function convertLspWorkspaceEdit(edit: WorkspaceEdit | null): RefactorResult {
|
|
|
146
147
|
return { kind: "precise", edits: { edits: fileEdits } };
|
|
147
148
|
}
|
|
148
149
|
|
|
149
|
-
function resolveFileFromUri(uri: string): string {
|
|
150
|
-
if (!uri.startsWith("file://")) return uri;
|
|
151
|
-
try {
|
|
152
|
-
return decodeURIComponent(uri.slice(7));
|
|
153
|
-
} catch {
|
|
154
|
-
return uri;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
150
|
function collectDocumentChangeEdits(
|
|
159
151
|
docChanges: NonNullable<WorkspaceEdit["documentChanges"]>,
|
|
160
152
|
): Array<{
|
|
@@ -170,7 +162,7 @@ function collectDocumentChangeEdits(
|
|
|
170
162
|
for (const change of docChanges) {
|
|
171
163
|
const tdEdit = change as TextDocumentEdit;
|
|
172
164
|
if (!tdEdit.textDocument || !tdEdit.edits) continue;
|
|
173
|
-
const file =
|
|
165
|
+
const file = uriToFile(tdEdit.textDocument.uri);
|
|
174
166
|
for (const singleEdit of tdEdit.edits) {
|
|
175
167
|
const te = singleEdit as TextEdit;
|
|
176
168
|
out.push({
|
|
@@ -198,7 +190,7 @@ function collectChangesEdits(changes: NonNullable<WorkspaceEdit["changes"]>): Ar
|
|
|
198
190
|
}> = [];
|
|
199
191
|
for (const [uri, textEdits] of Object.entries(changes)) {
|
|
200
192
|
if (!textEdits || textEdits.length === 0) continue;
|
|
201
|
-
const file =
|
|
193
|
+
const file = uriToFile(uri);
|
|
202
194
|
for (const te of textEdits) {
|
|
203
195
|
out.push({
|
|
204
196
|
file,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const DEFAULT_SEMANTIC_READY_TIMEOUT_MS = 15_000;
|
|
2
|
+
|
|
3
|
+
export type ReadinessValueResult<T> =
|
|
4
|
+
| { kind: "resolved"; value: T }
|
|
5
|
+
| { kind: "timeout" }
|
|
6
|
+
| { kind: "unavailable"; reason: string };
|
|
7
|
+
|
|
8
|
+
/** Await one readiness operation while preserving its concrete success value. */
|
|
9
|
+
export async function raceReadinessValue<T>(
|
|
10
|
+
readiness: Promise<T>,
|
|
11
|
+
timeoutMs: number | undefined,
|
|
12
|
+
): Promise<ReadinessValueResult<T>> {
|
|
13
|
+
const effectiveTimeoutMs = timeoutMs ?? DEFAULT_SEMANTIC_READY_TIMEOUT_MS;
|
|
14
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const value = await Promise.race([
|
|
18
|
+
readiness,
|
|
19
|
+
new Promise<never>((_resolve, reject) => {
|
|
20
|
+
timer = setTimeout(
|
|
21
|
+
() => reject(new Error("semantic-readiness-timeout")),
|
|
22
|
+
effectiveTimeoutMs,
|
|
23
|
+
);
|
|
24
|
+
}),
|
|
25
|
+
]);
|
|
26
|
+
return { kind: "resolved", value };
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error instanceof Error && error.message === "semantic-readiness-timeout") {
|
|
29
|
+
return { kind: "timeout" };
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
kind: "unavailable",
|
|
33
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
34
|
+
};
|
|
35
|
+
} finally {
|
|
36
|
+
if (timer) clearTimeout(timer);
|
|
37
|
+
}
|
|
38
|
+
}
|