@mrclrchtr/supi-code-intelligence 4.10.0 → 6.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 +4 -3
- 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 +2 -2
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +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 -6
- package/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/README.md +111 -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 +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +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 -6
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- 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 +22 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +245 -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 +171 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +413 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +15 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +214 -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 +690 -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 +97 -16
- 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 +448 -298
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +70 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +231 -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 +529 -119
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +161 -33
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +12 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +5 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-extends.ts +68 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-path.ts +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +176 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +42 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +46 -10
- 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 +17 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +350 -26
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +586 -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 +42 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +92 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +165 -118
- 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 +17 -7
- 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 +2 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +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 -6
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- 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/package.json +6 -3
- 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 +221 -77
- package/src/analysis/health/file-scope.ts +184 -0
- package/src/analysis/health/recovery.ts +14 -3
- package/src/analysis/provider.ts +58 -0
- package/src/analysis/readiness.ts +34 -9
- package/src/analysis/refactor/apply.ts +45 -1
- package/src/analysis/refactor/mutation-authority.ts +143 -0
- 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 -2
- package/src/analysis/search/ast-scan.ts +5 -2
- package/src/analysis/search/deadline.ts +2 -2
- package/src/analysis/search/pattern-analysis.ts +54 -33
- package/src/analysis/search/pattern.ts +18 -3
- 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/config.ts +35 -1
- package/src/diagnostics/evidence.ts +51 -0
- package/src/extension.ts +57 -14
- package/src/overview/overview-data.ts +9 -2
- package/src/overview/overview.ts +49 -30
- package/src/overview/types.ts +2 -0
- package/src/session/capability-adapter.ts +12 -3
- package/src/session/find-types.ts +2 -2
- package/src/session/find-workflow.ts +20 -9
- package/src/session/graph/collect.ts +18 -2
- package/src/session/graph-workflow.ts +19 -12
- package/src/session/health-refresh.ts +175 -0
- package/src/session/health-types.ts +43 -15
- package/src/session/health-workflow.ts +69 -54
- package/src/session/input/workflows.ts +2 -2
- package/src/session/inspect/collect.ts +20 -3
- package/src/session/inspect-workflow.ts +12 -6
- package/src/session/orientation/collect.ts +12 -3
- 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 +4 -1
- package/src/session/orientation-workflow.ts +23 -10
- package/src/session/refactor-plans.ts +2 -0
- package/src/session/refactor-workflow.ts +61 -15
- package/src/session/session.ts +29 -2
- 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 +284 -28
- package/src/substrate/lsp/recovery.ts +11 -5
- package/src/substrate/lsp/state.ts +2 -2
- package/src/substrate/workspace-provider-host.ts +25 -3
- package/src/tool/{find → code_find}/execute.ts +2 -16
- package/src/tool/code_find/guidance.ts +8 -0
- package/src/tool/{find → code_find}/render.ts +21 -4
- package/src/tool/{result/find.ts → code_find/result.ts} +78 -9
- package/src/tool/code_find/spec.ts +37 -0
- package/src/tool/code_find/tui.ts +41 -0
- package/src/tool/code_graph/execute.ts +24 -0
- package/src/tool/code_graph/guidance.ts +6 -0
- package/src/tool/{graph → code_graph}/markdown.ts +1 -1
- package/src/tool/{result/graph.ts → code_graph/result.ts} +111 -3
- package/src/tool/code_graph/spec.ts +39 -0
- package/src/tool/code_graph/tui.ts +117 -0
- package/src/tool/{health → code_health}/execute.ts +2 -13
- package/src/tool/code_health/guidance.ts +8 -0
- package/src/tool/{health → code_health}/markdown.ts +119 -27
- package/src/tool/code_health/refresh-status.ts +105 -0
- package/src/tool/{result/health.ts → code_health/result.ts} +80 -3
- package/src/tool/code_health/spec.ts +41 -0
- package/src/tool/{health → code_health}/tui.ts +95 -98
- package/src/tool/{inspect → code_inspect}/execute.ts +2 -16
- package/src/tool/code_inspect/guidance.ts +6 -0
- package/src/tool/{inspect → code_inspect}/markdown.ts +1 -1
- package/src/tool/{result/inspect.ts → code_inspect/result.ts} +143 -2
- package/src/tool/code_inspect/spec.ts +25 -0
- package/src/tool/code_inspect/tui.ts +44 -0
- package/src/tool/code_orientation/execute.ts +25 -0
- package/src/tool/code_orientation/guidance.ts +9 -0
- package/src/tool/{orientation → code_orientation}/markdown.ts +1 -1
- package/src/tool/code_orientation/result.ts +248 -0
- package/src/tool/code_orientation/spec.ts +25 -0
- package/src/tool/{orientation → code_orientation}/tui.ts +51 -35
- package/src/tool/code_refactor_apply/execute.ts +17 -0
- package/src/tool/code_refactor_apply/guidance.ts +5 -0
- package/src/tool/{refactor-apply/execute.ts → code_refactor_apply/result.ts} +8 -11
- package/src/tool/code_refactor_apply/spec.ts +26 -0
- package/src/tool/code_refactor_apply/tui.ts +73 -0
- package/src/tool/code_refactor_plan/execute.ts +27 -0
- package/src/tool/code_refactor_plan/guidance.ts +8 -0
- package/src/tool/{refactor-plan/execute.ts → code_refactor_plan/result.ts} +13 -22
- package/src/tool/code_refactor_plan/spec.ts +25 -0
- package/src/tool/code_refactor_plan/tui.ts +54 -0
- package/src/tool/code_resolve/execute.ts +19 -0
- package/src/tool/code_resolve/guidance.ts +8 -0
- package/src/tool/{resolve → code_resolve}/markdown.ts +1 -1
- package/src/tool/{result/resolve.ts → code_resolve/result.ts} +79 -29
- package/src/tool/code_resolve/spec.ts +25 -0
- package/src/tool/code_resolve/tui.ts +60 -0
- package/src/tool/guidance.ts +71 -52
- package/src/tool/infra/truncate.ts +40 -0
- package/src/tool/infra/workflow-control.ts +8 -0
- package/src/tool/{refactor-plan/markdown.ts → refactor-markdown.ts} +4 -7
- package/src/tool/register.ts +105 -88
- package/src/tool/result/display.ts +107 -0
- package/src/tool/result/errors.ts +20 -17
- package/src/tool/result/refactor.ts +37 -8
- package/src/tool/result/types.ts +20 -0
- package/src/tool/schemas.ts +26 -157
- package/src/tool/specs.ts +40 -126
- 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 -351
- package/src/tool/find/tui.ts +0 -39
- package/src/tool/graph/execute.ts +0 -62
- package/src/tool/graph/tui.ts +0 -104
- package/src/tool/inspect/tui.ts +0 -36
- package/src/tool/orientation/execute.ts +0 -62
- package/src/tool/refactor-apply/tui.ts +0 -55
- package/src/tool/refactor-plan/tui.ts +0 -41
- package/src/tool/resolve/execute.ts +0 -36
- package/src/tool/resolve/tui.ts +0 -42
- package/src/tool/result/orientation.ts +0 -132
- /package/src/tool/{find → code_find}/ast-kinds.ts +0 -0
- /package/src/tool/{find → code_find}/markdown.ts +0 -0
- /package/src/tool/{find → code_find}/modes.ts +0 -0
- /package/src/tool/{health → code_health}/semantic-state.ts +0 -0
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import ts from "typescript";
|
|
10
|
+
import { collectExtendedProjectConfigs } from "./tsconfig-extends.ts";
|
|
11
|
+
import { normalizeTsconfigPath as normalizePath } from "./tsconfig-path.ts";
|
|
12
|
+
|
|
13
|
+
export { isProjectConfigFileName } from "./tsconfig-extends.ts";
|
|
10
14
|
|
|
11
15
|
interface ParsedProjectConfig {
|
|
12
16
|
configPath: string;
|
|
@@ -19,8 +23,39 @@ interface ParsedProjectConfig {
|
|
|
19
23
|
usesDefaultInclude: boolean;
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
interface ParsedProjectConfigCacheEntry {
|
|
27
|
+
parsed: ParsedProjectConfig | null;
|
|
28
|
+
dependencies: Set<string>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Whether a file is inside the compilation scope of its nearest project config. */
|
|
32
|
+
export type FileScopeStatus = "included" | "excluded" | "no-config" | "out-of-tree";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The mechanism that produced a file scope decision.
|
|
36
|
+
*
|
|
37
|
+
* `extension` covers unsupported file extensions (checked before any pattern).
|
|
38
|
+
*/
|
|
39
|
+
export type ScopeDecisionBasis =
|
|
40
|
+
| "fileNames"
|
|
41
|
+
| "explicit"
|
|
42
|
+
| "include-pattern"
|
|
43
|
+
| "default-include"
|
|
44
|
+
| "exclude-pattern"
|
|
45
|
+
| "extension";
|
|
46
|
+
|
|
47
|
+
/** An explainable tsconfig scope decision for one file. */
|
|
48
|
+
export interface FileScopeDecision {
|
|
49
|
+
status: FileScopeStatus;
|
|
50
|
+
/** Decision mechanism; null when no decision applies (no-config, out-of-tree). */
|
|
51
|
+
basis: ScopeDecisionBasis | null;
|
|
52
|
+
/** Absolute path of the config that decided the file, when one exists. */
|
|
53
|
+
configPath: string | null;
|
|
54
|
+
caseSensitiveFileNames: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
22
57
|
const nearestConfigCache = new Map<string, string | null>();
|
|
23
|
-
const parsedConfigCache = new Map<string,
|
|
58
|
+
const parsedConfigCache = new Map<string, ParsedProjectConfigCacheEntry>();
|
|
24
59
|
|
|
25
60
|
const tsInternal = ts as typeof ts & {
|
|
26
61
|
getFileMatcherPatterns?: (
|
|
@@ -47,14 +82,63 @@ const tsInternal = ts as typeof ts & {
|
|
|
47
82
|
* @returns `true` if the file is excluded from compilation scope
|
|
48
83
|
*/
|
|
49
84
|
export function isFileExcludedByTsconfig(filePath: string, cwd: string): boolean {
|
|
85
|
+
// Legacy filter semantics: only a resolved "excluded" decision filters a
|
|
86
|
+
// file. Out-of-tree and no-config files stay unfiltered; the decision API
|
|
87
|
+
// still reports those statuses honestly for consumers that want them.
|
|
88
|
+
return getFileScopeDecision(filePath, cwd).status === "excluded";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Compute the explainable tsconfig scope decision for one file.
|
|
93
|
+
*
|
|
94
|
+
* The decision carries the mechanism that produced it (the scope decision
|
|
95
|
+
* basis) so consumers can report why a file is inside or outside the nearest
|
|
96
|
+
* project config without re-deriving the scope logic.
|
|
97
|
+
*/
|
|
98
|
+
export function getFileScopeDecision(filePath: string, cwd: string): FileScopeDecision {
|
|
99
|
+
const caseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames;
|
|
50
100
|
const absolutePath = path.resolve(cwd, filePath);
|
|
101
|
+
if (isOutOfTree(cwd, absolutePath)) {
|
|
102
|
+
return {
|
|
103
|
+
status: "out-of-tree",
|
|
104
|
+
basis: null,
|
|
105
|
+
configPath: null,
|
|
106
|
+
caseSensitiveFileNames,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
51
110
|
const configPath = findNearestProjectConfig(path.dirname(absolutePath), cwd);
|
|
52
|
-
if (!configPath)
|
|
111
|
+
if (!configPath) {
|
|
112
|
+
return {
|
|
113
|
+
status: "no-config",
|
|
114
|
+
basis: null,
|
|
115
|
+
configPath: null,
|
|
116
|
+
caseSensitiveFileNames,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
53
119
|
|
|
54
120
|
const parsed = parseProjectConfig(configPath);
|
|
55
|
-
if (!parsed)
|
|
121
|
+
if (!parsed) {
|
|
122
|
+
return {
|
|
123
|
+
status: "no-config",
|
|
124
|
+
basis: null,
|
|
125
|
+
configPath,
|
|
126
|
+
caseSensitiveFileNames,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
56
129
|
|
|
57
|
-
|
|
130
|
+
const decision = decideFileScope(parsed, absolutePath);
|
|
131
|
+
return {
|
|
132
|
+
status: decision.included ? "included" : "excluded",
|
|
133
|
+
basis: decision.basis,
|
|
134
|
+
configPath: parsed.configPath,
|
|
135
|
+
caseSensitiveFileNames,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function isOutOfTree(cwd: string, absolutePath: string): boolean {
|
|
140
|
+
const relative = path.relative(path.resolve(cwd), absolutePath);
|
|
141
|
+
return relative.startsWith(`..${path.sep}`) || relative === "..";
|
|
58
142
|
}
|
|
59
143
|
|
|
60
144
|
/**
|
|
@@ -104,11 +188,15 @@ function getLocalProjectConfig(directory: string): string | null {
|
|
|
104
188
|
function parseProjectConfig(configPath: string): ParsedProjectConfig | null {
|
|
105
189
|
const normalizedConfigPath = normalizePath(configPath);
|
|
106
190
|
const cached = parsedConfigCache.get(normalizedConfigPath);
|
|
107
|
-
if (cached !== undefined) return cached;
|
|
191
|
+
if (cached !== undefined) return cached.parsed;
|
|
108
192
|
|
|
193
|
+
const cacheEntry: ParsedProjectConfigCacheEntry = {
|
|
194
|
+
parsed: null,
|
|
195
|
+
dependencies: collectExtendedProjectConfigs(configPath),
|
|
196
|
+
};
|
|
109
197
|
const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, createParseConfigHost());
|
|
110
198
|
if (!parsed) {
|
|
111
|
-
parsedConfigCache.set(normalizedConfigPath,
|
|
199
|
+
parsedConfigCache.set(normalizedConfigPath, cacheEntry);
|
|
112
200
|
return null;
|
|
113
201
|
}
|
|
114
202
|
|
|
@@ -119,7 +207,10 @@ function parseProjectConfig(configPath: string): ParsedProjectConfig | null {
|
|
|
119
207
|
configDir,
|
|
120
208
|
parsed.raw.include,
|
|
121
209
|
parsed.raw.exclude,
|
|
122
|
-
|
|
210
|
+
{
|
|
211
|
+
useDefaultInclude: usesDefaultInclude,
|
|
212
|
+
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
|
213
|
+
},
|
|
123
214
|
);
|
|
124
215
|
const supportedExtensions = new Set(getSupportedExtensions(parsed.options));
|
|
125
216
|
if (parsed.options.resolveJsonModule) supportedExtensions.add(".json");
|
|
@@ -134,7 +225,8 @@ function parseProjectConfig(configPath: string): ParsedProjectConfig | null {
|
|
|
134
225
|
supportedExtensions,
|
|
135
226
|
usesDefaultInclude,
|
|
136
227
|
} satisfies ParsedProjectConfig;
|
|
137
|
-
|
|
228
|
+
cacheEntry.parsed = result;
|
|
229
|
+
parsedConfigCache.set(normalizedConfigPath, cacheEntry);
|
|
138
230
|
return result;
|
|
139
231
|
}
|
|
140
232
|
|
|
@@ -147,15 +239,21 @@ function extractExplicitFiles(rawFiles: unknown, configDir: string): Set<string>
|
|
|
147
239
|
);
|
|
148
240
|
}
|
|
149
241
|
|
|
242
|
+
interface FileMatcherOptions {
|
|
243
|
+
useDefaultInclude: boolean;
|
|
244
|
+
useCaseSensitiveFileNames: boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
150
247
|
function createFileMatchers(
|
|
151
248
|
configDir: string,
|
|
152
249
|
rawInclude: unknown,
|
|
153
250
|
rawExclude: unknown,
|
|
154
|
-
|
|
251
|
+
options: FileMatcherOptions,
|
|
155
252
|
): {
|
|
156
253
|
includeFilePattern: RegExp | null;
|
|
157
254
|
excludePattern: RegExp | null;
|
|
158
255
|
} {
|
|
256
|
+
const { useDefaultInclude, useCaseSensitiveFileNames } = options;
|
|
159
257
|
const includeSpecs = Array.isArray(rawInclude)
|
|
160
258
|
? rawInclude.filter((entry): entry is string => typeof entry === "string")
|
|
161
259
|
: undefined;
|
|
@@ -167,29 +265,45 @@ function createFileMatchers(
|
|
|
167
265
|
excludeSpecs,
|
|
168
266
|
useDefaultInclude ? ["**/*"] : includeSpecs,
|
|
169
267
|
);
|
|
268
|
+
// normalizePath() lowercases paths when the filesystem is case-insensitive,
|
|
269
|
+
// so the include/exclude regexes must match case-insensitively too. Otherwise
|
|
270
|
+
// files created after the parse (absent from fileNames, tested against the
|
|
271
|
+
// include pattern) are wrongly declared excluded.
|
|
272
|
+
const regexFlags = useCaseSensitiveFileNames ? undefined : "i";
|
|
170
273
|
|
|
171
274
|
return {
|
|
172
275
|
includeFilePattern: matcherPatterns.includeFilePattern
|
|
173
|
-
? new RegExp(matcherPatterns.includeFilePattern)
|
|
276
|
+
? new RegExp(matcherPatterns.includeFilePattern, regexFlags)
|
|
174
277
|
: null,
|
|
175
278
|
excludePattern: matcherPatterns.excludePattern
|
|
176
|
-
? new RegExp(matcherPatterns.excludePattern)
|
|
279
|
+
? new RegExp(matcherPatterns.excludePattern, regexFlags)
|
|
177
280
|
: null,
|
|
178
281
|
};
|
|
179
282
|
}
|
|
180
283
|
|
|
181
|
-
function
|
|
284
|
+
function decideFileScope(
|
|
285
|
+
parsed: ParsedProjectConfig,
|
|
286
|
+
absolutePath: string,
|
|
287
|
+
): { included: boolean; basis: ScopeDecisionBasis } {
|
|
182
288
|
const normalizedPath = normalizePath(absolutePath);
|
|
183
|
-
if (parsed.fileNames.has(normalizedPath)) return true;
|
|
289
|
+
if (parsed.fileNames.has(normalizedPath)) return { included: true, basis: "fileNames" };
|
|
184
290
|
|
|
185
291
|
const extension = path.extname(absolutePath).toLowerCase();
|
|
186
|
-
if (!parsed.supportedExtensions.has(extension)) return false;
|
|
292
|
+
if (!parsed.supportedExtensions.has(extension)) return { included: false, basis: "extension" };
|
|
187
293
|
|
|
188
|
-
if (parsed.explicitFiles)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
294
|
+
if (parsed.explicitFiles) {
|
|
295
|
+
return { included: parsed.explicitFiles.has(normalizedPath), basis: "explicit" };
|
|
296
|
+
}
|
|
297
|
+
// Include patterns are rooted at the config directory; a file outside it can
|
|
298
|
+
// only be in scope through the parse-time file set or an explicit files list.
|
|
299
|
+
if (!isWithinOrEqual(parsed.configDir, absolutePath)) {
|
|
300
|
+
return { included: false, basis: "include-pattern" };
|
|
301
|
+
}
|
|
302
|
+
if (parsed.excludePattern?.test(normalizedPath))
|
|
303
|
+
return { included: false, basis: "exclude-pattern" };
|
|
304
|
+
if (parsed.usesDefaultInclude) return { included: true, basis: "default-include" };
|
|
305
|
+
if (!parsed.includeFilePattern) return { included: false, basis: "include-pattern" };
|
|
306
|
+
return { included: parsed.includeFilePattern.test(normalizedPath), basis: "include-pattern" };
|
|
193
307
|
}
|
|
194
308
|
|
|
195
309
|
function getSupportedExtensions(options: ts.CompilerOptions): string[] {
|
|
@@ -229,11 +343,6 @@ function isWithinOrEqual(root: string, target: string): boolean {
|
|
|
229
343
|
return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== "..");
|
|
230
344
|
}
|
|
231
345
|
|
|
232
|
-
function normalizePath(target: string): string {
|
|
233
|
-
const resolved = path.resolve(target).replaceAll("\\", "/");
|
|
234
|
-
return ts.sys.useCaseSensitiveFileNames ? resolved : resolved.toLowerCase();
|
|
235
|
-
}
|
|
236
|
-
|
|
237
346
|
/**
|
|
238
347
|
* Clear cached nearest-config lookups and parsed project config state.
|
|
239
348
|
* Useful for testing and after workspace file changes.
|
|
@@ -242,3 +351,46 @@ export function clearTsconfigCache(): void {
|
|
|
242
351
|
nearestConfigCache.clear();
|
|
243
352
|
parsedConfigCache.clear();
|
|
244
353
|
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Invalidate cached state for one config file that changed, was created, or
|
|
357
|
+
* was deleted.
|
|
358
|
+
*
|
|
359
|
+
* Removes the changed config and every cached root config that depends on it,
|
|
360
|
+
* so the next decision re-reads current config data. The wholesale
|
|
361
|
+
* {@link clearTsconfigCache} stays for lifecycle events; change signals use
|
|
362
|
+
* this targeted invalidation instead.
|
|
363
|
+
*/
|
|
364
|
+
export function invalidateTsconfigCacheForConfig(configPath: string): void {
|
|
365
|
+
const normalizedConfigPath = normalizePath(configPath);
|
|
366
|
+
const invalidated = new Set([normalizedConfigPath]);
|
|
367
|
+
|
|
368
|
+
for (const [cachedConfigPath, cacheEntry] of parsedConfigCache) {
|
|
369
|
+
if (cacheEntry.dependencies.has(normalizedConfigPath)) invalidated.add(cachedConfigPath);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
for (const cachedConfigPath of invalidated) {
|
|
373
|
+
parsedConfigCache.delete(cachedConfigPath);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
for (const [key, value] of nearestConfigCache) {
|
|
377
|
+
if (value !== null && invalidated.has(normalizePath(value))) nearestConfigCache.delete(key);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Invalidate nearest-config lookups that found nothing under `dir`.
|
|
383
|
+
*
|
|
384
|
+
* Used when a config is created: directories below the new config that
|
|
385
|
+
* previously resolved to no config may now resolve to it.
|
|
386
|
+
*/
|
|
387
|
+
export function invalidateTsconfigCacheForConfigDir(dir: string): void {
|
|
388
|
+
const normalizedDir = normalizePath(dir);
|
|
389
|
+
for (const [key, value] of nearestConfigCache) {
|
|
390
|
+
if (value !== null) continue;
|
|
391
|
+
const keyDir = key.split("::", 1)[0];
|
|
392
|
+
if (keyDir === normalizedDir || keyDir.startsWith(`${normalizedDir}/`)) {
|
|
393
|
+
nearestConfigCache.delete(key);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Bounded identity vocabulary for LSP debug telemetry.
|
|
2
|
+
//
|
|
3
|
+
// LSP debug events may carry server, workspace, file, method, and root
|
|
4
|
+
// identity for local protocol diagnosis. Every emitted identity string is
|
|
5
|
+
// bounded to MAX_IDENTITY_STRING UTF-16 code units (truncation marker
|
|
6
|
+
// included) and every identity list is bounded to MAX_SERVERS entries.
|
|
7
|
+
|
|
8
|
+
/** Maximum number of server entries in one bounded LSP debug payload. */
|
|
9
|
+
export const MAX_SERVERS = 16;
|
|
10
|
+
|
|
11
|
+
/** Maximum UTF-16 code-unit length of one emitted identity string in an LSP debug payload. */
|
|
12
|
+
export const MAX_IDENTITY_STRING = 512;
|
|
13
|
+
|
|
14
|
+
/** Marker appended to an identity string truncated to MAX_IDENTITY_STRING. */
|
|
15
|
+
export const IDENTITY_TRUNCATION_MARKER = "…";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* JSON-RPC error code recorded for client-local request timeouts.
|
|
19
|
+
*
|
|
20
|
+
* The JSON-RPC and LSP vocabularies define no timeout code. This constant
|
|
21
|
+
* uses the JSON-RPC implementation-defined error range (-32099..-32000) at a
|
|
22
|
+
* value that vscode-jsonrpc transport codes (-32099, -32098, -32097, -32096,
|
|
23
|
+
* -32002, -32001) and LSP error codes never use.
|
|
24
|
+
*/
|
|
25
|
+
export const LSP_REQUEST_TIMEOUT_ERROR_CODE = -32_095;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Truncate one identity string so the emitted value (marker included) never
|
|
29
|
+
* exceeds MAX_IDENTITY_STRING UTF-16 code units.
|
|
30
|
+
*/
|
|
31
|
+
export function truncateIdentity(value: string): string {
|
|
32
|
+
if (value.length <= MAX_IDENTITY_STRING) return value;
|
|
33
|
+
return `${value.slice(0, MAX_IDENTITY_STRING - 1)}${IDENTITY_TRUNCATION_MARKER}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Bound an optional workspace root for event-level identity; undefined stays absent. */
|
|
37
|
+
export function boundCwd(cwd: string | undefined): string | undefined {
|
|
38
|
+
if (cwd === undefined) return undefined;
|
|
39
|
+
return truncateIdentity(cwd);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Bound a server-name list to MAX_SERVERS entries, truncating each name. */
|
|
43
|
+
export function boundServerNames(names: readonly string[]): string[] {
|
|
44
|
+
return names.slice(0, MAX_SERVERS).map(truncateIdentity);
|
|
45
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Reason for a partial or unavailable single-file result when the push stays tentative. */
|
|
2
|
+
export const TENTATIVE_PUSH_UNAVAILABLE_REASON =
|
|
3
|
+
"The current push publication is tentative: a later diagnostic republish for the same document synchronization is required.";
|
|
4
|
+
|
|
5
|
+
/** A final evidence state for one tracked document. */
|
|
6
|
+
export type DiagnosticEvidenceStatus = "confirmed" | "unconfirmed" | "failed" | "removed";
|
|
7
|
+
|
|
8
|
+
/** Freshness evidence for one tracked document. */
|
|
9
|
+
export interface DiagnosticEvidenceDocument {
|
|
10
|
+
readonly file: string;
|
|
11
|
+
readonly status: DiagnosticEvidenceStatus;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Exact document coverage from a diagnostic snapshot or refresh attempt. */
|
|
15
|
+
export interface DiagnosticEvidenceSummary {
|
|
16
|
+
readonly requested: number;
|
|
17
|
+
readonly confirmed: number;
|
|
18
|
+
readonly unconfirmed: number;
|
|
19
|
+
readonly failed: number;
|
|
20
|
+
readonly removed: number;
|
|
21
|
+
readonly documents: readonly DiagnosticEvidenceDocument[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Build exact coverage counts from one document outcome per requested document. */
|
|
25
|
+
export function summarizeDiagnosticEvidence(
|
|
26
|
+
documents: readonly DiagnosticEvidenceDocument[],
|
|
27
|
+
): DiagnosticEvidenceSummary {
|
|
28
|
+
const counts = {
|
|
29
|
+
requested: documents.length,
|
|
30
|
+
confirmed: 0,
|
|
31
|
+
unconfirmed: 0,
|
|
32
|
+
failed: 0,
|
|
33
|
+
removed: 0,
|
|
34
|
+
};
|
|
35
|
+
for (const document of documents) counts[document.status]++;
|
|
36
|
+
return { ...counts, documents: [...documents] };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Return an empty evidence summary for a refresh with no tracked documents. */
|
|
40
|
+
export function emptyDiagnosticEvidence(): DiagnosticEvidenceSummary {
|
|
41
|
+
return summarizeDiagnosticEvidence([]);
|
|
42
|
+
}
|
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { FileChangeType, type FileEvent } from "../config/types.ts";
|
|
4
|
-
import { fileToUri } from "../utils.ts";
|
|
4
|
+
import { fileToUri, uriToFile } from "../utils.ts";
|
|
5
5
|
|
|
6
6
|
const IGNORED_DIRECTORIES = new Set(["node_modules", ".pnpm", ".git", "dist", "coverage"]);
|
|
7
7
|
const ROOT_LOCKFILES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"];
|
|
8
8
|
|
|
9
|
+
export interface WorkspaceSentinelScanOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Also track every regular source file (not just sentinels). The workspace
|
|
12
|
+
* refresh path uses this to discover files created since the last refresh;
|
|
13
|
+
* the diff then reports them as `sourceChanges` so the caller can pull them.
|
|
14
|
+
*/
|
|
15
|
+
includeSourceFiles?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
/** Build a fresh snapshot of workspace sentinel files and their mtimes. */
|
|
10
|
-
export function scanWorkspaceSentinels(
|
|
19
|
+
export function scanWorkspaceSentinels(
|
|
20
|
+
cwd: string,
|
|
21
|
+
options: WorkspaceSentinelScanOptions = {},
|
|
22
|
+
): Map<string, number> {
|
|
11
23
|
const resolvedCwd = path.resolve(cwd);
|
|
12
24
|
const snapshot = new Map<string, number>();
|
|
13
25
|
|
|
14
26
|
if (!fs.existsSync(resolvedCwd)) return snapshot;
|
|
15
27
|
|
|
16
28
|
try {
|
|
17
|
-
walkWorkspace(resolvedCwd, resolvedCwd, snapshot);
|
|
29
|
+
walkWorkspace(resolvedCwd, resolvedCwd, snapshot, options.includeSourceFiles ?? false);
|
|
18
30
|
} catch {
|
|
19
31
|
return snapshot;
|
|
20
32
|
}
|
|
@@ -48,15 +60,34 @@ export function diffWorkspaceSentinelSnapshot(
|
|
|
48
60
|
return changes.sort((a, b) => a.uri.localeCompare(b.uri));
|
|
49
61
|
}
|
|
50
62
|
|
|
51
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Refresh a previous snapshot and return the new snapshot plus change events.
|
|
65
|
+
*
|
|
66
|
+
* When `includeSourceFiles` is set, the returned `changes` list keeps
|
|
67
|
+
* sentinel-file events only; source-file events are returned separately in
|
|
68
|
+
* `sourceChanges` so the caller can act on them without interpreting every
|
|
69
|
+
* regular file edit as a config-level workspace change.
|
|
70
|
+
*/
|
|
52
71
|
export function syncWorkspaceSentinelSnapshot(
|
|
53
72
|
cwd: string,
|
|
54
73
|
previous: Map<string, number>,
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
options: WorkspaceSentinelScanOptions = {},
|
|
75
|
+
): { snapshot: Map<string, number>; changes: FileEvent[]; sourceChanges: FileEvent[] } {
|
|
76
|
+
const snapshot = scanWorkspaceSentinels(cwd, options);
|
|
77
|
+
const changes = diffWorkspaceSentinelSnapshot(previous, snapshot);
|
|
78
|
+
if (!options.includeSourceFiles) return { snapshot, changes, sourceChanges: [] };
|
|
79
|
+
|
|
80
|
+
const sourceChanges = changes.filter((change) => {
|
|
81
|
+
const filePath = uriToFile(change.uri);
|
|
82
|
+
return !isWorkspaceSentinelPath(filePath, cwd);
|
|
83
|
+
});
|
|
57
84
|
return {
|
|
58
85
|
snapshot,
|
|
59
|
-
changes:
|
|
86
|
+
changes: changes.filter((change) => {
|
|
87
|
+
const filePath = uriToFile(change.uri);
|
|
88
|
+
return isWorkspaceSentinelPath(filePath, cwd);
|
|
89
|
+
}),
|
|
90
|
+
sourceChanges,
|
|
60
91
|
};
|
|
61
92
|
}
|
|
62
93
|
|
|
@@ -66,7 +97,12 @@ export function isWorkspaceRecoveryTrigger(filePath: string, cwd: string): boole
|
|
|
66
97
|
return isWorkspaceSentinelPath(path.resolve(root, filePath), root);
|
|
67
98
|
}
|
|
68
99
|
|
|
69
|
-
function walkWorkspace(
|
|
100
|
+
function walkWorkspace(
|
|
101
|
+
root: string,
|
|
102
|
+
directory: string,
|
|
103
|
+
snapshot: Map<string, number>,
|
|
104
|
+
includeSourceFiles: boolean,
|
|
105
|
+
): void {
|
|
70
106
|
let entries: fs.Dirent[];
|
|
71
107
|
try {
|
|
72
108
|
entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
@@ -80,14 +116,14 @@ function walkWorkspace(root: string, directory: string, snapshot: Map<string, nu
|
|
|
80
116
|
for (const entry of entries) {
|
|
81
117
|
if (entry.isDirectory()) {
|
|
82
118
|
if (IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
83
|
-
walkWorkspace(root, path.join(directory, entry.name), snapshot);
|
|
119
|
+
walkWorkspace(root, path.join(directory, entry.name), snapshot, includeSourceFiles);
|
|
84
120
|
continue;
|
|
85
121
|
}
|
|
86
122
|
|
|
87
123
|
if (!entry.isFile()) continue;
|
|
88
124
|
|
|
89
125
|
const filePath = path.join(directory, entry.name);
|
|
90
|
-
if (!isWorkspaceSentinelPath(filePath, root)) continue;
|
|
126
|
+
if (!includeSourceFiles && !isWorkspaceSentinelPath(filePath, root)) continue;
|
|
91
127
|
try {
|
|
92
128
|
snapshot.set(filePath, fs.statSync(filePath).mtimeMs);
|
|
93
129
|
} catch {
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
type CodeRequestControl,
|
|
4
|
+
isCodeRequestInterruption,
|
|
5
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
2
6
|
import type { LspClient } from "../client/client.ts";
|
|
7
|
+
import { getDiagnosticFileState } from "../client/client-file-state.ts";
|
|
8
|
+
import {
|
|
9
|
+
type DiagnosticEvidenceDocument,
|
|
10
|
+
type DiagnosticEvidenceSummary,
|
|
11
|
+
summarizeDiagnosticEvidence,
|
|
12
|
+
} from "../diagnostics/evidence.ts";
|
|
13
|
+
import { uriToFile } from "../utils.ts";
|
|
3
14
|
|
|
4
15
|
export function closeFileAcrossClients(clients: Iterable<LspClient>, filePath: string): void {
|
|
5
16
|
const resolvedPath = path.resolve(filePath);
|
|
@@ -21,14 +32,73 @@ export function pruneMissingFilesFromClients(clients: Iterable<LspClient>): stri
|
|
|
21
32
|
|
|
22
33
|
export async function refreshOpenDiagnosticsForClients(
|
|
23
34
|
clients: Iterable<LspClient>,
|
|
24
|
-
options?: { maxWaitMs?: number; quietMs?: number },
|
|
25
|
-
): Promise<
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
options?: { maxWaitMs?: number; quietMs?: number } & CodeRequestControl,
|
|
36
|
+
): Promise<DiagnosticEvidenceSummary> {
|
|
37
|
+
const documents: DiagnosticEvidenceDocument[] = [];
|
|
38
|
+
const refreshes = Array.from(clients).map(async (client) => {
|
|
39
|
+
const trackedFiles = [...client.openFiles];
|
|
40
|
+
if (client.status !== "running") {
|
|
41
|
+
documents.push(...failedCoverageForClient(client, trackedFiles));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const result = await client.refreshOpenDiagnostics(options);
|
|
47
|
+
const reportedFiles = new Set(result.documents.map((document) => document.file));
|
|
48
|
+
documents.push(...result.documents);
|
|
49
|
+
documents.push(
|
|
50
|
+
...trackedFiles
|
|
51
|
+
.filter((file) => !reportedFiles.has(file))
|
|
52
|
+
.map((file) => ({ file, status: "failed" as const })),
|
|
53
|
+
);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// A cancelled refresh must not degrade into failed coverage evidence.
|
|
56
|
+
if (isCodeRequestInterruption(error, options)) throw error;
|
|
57
|
+
// Keep failed coverage for every document requested by the client.
|
|
58
|
+
documents.push(...failedCoverageForClient(client, trackedFiles));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
33
61
|
await Promise.all(refreshes);
|
|
62
|
+
return summarizeDiagnosticEvidence(deduplicateEvidenceDocuments(documents));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function failedCoverageForClient(
|
|
66
|
+
client: LspClient,
|
|
67
|
+
trackedFiles: readonly string[],
|
|
68
|
+
): DiagnosticEvidenceDocument[] {
|
|
69
|
+
const snapshot = client.getDiagnosticSnapshot();
|
|
70
|
+
const files = new Set([
|
|
71
|
+
...trackedFiles,
|
|
72
|
+
...snapshot.documents.map((document) => uriToFile(document.uri)),
|
|
73
|
+
]);
|
|
74
|
+
return Array.from(files).map((file) => ({
|
|
75
|
+
file,
|
|
76
|
+
status: getDiagnosticFileState(file) === "removed" ? ("removed" as const) : ("failed" as const),
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function deduplicateEvidenceDocuments(
|
|
81
|
+
documents: readonly DiagnosticEvidenceDocument[],
|
|
82
|
+
): DiagnosticEvidenceDocument[] {
|
|
83
|
+
const byFile = new Map<string, DiagnosticEvidenceDocument>();
|
|
84
|
+
for (const document of documents) {
|
|
85
|
+
const existing = byFile.get(document.file);
|
|
86
|
+
if (!existing || evidenceStatusRank(document.status) > evidenceStatusRank(existing.status)) {
|
|
87
|
+
byFile.set(document.file, document);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return Array.from(byFile.values()).sort((a, b) => a.file.localeCompare(b.file));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function evidenceStatusRank(status: DiagnosticEvidenceDocument["status"]): number {
|
|
94
|
+
switch (status) {
|
|
95
|
+
case "confirmed":
|
|
96
|
+
return 1;
|
|
97
|
+
case "unconfirmed":
|
|
98
|
+
return 2;
|
|
99
|
+
case "failed":
|
|
100
|
+
return 3;
|
|
101
|
+
case "removed":
|
|
102
|
+
return 4;
|
|
103
|
+
}
|
|
34
104
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
type CodeQueryResult,
|
|
4
|
+
type CodeRequestControl,
|
|
5
|
+
mapCodeQueryResult,
|
|
6
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
2
7
|
import type { LspClient } from "../client/client.ts";
|
|
8
|
+
import type { DiagnosticEntry } from "../client/client-document-state.ts";
|
|
3
9
|
import type { Diagnostic } from "../config/types.ts";
|
|
4
10
|
import { relativeFilePathFromUri } from "../diagnostics/diagnostic-summary.ts";
|
|
5
11
|
import { shouldIgnoreLspPath } from "../summary.ts";
|
|
@@ -10,23 +16,27 @@ export async function syncClientFileAndGetDiagnostics(
|
|
|
10
16
|
client: Pick<LspClient, "syncAndWaitForDiagnostics">,
|
|
11
17
|
filePath: string,
|
|
12
18
|
maxSeverity: number,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
19
|
+
control?: CodeRequestControl,
|
|
20
|
+
): Promise<CodeQueryResult<Diagnostic[]>> {
|
|
21
|
+
const content = readFileSync(filePath, "utf-8");
|
|
22
|
+
const result = control
|
|
23
|
+
? await client.syncAndWaitForDiagnostics(filePath, content, control)
|
|
24
|
+
: await client.syncAndWaitForDiagnostics(filePath, content);
|
|
25
|
+
return mapCodeQueryResult(result, (diagnostics) =>
|
|
26
|
+
filterDiagnosticsBySeverity(diagnostics, maxSeverity),
|
|
17
27
|
);
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
export function collectOutstandingDiagnosticsDetailed(
|
|
21
|
-
|
|
31
|
+
clientEntries: Iterable<ReadonlyArray<DiagnosticEntry>>,
|
|
22
32
|
cwd: string,
|
|
23
33
|
excludePatterns: string[],
|
|
24
34
|
maxSeverity: number,
|
|
25
35
|
): Array<{ file: string; diagnostics: Diagnostic[] }> {
|
|
26
36
|
const fileDiags = new Map<string, Diagnostic[]>();
|
|
27
37
|
|
|
28
|
-
for (const
|
|
29
|
-
for (const entry of
|
|
38
|
+
for (const entries of clientEntries) {
|
|
39
|
+
for (const entry of entries) {
|
|
30
40
|
const file = relativeFilePathFromUri(entry.uri, cwd);
|
|
31
41
|
if (shouldIgnoreLspPath(file, cwd)) continue;
|
|
32
42
|
if (isExcludedByPattern(file, excludePatterns)) continue;
|