@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
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type CodeRequestControl,
|
|
3
|
+
getDefaultWorkspaceRuntime,
|
|
4
|
+
throwIfCodeRequestInterrupted,
|
|
5
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
2
6
|
import {
|
|
3
7
|
getWorkspaceLspRuntime,
|
|
4
8
|
type SemanticReadinessResult,
|
|
@@ -14,7 +18,10 @@ export async function ensureSemanticReadiness(
|
|
|
14
18
|
cwd: string,
|
|
15
19
|
scope: SemanticStartupScope,
|
|
16
20
|
timeoutMs: number = DEFAULT_SEMANTIC_STARTUP_TIMEOUT_MS,
|
|
21
|
+
control?: CodeRequestControl,
|
|
17
22
|
): Promise<SemanticReadinessResult> {
|
|
23
|
+
// A cancelled caller stops before any readiness work starts.
|
|
24
|
+
throwIfCodeRequestInterrupted(control);
|
|
18
25
|
const workspace = getDefaultWorkspaceRuntime().getWorkspace(cwd);
|
|
19
26
|
if (workspace.semantic.provider === null) {
|
|
20
27
|
return {
|
|
@@ -24,7 +31,7 @@ export async function ensureSemanticReadiness(
|
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
if (workspace.semantic.state.kind === "ready") {
|
|
27
|
-
return resolveReadySemanticState(cwd, scope, timeoutMs);
|
|
34
|
+
return resolveReadySemanticState(cwd, scope, timeoutMs, control);
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
if (workspace.semantic.state.kind !== "pending") {
|
|
@@ -34,13 +41,15 @@ export async function ensureSemanticReadiness(
|
|
|
34
41
|
};
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
// The request deadline bounds the internal readiness budget when it is
|
|
45
|
+
// earlier than the relative startup timeout.
|
|
46
|
+
const deadline = Math.min(Date.now() + timeoutMs, control?.deadline ?? Infinity);
|
|
38
47
|
|
|
39
48
|
const remainingAfterService = deadline - Date.now();
|
|
40
49
|
if (remainingAfterService <= 0) {
|
|
41
50
|
return { kind: "timeout" };
|
|
42
51
|
}
|
|
43
|
-
const lspState = await resolveSemanticServiceState(cwd, remainingAfterService);
|
|
52
|
+
const lspState = await resolveSemanticServiceState(cwd, remainingAfterService, control);
|
|
44
53
|
if (lspState.kind === "pending") {
|
|
45
54
|
return { kind: "timeout" };
|
|
46
55
|
}
|
|
@@ -58,8 +67,13 @@ export async function ensureSemanticReadiness(
|
|
|
58
67
|
if (remainingAfterLsp <= 0) {
|
|
59
68
|
return { kind: "timeout" };
|
|
60
69
|
}
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
if (scope.kind === "workspace") {
|
|
71
|
+
return control
|
|
72
|
+
? lspState.runtime.waitUntilReadyForWorkspace({ timeoutMs: remainingAfterLsp }, control)
|
|
73
|
+
: lspState.runtime.waitUntilReadyForWorkspace({ timeoutMs: remainingAfterLsp });
|
|
74
|
+
}
|
|
75
|
+
return control
|
|
76
|
+
? lspState.runtime.waitUntilReadyForFile(scope.file, { timeoutMs: remainingAfterLsp }, control)
|
|
63
77
|
: lspState.runtime.waitUntilReadyForFile(scope.file, { timeoutMs: remainingAfterLsp });
|
|
64
78
|
}
|
|
65
79
|
|
|
@@ -67,11 +81,17 @@ function resolveReadySemanticState(
|
|
|
67
81
|
cwd: string,
|
|
68
82
|
scope: SemanticStartupScope,
|
|
69
83
|
timeoutMs: number,
|
|
84
|
+
control?: CodeRequestControl,
|
|
70
85
|
): Promise<SemanticReadinessResult> | SemanticReadinessResult {
|
|
71
86
|
const lspState = getWorkspaceLspRuntime(cwd);
|
|
72
87
|
if (lspState.kind === "ready") {
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
if (scope.kind === "workspace") {
|
|
89
|
+
return control
|
|
90
|
+
? lspState.runtime.waitUntilReadyForWorkspace({ timeoutMs }, control)
|
|
91
|
+
: lspState.runtime.waitUntilReadyForWorkspace({ timeoutMs });
|
|
92
|
+
}
|
|
93
|
+
return control
|
|
94
|
+
? lspState.runtime.waitUntilReadyForFile(scope.file, { timeoutMs }, control)
|
|
75
95
|
: lspState.runtime.waitUntilReadyForFile(scope.file, { timeoutMs });
|
|
76
96
|
}
|
|
77
97
|
return {
|
|
@@ -86,8 +106,9 @@ function resolveReadySemanticState(
|
|
|
86
106
|
async function resolveSemanticServiceState(
|
|
87
107
|
cwd: string,
|
|
88
108
|
timeoutMs: number,
|
|
109
|
+
control?: CodeRequestControl,
|
|
89
110
|
): Promise<WorkspaceLspRuntimeState> {
|
|
90
111
|
const initialState = getWorkspaceLspRuntime(cwd);
|
|
91
112
|
if (initialState.kind !== "pending") return initialState;
|
|
92
|
-
return waitForWorkspaceLspRuntime(cwd, timeoutMs);
|
|
113
|
+
return waitForWorkspaceLspRuntime(cwd, timeoutMs, control);
|
|
93
114
|
}
|
|
@@ -16,7 +16,8 @@ import { readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:
|
|
|
16
16
|
import { basename, dirname, join } from "node:path";
|
|
17
17
|
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
18
18
|
import type { FileEdit, WorkspaceEdit } from "@mrclrchtr/supi-code-runtime/api";
|
|
19
|
-
import {
|
|
19
|
+
import { revalidateMutationAuthority } from "./mutation-authority.ts";
|
|
20
|
+
import { compareCodePositions, createLogicalLineIndex } from "./position.ts";
|
|
20
21
|
import { validateEditAgainstFiles } from "./safety.ts";
|
|
21
22
|
|
|
22
23
|
export type ApplyResult =
|
|
@@ -24,8 +25,12 @@ export type ApplyResult =
|
|
|
24
25
|
| { kind: "error"; reason: string };
|
|
25
26
|
|
|
26
27
|
export interface ApplyOptions {
|
|
28
|
+
/** Canonical roots authorized by the semantic provider that made the plan. */
|
|
29
|
+
authorizedMutationRoots: readonly string[];
|
|
27
30
|
/** Expected SHA-256 fingerprints per file, validated after queue acquisition. */
|
|
28
31
|
expectedFingerprints?: ReadonlyMap<string, string>;
|
|
32
|
+
/** Current LSP document version, or null when the document is not open. */
|
|
33
|
+
getOpenDocumentVersion?: (file: string) => number | null;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
/**
|
|
@@ -49,16 +54,24 @@ export interface ApplyOptions {
|
|
|
49
54
|
*/
|
|
50
55
|
export async function applyWorkspaceEdit(
|
|
51
56
|
edit: WorkspaceEdit,
|
|
52
|
-
options: ApplyOptions
|
|
57
|
+
options: ApplyOptions,
|
|
53
58
|
): Promise<ApplyResult> {
|
|
54
59
|
const grouped = groupEditsByFile(edit.edits);
|
|
55
60
|
const files = [...grouped.keys()].sort();
|
|
56
61
|
return withAllMutationQueues(files, async () => {
|
|
62
|
+
const authority = revalidateMutationAuthority(files, options.authorizedMutationRoots);
|
|
63
|
+
if (authority.kind === "unavailable") {
|
|
64
|
+
return { kind: "error", reason: authority.reason };
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
if (options.expectedFingerprints) {
|
|
58
68
|
const freshness = validateFingerprints(files, options.expectedFingerprints);
|
|
59
69
|
if (!freshness.ok) return { kind: "error", reason: freshness.reason };
|
|
60
70
|
}
|
|
61
71
|
|
|
72
|
+
const documentState = validateDocumentPreconditions(edit, options.getOpenDocumentVersion);
|
|
73
|
+
if (!documentState.ok) return { kind: "error", reason: documentState.reason };
|
|
74
|
+
|
|
62
75
|
const validation = validateEditAgainstFiles(edit);
|
|
63
76
|
if (!validation.safe) {
|
|
64
77
|
return { kind: "error", reason: validation.reason };
|
|
@@ -76,6 +89,37 @@ export async function applyWorkspaceEdit(
|
|
|
76
89
|
});
|
|
77
90
|
}
|
|
78
91
|
|
|
92
|
+
function validateDocumentPreconditions(
|
|
93
|
+
edit: WorkspaceEdit,
|
|
94
|
+
getOpenDocumentVersion: ApplyOptions["getOpenDocumentVersion"],
|
|
95
|
+
): { ok: true } | { ok: false; reason: string } {
|
|
96
|
+
const preconditions = edit.documentPreconditions ?? [];
|
|
97
|
+
if (preconditions.length === 0) return { ok: true };
|
|
98
|
+
if (!getOpenDocumentVersion) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
reason: "Stored document preconditions cannot be revalidated.",
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const precondition of preconditions) {
|
|
106
|
+
const currentVersion = getOpenDocumentVersion(precondition.file);
|
|
107
|
+
if (precondition.kind === "open-document-version" && currentVersion !== precondition.version) {
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
reason: `File ${precondition.file} open document version has changed since the plan was generated.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (precondition.kind === "disk-content" && currentVersion !== null) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
reason: `File ${precondition.file} is now open and no longer uses disk content as its master.`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { ok: true };
|
|
121
|
+
}
|
|
122
|
+
|
|
79
123
|
function validateFingerprints(
|
|
80
124
|
files: string[],
|
|
81
125
|
expected: ReadonlyMap<string, string>,
|
|
@@ -158,18 +202,21 @@ function buildTransformedContents(
|
|
|
158
202
|
}
|
|
159
203
|
|
|
160
204
|
function applyEditsToContent(content: string, edits: FileEdit[]): string {
|
|
161
|
-
const
|
|
162
|
-
const sortedEdits =
|
|
163
|
-
(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
205
|
+
const lineIndex = createLogicalLineIndex(content);
|
|
206
|
+
const sortedEdits = edits
|
|
207
|
+
.map((edit, protocolIndex) => ({ edit, protocolIndex }))
|
|
208
|
+
.sort(
|
|
209
|
+
(left, right) =>
|
|
210
|
+
// Descending positions prevent shifts. Reverse protocol order at one
|
|
211
|
+
// position so sequential inserts appear in their supplied order.
|
|
212
|
+
compareCodePositions(right.edit.range.start, left.edit.range.start) ||
|
|
213
|
+
right.protocolIndex - left.protocolIndex,
|
|
214
|
+
);
|
|
168
215
|
|
|
169
216
|
let updated = content;
|
|
170
|
-
for (const fileEdit of sortedEdits) {
|
|
171
|
-
const startOffset =
|
|
172
|
-
const endOffset =
|
|
217
|
+
for (const { edit: fileEdit } of sortedEdits) {
|
|
218
|
+
const startOffset = lineIndex.offsetAt(fileEdit.range.start);
|
|
219
|
+
const endOffset = lineIndex.offsetAt(fileEdit.range.end);
|
|
173
220
|
updated = updated.slice(0, startOffset) + fileEdit.newText + updated.slice(endOffset);
|
|
174
221
|
}
|
|
175
222
|
|
|
@@ -266,14 +313,6 @@ function rollbackCommittedFiles(
|
|
|
266
313
|
}
|
|
267
314
|
}
|
|
268
315
|
|
|
269
|
-
function toOffset(lines: string[], line: number, character: number): number {
|
|
270
|
-
let offset = 0;
|
|
271
|
-
for (let index = 0; index < line && index < lines.length; index++) {
|
|
272
|
-
offset += lines[index].length + 1;
|
|
273
|
-
}
|
|
274
|
-
return offset + character;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
316
|
function toErrorMessage(error: unknown): string {
|
|
278
317
|
return error instanceof Error ? error.message : String(error);
|
|
279
318
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { lstatSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
|
|
4
|
+
export type MutationAuthorityResult =
|
|
5
|
+
| { kind: "authorized"; canonicalRoots: string[] }
|
|
6
|
+
| { kind: "unavailable"; reason: string };
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Establish canonical provider roots and verify that all mutation files are
|
|
10
|
+
* regular files inside at least one root.
|
|
11
|
+
*/
|
|
12
|
+
export function establishMutationAuthority(
|
|
13
|
+
files: readonly string[],
|
|
14
|
+
providerRoots: readonly string[],
|
|
15
|
+
): MutationAuthorityResult {
|
|
16
|
+
const roots = canonicalizeProviderRoots(providerRoots);
|
|
17
|
+
if (roots.kind === "unavailable") return roots;
|
|
18
|
+
|
|
19
|
+
const fileValidation = validateFiles(files, roots.canonicalRoots);
|
|
20
|
+
return fileValidation.kind === "unavailable" ? fileValidation : roots;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Revalidate stored canonical roots and mutation files immediately before mutation. */
|
|
24
|
+
export function revalidateMutationAuthority(
|
|
25
|
+
files: readonly string[],
|
|
26
|
+
canonicalRoots: readonly string[],
|
|
27
|
+
): MutationAuthorityResult {
|
|
28
|
+
if (canonicalRoots.length === 0) {
|
|
29
|
+
return { kind: "unavailable", reason: "Mutation plan has no authorized provider root." };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const root of canonicalRoots) {
|
|
33
|
+
if (!path.isAbsolute(root) || path.normalize(root) !== root) {
|
|
34
|
+
return { kind: "unavailable", reason: `Stored mutation root "${root}" is not canonical.` };
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
if (!lstatSync(root).isDirectory() || realpathSync(root) !== root) {
|
|
38
|
+
return { kind: "unavailable", reason: `Stored mutation root "${root}" has changed.` };
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
return { kind: "unavailable", reason: `Stored mutation root "${root}" is unavailable.` };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return validateFiles(files, canonicalRoots);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function canonicalizeProviderRoots(providerRoots: readonly string[]): MutationAuthorityResult {
|
|
49
|
+
if (providerRoots.length === 0) {
|
|
50
|
+
return { kind: "unavailable", reason: "Semantic route did not authorize a mutation root." };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const canonicalRoots = new Set<string>();
|
|
54
|
+
for (const root of providerRoots) {
|
|
55
|
+
if (!path.isAbsolute(root)) {
|
|
56
|
+
return { kind: "unavailable", reason: `Mutation root "${root}" is not absolute.` };
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const canonicalRoot = realpathSync(root);
|
|
60
|
+
if (!statSync(canonicalRoot).isDirectory()) {
|
|
61
|
+
return {
|
|
62
|
+
kind: "unavailable",
|
|
63
|
+
reason: `Mutation root "${root}" is not a directory.`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
canonicalRoots.add(canonicalRoot);
|
|
67
|
+
} catch {
|
|
68
|
+
return {
|
|
69
|
+
kind: "unavailable",
|
|
70
|
+
reason: `Mutation root "${root}" cannot be resolved to a canonical directory.`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { kind: "authorized", canonicalRoots: [...canonicalRoots].sort() };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function validateFiles(
|
|
79
|
+
files: readonly string[],
|
|
80
|
+
canonicalRoots: readonly string[],
|
|
81
|
+
): MutationAuthorityResult {
|
|
82
|
+
for (const file of new Set(files)) {
|
|
83
|
+
const validation = validateFile(file, canonicalRoots);
|
|
84
|
+
if (validation) return { kind: "unavailable", reason: validation };
|
|
85
|
+
}
|
|
86
|
+
return { kind: "authorized", canonicalRoots: [...canonicalRoots] };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function validateFile(file: string, canonicalRoots: readonly string[]): string | null {
|
|
90
|
+
if (!path.isAbsolute(file) || path.normalize(file) !== file) {
|
|
91
|
+
return `Mutation file "${file}" is not a normalized absolute path.`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let canonicalParent: string;
|
|
95
|
+
try {
|
|
96
|
+
canonicalParent = realpathSync(path.dirname(file));
|
|
97
|
+
} catch {
|
|
98
|
+
return `Mutation file "${file}" does not have a resolvable canonical parent.`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
if (!lstatSync(file).isFile()) {
|
|
103
|
+
return `Mutation file "${file}" is not a regular file.`;
|
|
104
|
+
}
|
|
105
|
+
} catch {
|
|
106
|
+
return `Mutation file "${file}" is not a readable regular file.`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let canonicalFile: string;
|
|
110
|
+
try {
|
|
111
|
+
canonicalFile = realpathSync(file);
|
|
112
|
+
} catch {
|
|
113
|
+
return `Mutation file "${file}" cannot be resolved to a canonical file.`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const expectedCanonicalFile = path.join(canonicalParent, path.basename(file));
|
|
117
|
+
if (path.resolve(canonicalFile) !== path.resolve(expectedCanonicalFile)) {
|
|
118
|
+
return `Mutation file "${file}" resolves through an unsupported file link.`;
|
|
119
|
+
}
|
|
120
|
+
if (!canonicalRoots.some((root) => isWithinOrEqual(root, canonicalFile))) {
|
|
121
|
+
return `Mutation file "${file}" is outside the authorized provider roots.`;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function isWithinOrEqual(root: string, candidate: string): boolean {
|
|
127
|
+
return isMutationPathWithinRoot(root, candidate, path);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Check canonical path containment with the selected platform path rules. */
|
|
131
|
+
export function isMutationPathWithinRoot(
|
|
132
|
+
root: string,
|
|
133
|
+
candidate: string,
|
|
134
|
+
pathApi: Pick<typeof path, "isAbsolute" | "relative" | "sep">,
|
|
135
|
+
): boolean {
|
|
136
|
+
const difference = pathApi.relative(root, candidate);
|
|
137
|
+
return (
|
|
138
|
+
difference === "" ||
|
|
139
|
+
(difference !== ".." &&
|
|
140
|
+
!difference.startsWith(`..${pathApi.sep}`) &&
|
|
141
|
+
!pathApi.isAbsolute(difference))
|
|
142
|
+
);
|
|
143
|
+
}
|
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
import type { CodePosition } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
2
|
|
|
3
|
+
interface IndexedLine {
|
|
4
|
+
readonly startOffset: number;
|
|
5
|
+
readonly length: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Logical source lines with offsets into the unchanged source string. */
|
|
9
|
+
export interface LogicalLineIndex {
|
|
10
|
+
readonly lineCount: number;
|
|
11
|
+
/** Get one logical line length in UTF-16 code units. */
|
|
12
|
+
lineLength(line: number): number | undefined;
|
|
13
|
+
/** Convert a validated zero-based UTF-16 position to a source-string offset. */
|
|
14
|
+
offsetAt(position: CodePosition): number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Index LF, CRLF, and bare-CR lines without normalizing source bytes.
|
|
19
|
+
*
|
|
20
|
+
* JavaScript string offsets and lengths use UTF-16 code units, which are the
|
|
21
|
+
* only LSP coordinate encoding that the refactor path supports.
|
|
22
|
+
*/
|
|
23
|
+
export function createLogicalLineIndex(source: string): LogicalLineIndex {
|
|
24
|
+
const lines: IndexedLine[] = [];
|
|
25
|
+
let lineStart = 0;
|
|
26
|
+
|
|
27
|
+
for (let offset = 0; offset < source.length; offset++) {
|
|
28
|
+
const codeUnit = source.charCodeAt(offset);
|
|
29
|
+
if (codeUnit !== 0x0a && codeUnit !== 0x0d) continue;
|
|
30
|
+
|
|
31
|
+
lines.push({ startOffset: lineStart, length: offset - lineStart });
|
|
32
|
+
if (codeUnit === 0x0d && source.charCodeAt(offset + 1) === 0x0a) offset++;
|
|
33
|
+
lineStart = offset + 1;
|
|
34
|
+
}
|
|
35
|
+
lines.push({ startOffset: lineStart, length: source.length - lineStart });
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
lineCount: lines.length,
|
|
39
|
+
lineLength: (line) => lines[line]?.length,
|
|
40
|
+
offsetAt: (position) => {
|
|
41
|
+
const line = lines[position.line];
|
|
42
|
+
if (!line || position.character < 0 || position.character > line.length) {
|
|
43
|
+
throw new RangeError("Position is outside the logical source lines");
|
|
44
|
+
}
|
|
45
|
+
return line.startOffset + position.character;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
3
50
|
/** Compare zero-based source positions lexicographically by line, then character. */
|
|
4
51
|
export function compareCodePositions(left: CodePosition, right: CodePosition): number {
|
|
5
52
|
return left.line - right.line || left.character - right.character;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { readFileSync } from "node:fs";
|
|
9
9
|
import type { FileEdit, WorkspaceEdit } from "@mrclrchtr/supi-code-runtime/api";
|
|
10
|
-
import { compareCodePositions } from "./position.ts";
|
|
10
|
+
import { compareCodePositions, createLogicalLineIndex, type LogicalLineIndex } from "./position.ts";
|
|
11
11
|
|
|
12
12
|
export type ValidationResult = { safe: true } | { safe: false; reason: string };
|
|
13
13
|
|
|
@@ -76,12 +76,12 @@ export function validateEditAgainstFiles(edit: WorkspaceEdit): ValidationResult
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
const
|
|
79
|
+
const lineIndex = createLogicalLineIndex(content);
|
|
80
80
|
for (const fileEdit of fileEdits) {
|
|
81
|
-
const lineValidation = validateLineBounds(file, fileEdit,
|
|
81
|
+
const lineValidation = validateLineBounds(file, fileEdit, lineIndex);
|
|
82
82
|
if (!lineValidation.safe) return lineValidation;
|
|
83
83
|
|
|
84
|
-
const characterValidation = validateCharacterBounds(file, fileEdit,
|
|
84
|
+
const characterValidation = validateCharacterBounds(file, fileEdit, lineIndex);
|
|
85
85
|
if (!characterValidation.safe) return characterValidation;
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -89,19 +89,27 @@ export function validateEditAgainstFiles(edit: WorkspaceEdit): ValidationResult
|
|
|
89
89
|
return { safe: true };
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function validateLineBounds(
|
|
93
|
-
|
|
92
|
+
function validateLineBounds(
|
|
93
|
+
file: string,
|
|
94
|
+
edit: FileEdit,
|
|
95
|
+
lineIndex: LogicalLineIndex,
|
|
96
|
+
): ValidationResult {
|
|
97
|
+
const maxLine = lineIndex.lineCount - 1;
|
|
94
98
|
if (edit.range.start.line > maxLine || edit.range.end.line > maxLine) {
|
|
95
99
|
return {
|
|
96
100
|
safe: false,
|
|
97
|
-
reason: `Edit in file "${file}" references line ${Math.max(edit.range.start.line, edit.range.end.line)}, but the file has only ${
|
|
101
|
+
reason: `Edit in file "${file}" references line ${Math.max(edit.range.start.line, edit.range.end.line)}, but the file has only ${lineIndex.lineCount} line${lineIndex.lineCount === 1 ? "" : "s"}`,
|
|
98
102
|
};
|
|
99
103
|
}
|
|
100
104
|
return { safe: true };
|
|
101
105
|
}
|
|
102
106
|
|
|
103
|
-
function validateCharacterBounds(
|
|
104
|
-
|
|
107
|
+
function validateCharacterBounds(
|
|
108
|
+
file: string,
|
|
109
|
+
edit: FileEdit,
|
|
110
|
+
lineIndex: LogicalLineIndex,
|
|
111
|
+
): ValidationResult {
|
|
112
|
+
const startLineLength = lineIndex.lineLength(edit.range.start.line) ?? 0;
|
|
105
113
|
if (edit.range.start.character > startLineLength) {
|
|
106
114
|
return {
|
|
107
115
|
safe: false,
|
|
@@ -109,7 +117,7 @@ function validateCharacterBounds(file: string, edit: FileEdit, lines: string[]):
|
|
|
109
117
|
};
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
const endLineLength =
|
|
120
|
+
const endLineLength = lineIndex.lineLength(edit.range.end.line) ?? 0;
|
|
113
121
|
if (edit.range.end.character > endLineLength) {
|
|
114
122
|
return {
|
|
115
123
|
safe: false,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Structural callee lookup — finds direct calls in the enclosing scope at a target position.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import type { CodeRequestControl } from "@mrclrchtr/supi-code-runtime/api";
|
|
5
6
|
import type { CalleeEntry, CalleeScope, RelationsServiceDeps } from "./types.ts";
|
|
6
7
|
|
|
7
8
|
export interface CalleesResult {
|
|
@@ -26,12 +27,19 @@ export async function collectCallees(
|
|
|
26
27
|
deps: RelationsServiceDeps,
|
|
27
28
|
maxResults?: number,
|
|
28
29
|
depth: "direct" | "deep" = "direct",
|
|
30
|
+
control?: CodeRequestControl,
|
|
29
31
|
): Promise<CalleesResult> {
|
|
30
32
|
if (!deps.provider?.calleesAt) {
|
|
31
33
|
return unavailableCallees(targetName);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
const
|
|
36
|
+
const depthOrOptions = control ? { depth, control } : depth;
|
|
37
|
+
const result = await deps.provider.calleesAt(
|
|
38
|
+
targetFile,
|
|
39
|
+
targetLine,
|
|
40
|
+
targetCharacter,
|
|
41
|
+
depthOrOptions,
|
|
42
|
+
);
|
|
35
43
|
if (result.kind !== "success" || !result.data) {
|
|
36
44
|
return unavailableCallees(targetName);
|
|
37
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SemanticProvider } from "@mrclrchtr/supi-code-runtime/api";
|
|
1
|
+
import type { CodeRequestControl, SemanticProvider } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Shared types for the relations analysis modules.
|
|
@@ -58,7 +58,10 @@ export interface RelationsServiceDeps {
|
|
|
58
58
|
file: string,
|
|
59
59
|
line: number,
|
|
60
60
|
character: number,
|
|
61
|
-
|
|
61
|
+
depthOrOptions?:
|
|
62
|
+
| "direct"
|
|
63
|
+
| "deep"
|
|
64
|
+
| { depth?: "direct" | "deep"; control?: CodeRequestControl },
|
|
62
65
|
) => Promise<{
|
|
63
66
|
kind: string;
|
|
64
67
|
data?: {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { CodeRequestControl } from "@mrclrchtr/supi-code-runtime/api";
|
|
1
2
|
import { startDebugTimer } from "@mrclrchtr/supi-core/debug";
|
|
3
|
+
import { truncateIdentity } from "@mrclrchtr/supi-lsp/debug-telemetry";
|
|
2
4
|
import type { StructuralSearchOperation } from "@mrclrchtr/supi-tree-sitter/api";
|
|
3
5
|
import type { StructuredFileAnalysis, StructuredPatternParams } from "./pattern-analysis.ts";
|
|
4
6
|
|
|
5
7
|
interface ScanContext {
|
|
6
|
-
readonly cwd: string;
|
|
7
8
|
readonly params: StructuredPatternParams;
|
|
8
9
|
}
|
|
9
10
|
|
|
@@ -26,8 +27,11 @@ export interface AstScanTimer {
|
|
|
26
27
|
record(input: AstScanTimingInput): void;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
/** Start one AST scan timer
|
|
30
|
-
export function startAstScanTimer(
|
|
30
|
+
/** Start one AST scan timer, recording the workspace root as event-level cwd. */
|
|
31
|
+
export function startAstScanTimer(
|
|
32
|
+
control: CodeRequestControl | undefined,
|
|
33
|
+
cwd: string,
|
|
34
|
+
): AstScanTimer {
|
|
31
35
|
const timer = startDebugTimer();
|
|
32
36
|
return {
|
|
33
37
|
enumerationCompleted() {
|
|
@@ -36,11 +40,12 @@ export function startAstScanTimer(): AstScanTimer {
|
|
|
36
40
|
record(input) {
|
|
37
41
|
timer.finish(
|
|
38
42
|
() => ({
|
|
43
|
+
operationId: control?.operationId,
|
|
39
44
|
source: "code-intelligence",
|
|
40
45
|
level: "debug",
|
|
41
46
|
category: "ast-scan.timing",
|
|
42
47
|
message: `AST ${input.context.params.kind} scan analyzed ${input.analysis.analyzedFileCount} files`,
|
|
43
|
-
cwd:
|
|
48
|
+
cwd: truncateIdentity(cwd),
|
|
44
49
|
data: {
|
|
45
50
|
kind: input.context.params.kind,
|
|
46
51
|
operation: input.operation,
|
|
@@ -292,7 +292,7 @@ class AstFileEnumerator {
|
|
|
292
292
|
#checkControl(): boolean {
|
|
293
293
|
this.options.signal?.throwIfAborted();
|
|
294
294
|
if (this.#safetyLimited) return false;
|
|
295
|
-
if (this.#now()
|
|
295
|
+
if (this.#now() < this.options.deadline) return true;
|
|
296
296
|
this.#markTimeout();
|
|
297
297
|
return false;
|
|
298
298
|
}
|
|
@@ -24,7 +24,7 @@ export async function settleByDeadline<T>(
|
|
|
24
24
|
): Promise<DeadlineOutcome<T>> {
|
|
25
25
|
control.signal?.throwIfAborted();
|
|
26
26
|
const remainingMs = control.deadline - control.now();
|
|
27
|
-
if (remainingMs
|
|
27
|
+
if (remainingMs <= 0) return { kind: "timeout" };
|
|
28
28
|
|
|
29
29
|
return new Promise<DeadlineOutcome<T>>((resolve, reject) => {
|
|
30
30
|
let settled = false;
|
|
@@ -70,7 +70,7 @@ export async function settleByDeadline<T>(
|
|
|
70
70
|
.then(
|
|
71
71
|
(value) => {
|
|
72
72
|
complete(
|
|
73
|
-
control.now()
|
|
73
|
+
control.now() >= control.deadline ? { kind: "timeout" } : { kind: "completed", value },
|
|
74
74
|
);
|
|
75
75
|
},
|
|
76
76
|
(error: unknown) => fail(error),
|