@mrclrchtr/supi-code-intelligence 4.9.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/README.md +80 -5
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/package.json +7 -6
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +10 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +206 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +78 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +114 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +372 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +14 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +62 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +491 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +215 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +206 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +370 -266
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +52 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +185 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-file-state.ts +25 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +467 -115
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +238 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +10 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +38 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +79 -9
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +13 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +300 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +464 -96
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-refactor-provider.ts +130 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +45 -329
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/refactor-planning.ts +57 -120
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-edit-normalizer.ts +320 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +207 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/readiness.ts +72 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +151 -26
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +40 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +50 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +136 -120
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +49 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +102 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +26 -6
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-timing.ts +34 -21
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/LICENSE +21 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/README.md +258 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/babel.cjs +257 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/jiti.cjs +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-cli.mjs +34 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-hooks.mjs +124 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-native.mjs +121 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.d.mts +1 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.mjs +4 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-static.mjs +23 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.cjs +30 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.cts +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.mts +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.mjs +29 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/types.d.ts +420 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/package.json +146 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +7 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +0 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +16 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +65 -87
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +47 -95
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-timing.ts +147 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client-lifecycle.ts +39 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +561 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-message-size.ts +24 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +452 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +12 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/imports.ts +4 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +5 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +33 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/blocking-test-bootstrap.mjs +34 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/bootstrap.mjs +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store-helpers.ts +23 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +400 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/request-control.ts +48 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-parser-helpers.ts +24 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-query-helpers.ts +43 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +421 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +116 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +201 -0
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +236 -24
- package/src/analysis/health/recovery.ts +6 -2
- package/src/analysis/provider.ts +58 -0
- package/src/analysis/readiness.ts +30 -9
- package/src/analysis/refactor/apply.ts +59 -20
- package/src/analysis/refactor/mutation-authority.ts +143 -0
- package/src/analysis/refactor/position.ts +47 -0
- package/src/analysis/refactor/safety.ts +18 -10
- package/src/analysis/relations/callees.ts +9 -1
- package/src/analysis/relations/types.ts +5 -2
- package/src/analysis/search/ast-scan-timing.ts +9 -4
- package/src/analysis/search/ast-scan.ts +1 -1
- package/src/analysis/search/deadline.ts +2 -2
- package/src/analysis/search/pattern-analysis.ts +46 -32
- package/src/analysis/search/pattern.ts +12 -2
- package/src/analysis/target/anchored.ts +20 -11
- package/src/analysis/target/file.ts +18 -11
- package/src/analysis/target/identity.ts +19 -3
- package/src/analysis/target/symbol.ts +36 -15
- package/src/api.ts +4 -0
- package/src/diagnostics/evidence.ts +51 -0
- package/src/session/capability-adapter.ts +12 -3
- package/src/session/find-workflow.ts +17 -7
- package/src/session/graph/collect.ts +18 -2
- package/src/session/graph-workflow.ts +19 -12
- package/src/session/health-refresh.ts +148 -0
- package/src/session/health-types.ts +37 -15
- package/src/session/health-workflow.ts +37 -39
- package/src/session/inspect/collect.ts +20 -3
- package/src/session/inspect-workflow.ts +10 -5
- package/src/session/orientation/collect.ts +10 -1
- package/src/session/orientation/context-facts.ts +3 -1
- package/src/session/orientation/context-sections.ts +20 -7
- package/src/session/orientation/gather.ts +17 -11
- package/src/session/orientation-types.ts +2 -1
- package/src/session/orientation-workflow.ts +15 -8
- package/src/session/refactor-plans.ts +2 -0
- package/src/session/refactor-workflow.ts +59 -14
- package/src/session/session.ts +4 -1
- package/src/session/target-workflow.ts +54 -23
- package/src/session/workflow-control.ts +7 -3
- package/src/substrate/lsp/lifecycle.ts +26 -46
- package/src/substrate/lsp/maintenance.ts +199 -25
- package/src/substrate/lsp/state.ts +2 -2
- package/src/substrate/workspace-provider-host.ts +25 -3
- package/src/tool/find/execute.ts +7 -1
- package/src/tool/find/render.ts +20 -3
- package/src/tool/find/tui.ts +23 -21
- package/src/tool/graph/execute.ts +23 -3
- package/src/tool/graph/tui.ts +59 -46
- package/src/tool/health/execute.ts +7 -2
- package/src/tool/health/markdown.ts +56 -14
- package/src/tool/health/refresh-status.ts +105 -0
- package/src/tool/health/tui.ts +95 -98
- package/src/tool/infra/workflow-control.ts +8 -0
- package/src/tool/inspect/execute.ts +7 -1
- package/src/tool/inspect/tui.ts +26 -18
- package/src/tool/orientation/execute.ts +21 -3
- package/src/tool/orientation/tui.ts +51 -35
- package/src/tool/refactor-apply/execute.ts +3 -0
- package/src/tool/refactor-apply/tui.ts +43 -25
- package/src/tool/refactor-plan/execute.ts +4 -0
- package/src/tool/refactor-plan/tui.ts +33 -20
- package/src/tool/register.ts +127 -22
- package/src/tool/resolve/execute.ts +18 -1
- package/src/tool/resolve/tui.ts +42 -24
- package/src/tool/result/display.ts +107 -0
- package/src/tool/result/errors.ts +20 -17
- package/src/tool/result/find.ts +52 -8
- package/src/tool/result/graph.ts +44 -1
- package/src/tool/result/health.ts +53 -1
- package/src/tool/result/inspect.ts +113 -1
- package/src/tool/result/orientation.ts +66 -14
- package/src/tool/result/refactor.ts +36 -4
- package/src/tool/result/resolve.ts +44 -7
- package/src/tool/result/types.ts +20 -0
- package/src/types/execution.ts +18 -5
- package/src/types/index.ts +8 -1
- package/src/ui/status-command.ts +5 -3
- package/src/ui/status-overlay.ts +4 -0
- package/src/ui/tui/common.ts +108 -10
- package/src/ui/tui/display.ts +48 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +0 -247
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import type {
|
|
3
|
+
CodeLocation,
|
|
4
|
+
CodeSymbol,
|
|
5
|
+
DeclarationNesting,
|
|
6
|
+
DocumentCodeSymbol,
|
|
7
|
+
SourceRange,
|
|
8
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
9
|
+
import { uriToFile } from "@mrclrchtr/supi-core/path";
|
|
10
|
+
import type {
|
|
11
|
+
DocumentSymbol,
|
|
12
|
+
Hover,
|
|
13
|
+
Location,
|
|
14
|
+
LocationLink,
|
|
15
|
+
MarkupContent,
|
|
16
|
+
SymbolInformation,
|
|
17
|
+
} from "../config/types.ts";
|
|
18
|
+
|
|
19
|
+
/** Map one LSP location result to shared code locations. */
|
|
20
|
+
export function mapLocations(value: Location | Location[] | LocationLink[] | null): CodeLocation[] {
|
|
21
|
+
if (!value) return [];
|
|
22
|
+
const locations = Array.isArray(value) ? value : [value];
|
|
23
|
+
return locations.flatMap((item) => {
|
|
24
|
+
const location = toCodeLocation(item);
|
|
25
|
+
return location ? [location] : [];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function toCodeLocation(item: Location | LocationLink): CodeLocation | null {
|
|
30
|
+
const loc = item as Record<string, unknown>;
|
|
31
|
+
const uri = loc.uri ?? loc.targetUri;
|
|
32
|
+
if (typeof uri !== "string") return null;
|
|
33
|
+
const range = loc.targetSelectionRange ?? loc.targetRange ?? loc.range;
|
|
34
|
+
if (!range || typeof range !== "object") return null;
|
|
35
|
+
const r = range as { start: Record<string, unknown>; end: Record<string, unknown> };
|
|
36
|
+
if (!r.start || !r.end) return null;
|
|
37
|
+
return {
|
|
38
|
+
uri,
|
|
39
|
+
range: {
|
|
40
|
+
start: {
|
|
41
|
+
line: (r.start.line as number) ?? 0,
|
|
42
|
+
character: (r.start.character as number) ?? 0,
|
|
43
|
+
},
|
|
44
|
+
end: { line: (r.end.line as number) ?? 0, character: (r.end.character as number) ?? 0 },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const SYMBOL_KIND_NAMES: Record<number, string> = {
|
|
50
|
+
1: "File",
|
|
51
|
+
2: "Module",
|
|
52
|
+
3: "Namespace",
|
|
53
|
+
4: "Package",
|
|
54
|
+
5: "Class",
|
|
55
|
+
6: "Method",
|
|
56
|
+
7: "Property",
|
|
57
|
+
8: "Field",
|
|
58
|
+
9: "Constructor",
|
|
59
|
+
10: "Enum",
|
|
60
|
+
11: "Interface",
|
|
61
|
+
12: "Function",
|
|
62
|
+
13: "Variable",
|
|
63
|
+
14: "Constant",
|
|
64
|
+
15: "String",
|
|
65
|
+
16: "Number",
|
|
66
|
+
17: "Boolean",
|
|
67
|
+
18: "Array",
|
|
68
|
+
19: "Object",
|
|
69
|
+
20: "Key",
|
|
70
|
+
21: "Null",
|
|
71
|
+
22: "EnumMember",
|
|
72
|
+
23: "Struct",
|
|
73
|
+
24: "Event",
|
|
74
|
+
25: "Operator",
|
|
75
|
+
26: "TypeParameter",
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
function symbolKindName(kind: number): string {
|
|
79
|
+
return SYMBOL_KIND_NAMES[kind] ?? "Unknown";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Flatten provider symbols while preserving declaration and name anchors. */
|
|
83
|
+
export async function mapDocumentSymbols(
|
|
84
|
+
symbols: DocumentSymbol[] | SymbolInformation[],
|
|
85
|
+
filePath: string,
|
|
86
|
+
): Promise<DocumentCodeSymbol[]> {
|
|
87
|
+
return flattenDocumentSymbols(symbols, filePath, null, {
|
|
88
|
+
sourceLines: await readSourceLines(filePath),
|
|
89
|
+
nesting: "top-level",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function flattenDocumentSymbols(
|
|
94
|
+
symbols: DocumentSymbol[] | SymbolInformation[],
|
|
95
|
+
filePath: string,
|
|
96
|
+
container: string | null,
|
|
97
|
+
context: { sourceLines: readonly string[] | null; nesting: DeclarationNesting },
|
|
98
|
+
): DocumentCodeSymbol[] {
|
|
99
|
+
const result: DocumentCodeSymbol[] = [];
|
|
100
|
+
for (const sym of symbols) {
|
|
101
|
+
let information: SymbolInformation | null = null;
|
|
102
|
+
let document: DocumentSymbol | null = null;
|
|
103
|
+
if (isSymbolInformation(sym)) information = sym;
|
|
104
|
+
else document = sym;
|
|
105
|
+
const declStart = document?.range.start ?? information?.location.range.start;
|
|
106
|
+
if (!declStart) continue;
|
|
107
|
+
const nameStart = document
|
|
108
|
+
? resolveDocumentSymbolNameStart(document, context.sourceLines)
|
|
109
|
+
: null;
|
|
110
|
+
const symbol: DocumentCodeSymbol = {
|
|
111
|
+
name: sym.name,
|
|
112
|
+
kind: symbolKindName(sym.kind),
|
|
113
|
+
file: filePath,
|
|
114
|
+
declarationAnchor: { line: declStart.line + 1, character: declStart.character + 1 },
|
|
115
|
+
container: information?.containerName ?? container,
|
|
116
|
+
nesting: information ? "unknown" : context.nesting,
|
|
117
|
+
};
|
|
118
|
+
if (nameStart) {
|
|
119
|
+
symbol.nameAnchor = { line: nameStart.line + 1, character: nameStart.character + 1 };
|
|
120
|
+
}
|
|
121
|
+
result.push(symbol);
|
|
122
|
+
if (document?.children?.length) {
|
|
123
|
+
result.push(
|
|
124
|
+
...flattenDocumentSymbols(document.children, filePath, sym.name, {
|
|
125
|
+
sourceLines: context.sourceLines,
|
|
126
|
+
nesting: "nested",
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function isSymbolInformation(
|
|
135
|
+
symbol: DocumentSymbol | SymbolInformation,
|
|
136
|
+
): symbol is SymbolInformation {
|
|
137
|
+
return "location" in symbol;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function readSourceLines(filePath: string): Promise<readonly string[] | null> {
|
|
141
|
+
try {
|
|
142
|
+
return (await readFile(filePath, "utf-8")).split(/\r?\n/);
|
|
143
|
+
} catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function resolveDocumentSymbolNameStart(
|
|
149
|
+
symbol: DocumentSymbol,
|
|
150
|
+
sourceLines: readonly string[] | null,
|
|
151
|
+
): { line: number; character: number } | null {
|
|
152
|
+
const selectionStart = symbol.selectionRange?.start;
|
|
153
|
+
if (!selectionStart) return null;
|
|
154
|
+
if (!sourceLines) return selectionStart;
|
|
155
|
+
const sourceLine = sourceLines[selectionStart.line];
|
|
156
|
+
if (sourceLine === undefined) return null;
|
|
157
|
+
if (
|
|
158
|
+
sourceLine.slice(selectionStart.character, selectionStart.character + symbol.name.length) ===
|
|
159
|
+
symbol.name
|
|
160
|
+
) {
|
|
161
|
+
return selectionStart;
|
|
162
|
+
}
|
|
163
|
+
const rangeStart = symbol.range?.start;
|
|
164
|
+
const rangeEnd = symbol.range?.end;
|
|
165
|
+
const from = rangeStart?.line === selectionStart.line ? rangeStart.character : 0;
|
|
166
|
+
const until = rangeEnd?.line === selectionStart.line ? rangeEnd.character : sourceLine.length;
|
|
167
|
+
const character = sourceLine.indexOf(symbol.name, from);
|
|
168
|
+
if (character < 0 || character + symbol.name.length > until) return null;
|
|
169
|
+
return { line: selectionStart.line, character };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Map one workspace symbol to the shared representation. */
|
|
173
|
+
export function toCodeSymbol(sym: SymbolInformation): CodeSymbol {
|
|
174
|
+
const start = sym.location?.range?.start;
|
|
175
|
+
return {
|
|
176
|
+
name: sym.name,
|
|
177
|
+
kind: symbolKindName(sym.kind),
|
|
178
|
+
file: uriToFile(sym.location?.uri ?? ""),
|
|
179
|
+
declarationAnchor: {
|
|
180
|
+
line: start ? start.line + 1 : 0,
|
|
181
|
+
character: start ? start.character + 1 : 0,
|
|
182
|
+
},
|
|
183
|
+
container: sym.containerName ?? null,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Convert an LSP hover response to the shared representation. */
|
|
188
|
+
export function convertLspHover(hover: Hover): { contents: string; range?: SourceRange } {
|
|
189
|
+
const result: { contents: string; range?: SourceRange } = {
|
|
190
|
+
contents: extractHoverText(hover.contents),
|
|
191
|
+
};
|
|
192
|
+
if (hover.range) {
|
|
193
|
+
result.range = {
|
|
194
|
+
start: { line: hover.range.start.line, character: hover.range.start.character },
|
|
195
|
+
end: { line: hover.range.end.line, character: hover.range.end.character },
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function extractHoverText(contents: Hover["contents"]): string {
|
|
202
|
+
if (typeof contents === "string") return contents;
|
|
203
|
+
if (Array.isArray(contents)) {
|
|
204
|
+
return contents.map((item) => (typeof item === "string" ? item : item.value)).join("\n");
|
|
205
|
+
}
|
|
206
|
+
return (contents as MarkupContent).value ?? "";
|
|
207
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CodeRequestControl,
|
|
3
|
+
CodeRequestDeadlineError,
|
|
4
|
+
isCodeRequestInterruption,
|
|
5
|
+
throwIfCodeRequestInterrupted,
|
|
6
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
7
|
+
|
|
1
8
|
const DEFAULT_SEMANTIC_READY_TIMEOUT_MS = 15_000;
|
|
2
9
|
|
|
3
10
|
export type ReadinessValueResult<T> =
|
|
@@ -5,26 +12,88 @@ export type ReadinessValueResult<T> =
|
|
|
5
12
|
| { kind: "timeout" }
|
|
6
13
|
| { kind: "unavailable"; reason: string };
|
|
7
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Race one promise against request-control interruption (abort or absolute
|
|
17
|
+
* deadline) and rethrow the interruption. The underlying promise keeps its
|
|
18
|
+
* own lifecycle; only the caller's wait stops promptly.
|
|
19
|
+
*/
|
|
20
|
+
export async function raceRequestControl<T>(
|
|
21
|
+
promise: Promise<T>,
|
|
22
|
+
control?: CodeRequestControl,
|
|
23
|
+
): Promise<T> {
|
|
24
|
+
if (!control) return promise;
|
|
25
|
+
// Preflight: an already-cancelled caller stops even when the awaited
|
|
26
|
+
// promise is already settled.
|
|
27
|
+
throwIfCodeRequestInterrupted(control);
|
|
28
|
+
const deadlineRemaining =
|
|
29
|
+
control.deadline === undefined ? undefined : control.deadline - Date.now();
|
|
30
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
31
|
+
let abortHandler: (() => void) | undefined;
|
|
32
|
+
try {
|
|
33
|
+
return await Promise.race([
|
|
34
|
+
promise,
|
|
35
|
+
new Promise<never>((_resolve, reject) => {
|
|
36
|
+
if (deadlineRemaining !== undefined && deadlineRemaining <= 0) {
|
|
37
|
+
reject(new CodeRequestDeadlineError());
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (deadlineRemaining !== undefined) {
|
|
41
|
+
timer = setTimeout(() => reject(new CodeRequestDeadlineError()), deadlineRemaining);
|
|
42
|
+
}
|
|
43
|
+
abortHandler = () => reject(control.signal?.reason ?? new Error("Request cancelled"));
|
|
44
|
+
if (control.signal?.aborted) abortHandler();
|
|
45
|
+
else control.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
46
|
+
}),
|
|
47
|
+
]);
|
|
48
|
+
} finally {
|
|
49
|
+
if (timer) clearTimeout(timer);
|
|
50
|
+
if (abortHandler) control.signal?.removeEventListener("abort", abortHandler);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
8
54
|
/** Await one readiness operation while preserving its concrete success value. */
|
|
9
55
|
export async function raceReadinessValue<T>(
|
|
10
56
|
readiness: Promise<T>,
|
|
11
57
|
timeoutMs: number | undefined,
|
|
58
|
+
control?: CodeRequestControl,
|
|
12
59
|
): Promise<ReadinessValueResult<T>> {
|
|
60
|
+
// Preflight: an already-cancelled caller stops even when the readiness
|
|
61
|
+
// promise is already settled.
|
|
62
|
+
throwIfCodeRequestInterrupted(control);
|
|
13
63
|
const effectiveTimeoutMs = timeoutMs ?? DEFAULT_SEMANTIC_READY_TIMEOUT_MS;
|
|
64
|
+
const deadlineRemaining =
|
|
65
|
+
control?.deadline === undefined ? undefined : control.deadline - Date.now();
|
|
66
|
+
const bindMs =
|
|
67
|
+
deadlineRemaining === undefined
|
|
68
|
+
? effectiveTimeoutMs
|
|
69
|
+
: Math.min(effectiveTimeoutMs, deadlineRemaining);
|
|
14
70
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
71
|
+
let abortHandler: (() => void) | undefined;
|
|
15
72
|
|
|
16
73
|
try {
|
|
17
74
|
const value = await Promise.race([
|
|
18
75
|
readiness,
|
|
19
76
|
new Promise<never>((_resolve, reject) => {
|
|
20
77
|
timer = setTimeout(
|
|
21
|
-
() =>
|
|
22
|
-
|
|
78
|
+
() =>
|
|
79
|
+
reject(
|
|
80
|
+
deadlineRemaining !== undefined && deadlineRemaining < effectiveTimeoutMs
|
|
81
|
+
? new CodeRequestDeadlineError()
|
|
82
|
+
: new Error("semantic-readiness-timeout"),
|
|
83
|
+
),
|
|
84
|
+
Math.max(0, bindMs),
|
|
23
85
|
);
|
|
24
86
|
}),
|
|
87
|
+
new Promise<never>((_resolve, reject) => {
|
|
88
|
+
abortHandler = () =>
|
|
89
|
+
reject(control?.signal?.reason ?? new Error("Readiness wait cancelled"));
|
|
90
|
+
if (control?.signal?.aborted) abortHandler();
|
|
91
|
+
else control?.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
92
|
+
}),
|
|
25
93
|
]);
|
|
26
94
|
return { kind: "resolved", value };
|
|
27
95
|
} catch (error) {
|
|
96
|
+
if (isCodeRequestInterruption(error, control)) throw error;
|
|
28
97
|
if (error instanceof Error && error.message === "semantic-readiness-timeout") {
|
|
29
98
|
return { kind: "timeout" };
|
|
30
99
|
}
|
|
@@ -34,5 +103,6 @@ export async function raceReadinessValue<T>(
|
|
|
34
103
|
};
|
|
35
104
|
} finally {
|
|
36
105
|
if (timer) clearTimeout(timer);
|
|
106
|
+
if (abortHandler) control?.signal?.removeEventListener("abort", abortHandler);
|
|
37
107
|
}
|
|
38
108
|
}
|
|
@@ -7,13 +7,16 @@
|
|
|
7
7
|
//
|
|
8
8
|
// It does NOT import pi event types or ExtensionAPI.
|
|
9
9
|
|
|
10
|
+
// biome-ignore lint/style/noExcessiveLinesPerFile: session lifecycle, capability projection, and telemetry stay in one controller.
|
|
10
11
|
import type { WorkspaceRuntime } from "@mrclrchtr/supi-code-runtime/api";
|
|
12
|
+
import { recordDebugEvent } from "@mrclrchtr/supi-core/debug";
|
|
11
13
|
import { loadConfig } from "../config/config.ts";
|
|
12
14
|
import { type LspSettings, loadLspSettings } from "../config/lsp-settings.ts";
|
|
13
15
|
import { clearTsconfigCache } from "../config/tsconfig-scope.ts";
|
|
14
16
|
import type { DetectedProjectServer, LspConfig, ProjectServerInfo } from "../config/types.ts";
|
|
17
|
+
import { truncateIdentity } from "../debug-telemetry.ts";
|
|
15
18
|
import { scanWorkspaceSentinels } from "../diagnostics/workspace-sentinels.ts";
|
|
16
|
-
import { LspManager } from "../manager/manager.ts";
|
|
19
|
+
import { LspManager, type ManagerLifecycleTransition } from "../manager/manager.ts";
|
|
17
20
|
import {
|
|
18
21
|
markLspCapabilitiesReady,
|
|
19
22
|
registerPendingLspCapabilities,
|
|
@@ -25,6 +28,7 @@ import {
|
|
|
25
28
|
setWorkspaceLspRuntimeState,
|
|
26
29
|
type WorkspaceLspRuntime,
|
|
27
30
|
} from "./runtime-registry.ts";
|
|
31
|
+
import { recordLspRuntimeTransition } from "./runtime-transition-debug.ts";
|
|
28
32
|
import { scanMissingServers, scanProjectCapabilities, startDetectedServers } from "./scanner.ts";
|
|
29
33
|
|
|
30
34
|
// ── Types ─────────────────────────────────────────────────────────────
|
|
@@ -72,6 +76,28 @@ export type LspStartResult =
|
|
|
72
76
|
| { kind: "disabled"; message: string }
|
|
73
77
|
| { kind: "unavailable"; reason: string };
|
|
74
78
|
|
|
79
|
+
/** Cause of one observable LSP runtime lifecycle transition. */
|
|
80
|
+
export type LspRuntimeTransitionKind =
|
|
81
|
+
| "startup"
|
|
82
|
+
| "readiness"
|
|
83
|
+
| "crash"
|
|
84
|
+
| "recovery"
|
|
85
|
+
| "shutdown"
|
|
86
|
+
| "tracked-files";
|
|
87
|
+
|
|
88
|
+
/** Immutable aggregate state published when the LSP runtime changes. */
|
|
89
|
+
export interface LspRuntimeTransition {
|
|
90
|
+
/** Monotonic transition generation for this controller instance. */
|
|
91
|
+
readonly generation: number;
|
|
92
|
+
readonly kind: LspRuntimeTransitionKind;
|
|
93
|
+
/** True only when at least one concrete client is active and ready. */
|
|
94
|
+
readonly semanticReady: boolean;
|
|
95
|
+
readonly projectServers: readonly ProjectServerInfo[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Listener for aggregate LSP runtime transitions. */
|
|
99
|
+
export type LspRuntimeTransitionListener = (transition: LspRuntimeTransition) => void;
|
|
100
|
+
|
|
75
101
|
function supersededStartResult(): LspStartResult {
|
|
76
102
|
return { kind: "unavailable", reason: "LSP startup was superseded by a newer lifecycle event." };
|
|
77
103
|
}
|
|
@@ -99,7 +125,13 @@ export class LspRuntimeController {
|
|
|
99
125
|
readonly #cwd: string;
|
|
100
126
|
#state: LspControllerState;
|
|
101
127
|
#capabilityRuntime: WorkspaceRuntime | null;
|
|
102
|
-
|
|
128
|
+
#activeManager: LspManager | null = null;
|
|
129
|
+
#latestManagerTransition: ManagerLifecycleTransition | null = null;
|
|
130
|
+
#projectedSemanticReady: boolean | null = null;
|
|
131
|
+
#lifecycleGeneration = 0;
|
|
132
|
+
#latestLifecycleTransition: LspRuntimeTransition | null = null;
|
|
133
|
+
readonly #lifecycleListeners = new Set<LspRuntimeTransitionListener>();
|
|
134
|
+
/** Monotonic ownership token for starts, shutdowns, and manager callbacks. */
|
|
103
135
|
#readinessGeneration = 0;
|
|
104
136
|
|
|
105
137
|
constructor(cwd: string, runtime?: WorkspaceRuntime) {
|
|
@@ -151,6 +183,22 @@ export class LspRuntimeController {
|
|
|
151
183
|
this.#capabilityRuntime = runtime;
|
|
152
184
|
}
|
|
153
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Subscribe to aggregate runtime transitions.
|
|
188
|
+
*
|
|
189
|
+
* A late subscriber immediately receives the latest transition. The returned
|
|
190
|
+
* function is idempotent and stops all later notifications for this listener.
|
|
191
|
+
*/
|
|
192
|
+
subscribeLifecycle(listener: LspRuntimeTransitionListener): () => void {
|
|
193
|
+
this.#lifecycleListeners.add(listener);
|
|
194
|
+
if (this.#latestLifecycleTransition) {
|
|
195
|
+
this.notifyLifecycleListener(listener, this.#latestLifecycleTransition);
|
|
196
|
+
}
|
|
197
|
+
return () => {
|
|
198
|
+
this.#lifecycleListeners.delete(listener);
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
154
202
|
/**
|
|
155
203
|
* Start the LSP session for this controller's cwd.
|
|
156
204
|
*
|
|
@@ -191,8 +239,11 @@ export class LspRuntimeController {
|
|
|
191
239
|
*/
|
|
192
240
|
private async cleanupExistingSession(): Promise<void> {
|
|
193
241
|
if (this.#state.kind !== "ready") return;
|
|
242
|
+
this.#activeManager = null;
|
|
243
|
+
this.#latestManagerTransition = null;
|
|
194
244
|
await this.#state.runtimeOwner.shutdown();
|
|
195
245
|
if (this.#capabilityRuntime) unregisterLspCapabilities(this.#capabilityRuntime, this.#cwd);
|
|
246
|
+
this.#projectedSemanticReady = null;
|
|
196
247
|
clearWorkspaceLspRuntime(this.#cwd);
|
|
197
248
|
}
|
|
198
249
|
|
|
@@ -200,7 +251,10 @@ export class LspRuntimeController {
|
|
|
200
251
|
private setDisabled(generation: number): LspStartResult {
|
|
201
252
|
if (generation !== this.#readinessGeneration) return supersededStartResult();
|
|
202
253
|
const message = "All language servers are disabled by configuration.";
|
|
254
|
+
this.#activeManager = null;
|
|
255
|
+
this.#latestManagerTransition = null;
|
|
203
256
|
if (this.#capabilityRuntime) unregisterLspCapabilities(this.#capabilityRuntime, this.#cwd);
|
|
257
|
+
this.#projectedSemanticReady = null;
|
|
204
258
|
this.#state = { kind: "disabled", message };
|
|
205
259
|
setWorkspaceLspRuntimeState(this.#cwd, { kind: "disabled" });
|
|
206
260
|
return { kind: "disabled", message };
|
|
@@ -210,6 +264,10 @@ export class LspRuntimeController {
|
|
|
210
264
|
private setUnavailable(error: unknown, generation: number): LspStartResult {
|
|
211
265
|
if (generation !== this.#readinessGeneration) return supersededStartResult();
|
|
212
266
|
const reason = error instanceof Error ? error.message : String(error);
|
|
267
|
+
this.#activeManager = null;
|
|
268
|
+
this.#latestManagerTransition = null;
|
|
269
|
+
if (this.#capabilityRuntime) unregisterLspCapabilities(this.#capabilityRuntime, this.#cwd);
|
|
270
|
+
this.#projectedSemanticReady = null;
|
|
213
271
|
this.#state = { kind: "unavailable", reason };
|
|
214
272
|
setWorkspaceLspRuntimeState(this.#cwd, { kind: "unavailable", reason });
|
|
215
273
|
return { kind: "unavailable", reason };
|
|
@@ -229,7 +287,13 @@ export class LspRuntimeController {
|
|
|
229
287
|
if (Object.keys(config.servers).length === 0) return this.setDisabled(generation);
|
|
230
288
|
this.#state = { kind: "pending" };
|
|
231
289
|
|
|
232
|
-
|
|
290
|
+
let manager: LspManager;
|
|
291
|
+
manager = new LspManager(config, this.#cwd, (transition) => {
|
|
292
|
+
this.handleManagerLifecycle(manager, generation, transition);
|
|
293
|
+
});
|
|
294
|
+
this.#activeManager = manager;
|
|
295
|
+
this.#latestManagerTransition = null;
|
|
296
|
+
this.publishLifecycle("startup", false, []);
|
|
233
297
|
manager.setExcludePatterns(settings.exclude);
|
|
234
298
|
setWorkspaceLspRuntimeState(this.#cwd, { kind: "pending" });
|
|
235
299
|
|
|
@@ -237,6 +301,7 @@ export class LspRuntimeController {
|
|
|
237
301
|
manager.registerDetectedServers(detectedServers);
|
|
238
302
|
await startDetectedServers(manager, detectedServers);
|
|
239
303
|
if (generation !== this.#readinessGeneration) {
|
|
304
|
+
if (this.#activeManager === manager) this.#activeManager = null;
|
|
240
305
|
await manager.shutdownAll();
|
|
241
306
|
return supersededStartResult();
|
|
242
307
|
}
|
|
@@ -249,9 +314,17 @@ export class LspRuntimeController {
|
|
|
249
314
|
|
|
250
315
|
if (this.#capabilityRuntime) {
|
|
251
316
|
registerPendingLspCapabilities(this.#capabilityRuntime, this.#cwd, workspaceRuntime);
|
|
317
|
+
this.#projectedSemanticReady = false;
|
|
252
318
|
}
|
|
253
319
|
|
|
254
|
-
const
|
|
320
|
+
const latestManagerTransition = this
|
|
321
|
+
.#latestManagerTransition as ManagerLifecycleTransition | null;
|
|
322
|
+
const projectServers = [
|
|
323
|
+
...(latestManagerTransition?.projectServers ?? workspaceRuntime.getProjectServers()),
|
|
324
|
+
];
|
|
325
|
+
const semanticReady =
|
|
326
|
+
latestManagerTransition?.semanticReady ??
|
|
327
|
+
projectServers.some((server) => server.status === "running" && server.ready);
|
|
255
328
|
|
|
256
329
|
this.#state = {
|
|
257
330
|
kind: "ready",
|
|
@@ -262,40 +335,88 @@ export class LspRuntimeController {
|
|
|
262
335
|
settings,
|
|
263
336
|
};
|
|
264
337
|
|
|
265
|
-
|
|
338
|
+
this.projectSemanticReadiness(workspaceRuntime, semanticReady);
|
|
266
339
|
|
|
267
340
|
return { kind: "ready", runtime: workspaceRuntime };
|
|
268
341
|
}
|
|
269
342
|
|
|
270
|
-
private
|
|
271
|
-
|
|
343
|
+
private handleManagerLifecycle(
|
|
344
|
+
manager: LspManager,
|
|
272
345
|
readinessGeneration: number,
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
346
|
+
transition: ManagerLifecycleTransition,
|
|
347
|
+
): void {
|
|
348
|
+
if (readinessGeneration !== this.#readinessGeneration) return;
|
|
349
|
+
if (this.#activeManager !== manager) return;
|
|
350
|
+
this.#latestManagerTransition = transition;
|
|
351
|
+
if (this.#state.kind === "ready") {
|
|
352
|
+
this.#state.projectServers = [...transition.projectServers];
|
|
353
|
+
this.projectSemanticReadiness(this.#state.workspaceRuntime, transition.semanticReady);
|
|
354
|
+
}
|
|
355
|
+
this.publishLifecycle(transition.kind, transition.semanticReady, transition.projectServers);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private projectSemanticReadiness(
|
|
359
|
+
workspaceRuntime: WorkspaceLspRuntime,
|
|
360
|
+
semanticReady: boolean,
|
|
361
|
+
): void {
|
|
362
|
+
if (!this.#capabilityRuntime) return;
|
|
363
|
+
if (semanticReady) {
|
|
364
|
+
if (this.#projectedSemanticReady === true) return;
|
|
365
|
+
markLspCapabilitiesReady(this.#capabilityRuntime, this.#cwd);
|
|
366
|
+
this.#projectedSemanticReady = true;
|
|
367
|
+
this.recordCapabilityTransition(true);
|
|
278
368
|
return;
|
|
279
369
|
}
|
|
370
|
+
if (this.#projectedSemanticReady === false) return;
|
|
371
|
+
registerPendingLspCapabilities(this.#capabilityRuntime, this.#cwd, workspaceRuntime);
|
|
372
|
+
this.#projectedSemanticReady = false;
|
|
373
|
+
this.recordCapabilityTransition(false);
|
|
374
|
+
}
|
|
280
375
|
|
|
281
|
-
|
|
282
|
-
|
|
376
|
+
/** Record one semantic capability ready↔pending transition for telemetry. */
|
|
377
|
+
private recordCapabilityTransition(ready: boolean): void {
|
|
378
|
+
recordDebugEvent({
|
|
379
|
+
source: "lsp",
|
|
380
|
+
level: "debug",
|
|
381
|
+
category: "capability.transition",
|
|
382
|
+
message: `LSP capability transition: ${ready ? "ready" : "pending"}`,
|
|
383
|
+
cwd: truncateIdentity(this.#cwd),
|
|
384
|
+
data: { ready },
|
|
385
|
+
});
|
|
386
|
+
}
|
|
283
387
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
388
|
+
private publishLifecycle(
|
|
389
|
+
kind: LspRuntimeTransitionKind,
|
|
390
|
+
semanticReady: boolean,
|
|
391
|
+
projectServers: readonly ProjectServerInfo[],
|
|
392
|
+
): void {
|
|
393
|
+
const transition: LspRuntimeTransition = {
|
|
394
|
+
generation: ++this.#lifecycleGeneration,
|
|
395
|
+
kind,
|
|
396
|
+
semanticReady,
|
|
397
|
+
projectServers: projectServers.map((server) => ({
|
|
398
|
+
...server,
|
|
399
|
+
fileTypes: [...server.fileTypes],
|
|
400
|
+
supportedActions: [...server.supportedActions],
|
|
401
|
+
openFiles: [...server.openFiles],
|
|
402
|
+
})),
|
|
403
|
+
};
|
|
404
|
+
recordLspRuntimeTransition(this.#cwd, transition);
|
|
405
|
+
this.#latestLifecycleTransition = transition;
|
|
406
|
+
for (const listener of this.#lifecycleListeners) {
|
|
407
|
+
this.notifyLifecycleListener(listener, transition);
|
|
287
408
|
}
|
|
288
409
|
}
|
|
289
410
|
|
|
290
|
-
private
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
):
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
411
|
+
private notifyLifecycleListener(
|
|
412
|
+
listener: LspRuntimeTransitionListener,
|
|
413
|
+
transition: LspRuntimeTransition,
|
|
414
|
+
): void {
|
|
415
|
+
try {
|
|
416
|
+
listener(transition);
|
|
417
|
+
} catch {
|
|
418
|
+
// Lifecycle observers must not alter runtime behavior.
|
|
419
|
+
}
|
|
299
420
|
}
|
|
300
421
|
|
|
301
422
|
/**
|
|
@@ -307,10 +428,13 @@ export class LspRuntimeController {
|
|
|
307
428
|
async shutdown(): Promise<void> {
|
|
308
429
|
this.#readinessGeneration++;
|
|
309
430
|
clearTsconfigCache();
|
|
431
|
+
this.#activeManager = null;
|
|
432
|
+
this.#latestManagerTransition = null;
|
|
310
433
|
|
|
311
434
|
if (this.#capabilityRuntime) {
|
|
312
435
|
unregisterLspCapabilities(this.#capabilityRuntime, this.#cwd);
|
|
313
436
|
}
|
|
437
|
+
this.#projectedSemanticReady = null;
|
|
314
438
|
|
|
315
439
|
if (this.#cwd) {
|
|
316
440
|
clearWorkspaceLspRuntime(this.#cwd);
|
|
@@ -321,6 +445,7 @@ export class LspRuntimeController {
|
|
|
321
445
|
}
|
|
322
446
|
|
|
323
447
|
this.#state = { kind: "initial" };
|
|
448
|
+
this.publishLifecycle("shutdown", false, []);
|
|
324
449
|
}
|
|
325
450
|
|
|
326
451
|
/** Get the missing servers warning (servers whose binary is not on PATH). */
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CodeQueryResult, CodeRequestControl } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
|
+
import type { Diagnostic } from "../config/types.ts";
|
|
3
|
+
import type {
|
|
4
|
+
DiagnosticEvidenceSummary,
|
|
5
|
+
OutstandingDiagnosticSummaryEntry,
|
|
6
|
+
RecoverDiagnosticsResult,
|
|
7
|
+
WorkspaceDiagnosticSnapshot,
|
|
8
|
+
WorkspaceDiagnosticSummaryEntry,
|
|
9
|
+
} from "./runtime-diagnostics.ts";
|
|
10
|
+
|
|
11
|
+
/** Diagnostic and recovery operations owned by a workspace LSP runtime. */
|
|
12
|
+
export interface WorkspaceLspDiagnosticSurface {
|
|
13
|
+
fileDiagnostics(
|
|
14
|
+
filePath: string,
|
|
15
|
+
maxSeverity?: number,
|
|
16
|
+
control?: CodeRequestControl,
|
|
17
|
+
): Promise<CodeQueryResult<Diagnostic[]>>;
|
|
18
|
+
/** Re-synchronize tracked documents and return exact five-state evidence coverage. */
|
|
19
|
+
refreshOpenDiagnostics(
|
|
20
|
+
options?: { maxWaitMs?: number; quietMs?: number },
|
|
21
|
+
control?: CodeRequestControl,
|
|
22
|
+
): Promise<DiagnosticEvidenceSummary>;
|
|
23
|
+
/** Return tracked-file counts with aggregate cache freshness and evidence coverage. */
|
|
24
|
+
getWorkspaceDiagnosticSummary(): WorkspaceDiagnosticSnapshot<WorkspaceDiagnosticSummaryEntry>;
|
|
25
|
+
/** Return tracked-file messages with aggregate cache freshness and evidence coverage. */
|
|
26
|
+
getOutstandingDiagnostics(
|
|
27
|
+
maxSeverity?: number,
|
|
28
|
+
): WorkspaceDiagnosticSnapshot<{ file: string; diagnostics: Diagnostic[] }>;
|
|
29
|
+
/** Return tracked-file severity counts with aggregate cache freshness and evidence coverage. */
|
|
30
|
+
getOutstandingDiagnosticSummary(
|
|
31
|
+
maxSeverity?: number,
|
|
32
|
+
): WorkspaceDiagnosticSnapshot<OutstandingDiagnosticSummaryEntry>;
|
|
33
|
+
/** Run best-effort recovery and retain final diagnostic evidence, including failures. */
|
|
34
|
+
recoverDiagnostics(options?: {
|
|
35
|
+
restartIfStillStale?: boolean;
|
|
36
|
+
maxWaitMs?: number;
|
|
37
|
+
quietMs?: number;
|
|
38
|
+
control?: CodeRequestControl;
|
|
39
|
+
}): Promise<RecoverDiagnosticsResult>;
|
|
40
|
+
}
|