@duckmind/dm-linux-x64 0.65.8 → 0.66.2
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/dm +2 -2
- package/extensions/.dm-extensions.json +14 -548
- package/extensions/dm-lens/clients/dispatch/runners/utils/runner-helpers.js +1 -1
- package/extensions/dm-mcp-adapter/mcp-keyring-helper.cjs +1 -1
- package/extensions/dm-ponytail/hooks/ponytail-activate.js +1 -1
- package/extensions/dm-ponytail/hooks/ponytail-instructions.js +1 -1
- package/extensions/dm-subagents/src/extension/tool-activation.js +7 -2
- package/extensions/dm-subagents/src/runs/background/runner-aliases.js +2 -2
- package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +4 -1
- package/extensions/dm-subagents/src/runs/shared/child-session.js +8 -6
- package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +5 -1
- package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +9 -1
- package/extensions/dm-subagents/src/watchdog/review.js +4 -1
- package/extensions/dm-web-access/tool-activation.js +0 -1
- package/package.json +1 -1
- package/extensions/dm-lens/dist/clients/actionable-warnings-logger.js +0 -24
- package/extensions/dm-lens/dist/clients/actionable-warnings.js +0 -1014
- package/extensions/dm-lens/dist/clients/advisory-provenance.js +0 -385
- package/extensions/dm-lens/dist/clients/agent-behavior-client.js +0 -94
- package/extensions/dm-lens/dist/clients/agent-nudge.js +0 -151
- package/extensions/dm-lens/dist/clients/analysed-root.js +0 -1
- package/extensions/dm-lens/dist/clients/ast-grep-client.js +0 -576
- package/extensions/dm-lens/dist/clients/ast-grep-rule-manager.js +0 -114
- package/extensions/dm-lens/dist/clients/ast-grep-tool-logger.js +0 -85
- package/extensions/dm-lens/dist/clients/ast-grep-types.js +0 -1
- package/extensions/dm-lens/dist/clients/ast-grep-yaml-synth.js +0 -109
- package/extensions/dm-lens/dist/clients/atomic-write-staging.js +0 -66
- package/extensions/dm-lens/dist/clients/atomic-write.js +0 -37
- package/extensions/dm-lens/dist/clients/availability-probe-flight.js +0 -11
- package/extensions/dm-lens/dist/clients/bash-file-access.js +0 -830
- package/extensions/dm-lens/dist/clients/biome-client.js +0 -221
- package/extensions/dm-lens/dist/clients/blocker-freshness.js +0 -385
- package/extensions/dm-lens/dist/clients/blocker-past-eof.js +0 -49
- package/extensions/dm-lens/dist/clients/bootstrap.js +0 -246
- package/extensions/dm-lens/dist/clients/bounded-cache.js +0 -105
- package/extensions/dm-lens/dist/clients/bounded-pid-file-lock.js +0 -173
- package/extensions/dm-lens/dist/clients/bounded-telemetry.js +0 -104
- package/extensions/dm-lens/dist/clients/build-identity.js +0 -45
- package/extensions/dm-lens/dist/clients/bundled-resource-health.js +0 -41
- package/extensions/dm-lens/dist/clients/bus-events-logger.js +0 -48
- package/extensions/dm-lens/dist/clients/bus-publish.js +0 -117
- package/extensions/dm-lens/dist/clients/cache/rule-cache.js +0 -103
- package/extensions/dm-lens/dist/clients/cache-manager.js +0 -340
- package/extensions/dm-lens/dist/clients/cache-observability.js +0 -847
- package/extensions/dm-lens/dist/clients/call-graph.js +0 -628
- package/extensions/dm-lens/dist/clients/cargo-manifest.js +0 -151
- package/extensions/dm-lens/dist/clients/cascade-budget.js +0 -52
- package/extensions/dm-lens/dist/clients/cascade-format.js +0 -160
- package/extensions/dm-lens/dist/clients/cascade-logger.js +0 -22
- package/extensions/dm-lens/dist/clients/cascade-types.js +0 -1
- package/extensions/dm-lens/dist/clients/child-unref.js +0 -59
- package/extensions/dm-lens/dist/clients/code-quality-warnings.js +0 -192
- package/extensions/dm-lens/dist/clients/codebase-model.js +0 -110
- package/extensions/dm-lens/dist/clients/collateral-test-role.js +0 -8
- package/extensions/dm-lens/dist/clients/complexity-client.js +0 -398
- package/extensions/dm-lens/dist/clients/config-core/deny.js +0 -117
- package/extensions/dm-lens/dist/clients/config-core/index.js +0 -2
- package/extensions/dm-lens/dist/clients/config-core/merge.js +0 -228
- package/extensions/dm-lens/dist/clients/config-core/normalize.js +0 -217
- package/extensions/dm-lens/dist/clients/config-core/process-spec.js +0 -147
- package/extensions/dm-lens/dist/clients/config-core/provenance.js +0 -54
- package/extensions/dm-lens/dist/clients/config-core/records.js +0 -79
- package/extensions/dm-lens/dist/clients/config-core/resolve.js +0 -48
- package/extensions/dm-lens/dist/clients/config-core/safe-object.js +0 -22
- package/extensions/dm-lens/dist/clients/config-core/schema.js +0 -90
- package/extensions/dm-lens/dist/clients/config-diagnostic-codes.js +0 -90
- package/extensions/dm-lens/dist/clients/config-locations.js +0 -175
- package/extensions/dm-lens/dist/clients/config-resolve.js +0 -359
- package/extensions/dm-lens/dist/clients/config-schema.js +0 -128
- package/extensions/dm-lens/dist/clients/config-warn.js +0 -146
- package/extensions/dm-lens/dist/clients/console-guard-install.js +0 -4
- package/extensions/dm-lens/dist/clients/cooperative-budget.js +0 -36
- package/extensions/dm-lens/dist/clients/dead-code-client.js +0 -342
- package/extensions/dm-lens/dist/clients/dead-code-logger.js +0 -18
- package/extensions/dm-lens/dist/clients/deadline-utils.js +0 -117
- package/extensions/dm-lens/dist/clients/debug-handles.js +0 -121
- package/extensions/dm-lens/dist/clients/debug-heap.js +0 -80
- package/extensions/dm-lens/dist/clients/deferred-lsp-work.js +0 -33
- package/extensions/dm-lens/dist/clients/degradation-ledger.js +0 -272
- package/extensions/dm-lens/dist/clients/demoted-finding-render.js +0 -61
- package/extensions/dm-lens/dist/clients/dependency-checker.js +0 -525
- package/extensions/dm-lens/dist/clients/deps/ast-grep-napi.js +0 -17
- package/extensions/dm-lens/dist/clients/deps/dm-host-deps.js +0 -10
- package/extensions/dm-lens/dist/clients/deps/dm-tui-compat.js +0 -58
- package/extensions/dm-lens/dist/clients/deps/js-yaml.js +0 -7
- package/extensions/dm-lens/dist/clients/deps/minimatch.js +0 -6
- package/extensions/dm-lens/dist/clients/deps/pidusage.js +0 -5
- package/extensions/dm-lens/dist/clients/deps/typebox.js +0 -5
- package/extensions/dm-lens/dist/clients/deps/vscode-jsonrpc.js +0 -8
- package/extensions/dm-lens/dist/clients/deps/web-tree-sitter.js +0 -11
- package/extensions/dm-lens/dist/clients/diagnostic-dispositions.js +0 -318
- package/extensions/dm-lens/dist/clients/diagnostic-line-freshness.js +0 -144
- package/extensions/dm-lens/dist/clients/diagnostic-logger.js +0 -60
- package/extensions/dm-lens/dist/clients/diagnostic-tracker.js +0 -89
- package/extensions/dm-lens/dist/clients/diagnostics-publish.js +0 -134
- package/extensions/dm-lens/dist/clients/dispatch/ast-grep-catalog.js +0 -16
- package/extensions/dm-lens/dist/clients/dispatch/auxiliary-lsp.js +0 -158
- package/extensions/dm-lens/dist/clients/dispatch/collect-later-tier.js +0 -25
- package/extensions/dm-lens/dist/clients/dispatch/diagnostic-taxonomy.js +0 -58
- package/extensions/dm-lens/dist/clients/dispatch/dispatcher.js +0 -953
- package/extensions/dm-lens/dist/clients/dispatch/fact-provider-types.js +0 -1
- package/extensions/dm-lens/dist/clients/dispatch/fact-rule-runner.js +0 -17
- package/extensions/dm-lens/dist/clients/dispatch/fact-runner.js +0 -18
- package/extensions/dm-lens/dist/clients/dispatch/fact-scheduler.js +0 -52
- package/extensions/dm-lens/dist/clients/dispatch/fact-store.js +0 -199
- package/extensions/dm-lens/dist/clients/dispatch/facts/comment-facts.js +0 -20
- package/extensions/dm-lens/dist/clients/dispatch/facts/file-content.js +0 -18
- package/extensions/dm-lens/dist/clients/dispatch/facts/function-facts.js +0 -281
- package/extensions/dm-lens/dist/clients/dispatch/facts/import-facts.js +0 -169
- package/extensions/dm-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -19
- package/extensions/dm-lens/dist/clients/dispatch/facts/try-catch-facts.js +0 -95
- package/extensions/dm-lens/dist/clients/dispatch/finding-policy.js +0 -159
- package/extensions/dm-lens/dist/clients/dispatch/indent-detect.js +0 -227
- package/extensions/dm-lens/dist/clients/dispatch/inline-suppressions.js +0 -52
- package/extensions/dm-lens/dist/clients/dispatch/integration.js +0 -1463
- package/extensions/dm-lens/dist/clients/dispatch/lazy.js +0 -8
- package/extensions/dm-lens/dist/clients/dispatch/pending-runner-findings.js +0 -75
- package/extensions/dm-lens/dist/clients/dispatch/plan.js +0 -278
- package/extensions/dm-lens/dist/clients/dispatch/priorities.js +0 -14
- package/extensions/dm-lens/dist/clients/dispatch/rule-id-normalize.js +0 -42
- package/extensions/dm-lens/dist/clients/dispatch/rule-ignores.js +0 -21
- package/extensions/dm-lens/dist/clients/dispatch/rule-policy.js +0 -73
- package/extensions/dm-lens/dist/clients/dispatch/rules/async-noise.js +0 -31
- package/extensions/dm-lens/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
- package/extensions/dm-lens/dist/clients/dispatch/rules/cors-wildcard.js +0 -35
- package/extensions/dm-lens/dist/clients/dispatch/rules/error-obscuring.js +0 -29
- package/extensions/dm-lens/dist/clients/dispatch/rules/error-swallowing.js +0 -29
- package/extensions/dm-lens/dist/clients/dispatch/rules/framework-call-noise.js +0 -31
- package/extensions/dm-lens/dist/clients/dispatch/rules/high-complexity.js +0 -56
- package/extensions/dm-lens/dist/clients/dispatch/rules/high-fan-out.js +0 -45
- package/extensions/dm-lens/dist/clients/dispatch/rules/high-import-coupling.js +0 -31
- package/extensions/dm-lens/dist/clients/dispatch/rules/missing-error-propagation.js +0 -45
- package/extensions/dm-lens/dist/clients/dispatch/rules/no-commented-credentials.js +0 -46
- package/extensions/dm-lens/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -38
- package/extensions/dm-lens/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
- package/extensions/dm-lens/dist/clients/dispatch/rules/unsafe-boundary.js +0 -101
- package/extensions/dm-lens/dist/clients/dispatch/runner-context.js +0 -46
- package/extensions/dm-lens/dist/clients/dispatch/runners/actionlint.js +0 -98
- package/extensions/dm-lens/dist/clients/dispatch/runners/ast-grep-napi.js +0 -690
- package/extensions/dm-lens/dist/clients/dispatch/runners/biome-check.js +0 -202
- package/extensions/dm-lens/dist/clients/dispatch/runners/cpp-check.js +0 -190
- package/extensions/dm-lens/dist/clients/dispatch/runners/credo.js +0 -66
- package/extensions/dm-lens/dist/clients/dispatch/runners/cue-vet.js +0 -167
- package/extensions/dm-lens/dist/clients/dispatch/runners/dart-analyze.js +0 -151
- package/extensions/dm-lens/dist/clients/dispatch/runners/detekt.js +0 -157
- package/extensions/dm-lens/dist/clients/dispatch/runners/dotnet-build.js +0 -157
- package/extensions/dm-lens/dist/clients/dispatch/runners/elixir-check.js +0 -153
- package/extensions/dm-lens/dist/clients/dispatch/runners/eslint.js +0 -99
- package/extensions/dm-lens/dist/clients/dispatch/runners/fact-rules.js +0 -20
- package/extensions/dm-lens/dist/clients/dispatch/runners/fish-indent.js +0 -65
- package/extensions/dm-lens/dist/clients/dispatch/runners/gleam-check.js +0 -107
- package/extensions/dm-lens/dist/clients/dispatch/runners/go-vet.js +0 -51
- package/extensions/dm-lens/dist/clients/dispatch/runners/golangci-lint.js +0 -89
- package/extensions/dm-lens/dist/clients/dispatch/runners/hadolint.js +0 -83
- package/extensions/dm-lens/dist/clients/dispatch/runners/helm-lint.js +0 -144
- package/extensions/dm-lens/dist/clients/dispatch/runners/helm-render.js +0 -593
- package/extensions/dm-lens/dist/clients/dispatch/runners/htmlhint.js +0 -103
- package/extensions/dm-lens/dist/clients/dispatch/runners/index.js +0 -100
- package/extensions/dm-lens/dist/clients/dispatch/runners/javac.js +0 -82
- package/extensions/dm-lens/dist/clients/dispatch/runners/ktlint.js +0 -93
- package/extensions/dm-lens/dist/clients/dispatch/runners/lsp.js +0 -206
- package/extensions/dm-lens/dist/clients/dispatch/runners/markdownlint.js +0 -129
- package/extensions/dm-lens/dist/clients/dispatch/runners/mypy.js +0 -70
- package/extensions/dm-lens/dist/clients/dispatch/runners/oxlint.js +0 -305
- package/extensions/dm-lens/dist/clients/dispatch/runners/php-lint.js +0 -70
- package/extensions/dm-lens/dist/clients/dispatch/runners/phpstan.js +0 -90
- package/extensions/dm-lens/dist/clients/dispatch/runners/prisma-validate.js +0 -56
- package/extensions/dm-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -307
- package/extensions/dm-lens/dist/clients/dispatch/runners/pyright.js +0 -118
- package/extensions/dm-lens/dist/clients/dispatch/runners/rubocop.js +0 -79
- package/extensions/dm-lens/dist/clients/dispatch/runners/ruff.js +0 -69
- package/extensions/dm-lens/dist/clients/dispatch/runners/rust-clippy.js +0 -147
- package/extensions/dm-lens/dist/clients/dispatch/runners/shellcheck.js +0 -111
- package/extensions/dm-lens/dist/clients/dispatch/runners/shfmt.js +0 -81
- package/extensions/dm-lens/dist/clients/dispatch/runners/spellcheck.js +0 -74
- package/extensions/dm-lens/dist/clients/dispatch/runners/spotbugs.js +0 -208
- package/extensions/dm-lens/dist/clients/dispatch/runners/sqlfluff.js +0 -136
- package/extensions/dm-lens/dist/clients/dispatch/runners/stylelint.js +0 -118
- package/extensions/dm-lens/dist/clients/dispatch/runners/swiftlint.js +0 -143
- package/extensions/dm-lens/dist/clients/dispatch/runners/taplo.js +0 -97
- package/extensions/dm-lens/dist/clients/dispatch/runners/terragrunt.js +0 -110
- package/extensions/dm-lens/dist/clients/dispatch/runners/tflint.js +0 -104
- package/extensions/dm-lens/dist/clients/dispatch/runners/tree-sitter.js +0 -516
- package/extensions/dm-lens/dist/clients/dispatch/runners/trivy-config.js +0 -155
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/availability-policy.js +0 -285
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -61
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -61
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -130
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1067
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -40
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/tool-failure.js +0 -117
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -88
- package/extensions/dm-lens/dist/clients/dispatch/runners/utils.js +0 -8
- package/extensions/dm-lens/dist/clients/dispatch/runners/vale.js +0 -106
- package/extensions/dm-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -163
- package/extensions/dm-lens/dist/clients/dispatch/runners/yamllint.js +0 -83
- package/extensions/dm-lens/dist/clients/dispatch/runners/zig-check.js +0 -72
- package/extensions/dm-lens/dist/clients/dispatch/suppress-writer.js +0 -57
- package/extensions/dm-lens/dist/clients/dispatch/tool-profile.js +0 -30
- package/extensions/dm-lens/dist/clients/dispatch/types.js +0 -7
- package/extensions/dm-lens/dist/clients/dispatch/utils/format-utils.js +0 -45
- package/extensions/dm-lens/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -31
- package/extensions/dm-lens/dist/clients/disposition-logger.js +0 -19
- package/extensions/dm-lens/dist/clients/disposition-publish.js +0 -88
- package/extensions/dm-lens/dist/clients/durable-store.js +0 -77
- package/extensions/dm-lens/dist/clients/effective-config.js +0 -160
- package/extensions/dm-lens/dist/clients/env-utils.js +0 -26
- package/extensions/dm-lens/dist/clients/error-class.js +0 -3
- package/extensions/dm-lens/dist/clients/eval-timestamp.js +0 -2
- package/extensions/dm-lens/dist/clients/event-loop-hold.js +0 -119
- package/extensions/dm-lens/dist/clients/event-loop-monitor.js +0 -84
- package/extensions/dm-lens/dist/clients/extension-log.js +0 -230
- package/extensions/dm-lens/dist/clients/extension-mode.js +0 -20
- package/extensions/dm-lens/dist/clients/feature-hints.js +0 -88
- package/extensions/dm-lens/dist/clients/file-kinds.js +0 -304
- package/extensions/dm-lens/dist/clients/file-role.js +0 -50
- package/extensions/dm-lens/dist/clients/file-time.js +0 -112
- package/extensions/dm-lens/dist/clients/file-utils.js +0 -630
- package/extensions/dm-lens/dist/clients/finding-delivery-gate.js +0 -129
- package/extensions/dm-lens/dist/clients/finding-identity.js +0 -23
- package/extensions/dm-lens/dist/clients/fix-worklog.js +0 -75
- package/extensions/dm-lens/dist/clients/format-events-publish.js +0 -230
- package/extensions/dm-lens/dist/clients/format-service.js +0 -154
- package/extensions/dm-lens/dist/clients/formatters-lazy.js +0 -8
- package/extensions/dm-lens/dist/clients/formatters.js +0 -1351
- package/extensions/dm-lens/dist/clients/freshness-cadence.js +0 -1
- package/extensions/dm-lens/dist/clients/freshness.js +0 -8
- package/extensions/dm-lens/dist/clients/generated-artifacts.js +0 -209
- package/extensions/dm-lens/dist/clients/generation-guard.js +0 -105
- package/extensions/dm-lens/dist/clients/git-guard.js +0 -770
- package/extensions/dm-lens/dist/clients/git-tracked-ignore.js +0 -120
- package/extensions/dm-lens/dist/clients/gitleaks-client.js +0 -282
- package/extensions/dm-lens/dist/clients/go-client.js +0 -47
- package/extensions/dm-lens/dist/clients/govulncheck-client.js +0 -411
- package/extensions/dm-lens/dist/clients/gradle-ktfmt-style.js +0 -177
- package/extensions/dm-lens/dist/clients/grammar-source.js +0 -224
- package/extensions/dm-lens/dist/clients/gzip-stage-write.js +0 -85
- package/extensions/dm-lens/dist/clients/hashline-anchor.js +0 -222
- package/extensions/dm-lens/dist/clients/hook-budgets.js +0 -16
- package/extensions/dm-lens/dist/clients/host-edit-normalize.js +0 -87
- package/extensions/dm-lens/dist/clients/host-ports.js +0 -35
- package/extensions/dm-lens/dist/clients/indent-retarget.js +0 -102
- package/extensions/dm-lens/dist/clients/inline-blocker-dispositions.js +0 -103
- package/extensions/dm-lens/dist/clients/install-diagnostics.js +0 -123
- package/extensions/dm-lens/dist/clients/installer/index.js +0 -3770
- package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh-session.js +0 -17
- package/extensions/dm-lens/dist/clients/installer/managed-tool-refresh.js +0 -522
- package/extensions/dm-lens/dist/clients/instance-reaper-state.js +0 -3
- package/extensions/dm-lens/dist/clients/instance-reaper.js +0 -577
- package/extensions/dm-lens/dist/clients/instance-registry-lock.js +0 -131
- package/extensions/dm-lens/dist/clients/instance-registry.js +0 -452
- package/extensions/dm-lens/dist/clients/jscpd-client.js +0 -252
- package/extensions/dm-lens/dist/clients/json-cache-read.js +0 -25
- package/extensions/dm-lens/dist/clients/knip-client.js +0 -472
- package/extensions/dm-lens/dist/clients/language-policy.js +0 -242
- package/extensions/dm-lens/dist/clients/language-profile.js +0 -261
- package/extensions/dm-lens/dist/clients/language-registry.js +0 -424
- package/extensions/dm-lens/dist/clients/latency-logger.js +0 -192
- package/extensions/dm-lens/dist/clients/lazy-import.js +0 -14
- package/extensions/dm-lens/dist/clients/ledger-bounds.js +0 -21
- package/extensions/dm-lens/dist/clients/lens-config.js +0 -237
- package/extensions/dm-lens/dist/clients/lens-engine.js +0 -181
- package/extensions/dm-lens/dist/clients/lens-events.js +0 -105
- package/extensions/dm-lens/dist/clients/lens-flag-registry.js +0 -302
- package/extensions/dm-lens/dist/clients/lens-map.js +0 -980
- package/extensions/dm-lens/dist/clients/live-bus-emitter.js +0 -53
- package/extensions/dm-lens/dist/clients/log-cleanup.js +0 -138
- package/extensions/dm-lens/dist/clients/lsp/aggregation.js +0 -88
- package/extensions/dm-lens/dist/clients/lsp/cascade-tier.js +0 -203
- package/extensions/dm-lens/dist/clients/lsp/client.js +0 -2709
- package/extensions/dm-lens/dist/clients/lsp/config.js +0 -252
- package/extensions/dm-lens/dist/clients/lsp/content-limits.js +0 -21
- package/extensions/dm-lens/dist/clients/lsp/diagnostic-binding.js +0 -79
- package/extensions/dm-lens/dist/clients/lsp/document-drift.js +0 -312
- package/extensions/dm-lens/dist/clients/lsp/edits.js +0 -930
- package/extensions/dm-lens/dist/clients/lsp/index.js +0 -4448
- package/extensions/dm-lens/dist/clients/lsp/inferred-project.js +0 -121
- package/extensions/dm-lens/dist/clients/lsp/jvm-runtime.js +0 -194
- package/extensions/dm-lens/dist/clients/lsp/language.js +0 -23
- package/extensions/dm-lens/dist/clients/lsp/launch.js +0 -468
- package/extensions/dm-lens/dist/clients/lsp/lombok.js +0 -146
- package/extensions/dm-lens/dist/clients/lsp/path-utils.js +0 -1
- package/extensions/dm-lens/dist/clients/lsp/pending-aux-coverage.js +0 -90
- package/extensions/dm-lens/dist/clients/lsp/position-encoding.js +0 -28
- package/extensions/dm-lens/dist/clients/lsp/ruby-drive-dirs.js +0 -42
- package/extensions/dm-lens/dist/clients/lsp/server.js +0 -2422
- package/extensions/dm-lens/dist/clients/lsp/session-roots.js +0 -52
- package/extensions/dm-lens/dist/clients/lsp/spawn-history.js +0 -15
- package/extensions/dm-lens/dist/clients/lsp/sync-kind.js +0 -30
- package/extensions/dm-lens/dist/clients/lsp/tsserver-sync.js +0 -156
- package/extensions/dm-lens/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
- package/extensions/dm-lens/dist/clients/lsp/wait-policy/classification.js +0 -36
- package/extensions/dm-lens/dist/clients/lsp/wait-policy/index.js +0 -3
- package/extensions/dm-lens/dist/clients/lsp/wait-policy/strategies.js +0 -175
- package/extensions/dm-lens/dist/clients/lsp/watch-queue.js +0 -40
- package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-cache.js +0 -267
- package/extensions/dm-lens/dist/clients/lsp/workspace-diagnostics-session.js +0 -7
- package/extensions/dm-lens/dist/clients/lsp/workspace-sweep-hold.js +0 -93
- package/extensions/dm-lens/dist/clients/lsp-budget.js +0 -83
- package/extensions/dm-lens/dist/clients/lsp-document-symbols.js +0 -93
- package/extensions/dm-lens/dist/clients/lsp-lazy.js +0 -8
- package/extensions/dm-lens/dist/clients/lsp-mutation.js +0 -273
- package/extensions/dm-lens/dist/clients/lsp-status.js +0 -37
- package/extensions/dm-lens/dist/clients/map-with-concurrency.js +0 -20
- package/extensions/dm-lens/dist/clients/mcp/analyze.js +0 -251
- package/extensions/dm-lens/dist/clients/mcp/host-shim.js +0 -14
- package/extensions/dm-lens/dist/clients/mcp/ipc.js +0 -302
- package/extensions/dm-lens/dist/clients/mcp/review.js +0 -137
- package/extensions/dm-lens/dist/clients/mcp/session.js +0 -305
- package/extensions/dm-lens/dist/clients/memory-sampler.js +0 -107
- package/extensions/dm-lens/dist/clients/message-end-attribution.js +0 -18
- package/extensions/dm-lens/dist/clients/metrics-client.js +0 -80
- package/extensions/dm-lens/dist/clients/metrics-history.js +0 -255
- package/extensions/dm-lens/dist/clients/middle-man-analysis.js +0 -161
- package/extensions/dm-lens/dist/clients/model-provider.js +0 -31
- package/extensions/dm-lens/dist/clients/module-report-lsp.js +0 -140
- package/extensions/dm-lens/dist/clients/module-report.js +0 -1642
- package/extensions/dm-lens/dist/clients/mutating-tool.js +0 -377
- package/extensions/dm-lens/dist/clients/mutation-attribution.js +0 -168
- package/extensions/dm-lens/dist/clients/mutation-bridge.js +0 -102
- package/extensions/dm-lens/dist/clients/ndjson-logger.js +0 -471
- package/extensions/dm-lens/dist/clients/observed-mutation-sources.js +0 -43
- package/extensions/dm-lens/dist/clients/observed-mutation.js +0 -709
- package/extensions/dm-lens/dist/clients/oldtext-autopatch.js +0 -100
- package/extensions/dm-lens/dist/clients/opaque-mutation-scan.js +0 -313
- package/extensions/dm-lens/dist/clients/opengrep-client.js +0 -216
- package/extensions/dm-lens/dist/clients/opengrep-config.js +0 -52
- package/extensions/dm-lens/dist/clients/package-manager.js +0 -303
- package/extensions/dm-lens/dist/clients/package-root.js +0 -25
- package/extensions/dm-lens/dist/clients/partial-edit-apply.js +0 -337
- package/extensions/dm-lens/dist/clients/path-attribution-telemetry.js +0 -31
- package/extensions/dm-lens/dist/clients/path-keyed-map.js +0 -57
- package/extensions/dm-lens/dist/clients/path-utils.js +0 -476
- package/extensions/dm-lens/dist/clients/performance-report.js +0 -291
- package/extensions/dm-lens/dist/clients/persist-debounce.js +0 -37
- package/extensions/dm-lens/dist/clients/persistent-reverify.js +0 -165
- package/extensions/dm-lens/dist/clients/php-cs-fixer-config.js +0 -15
- package/extensions/dm-lens/dist/clients/pipeline.js +0 -1091
- package/extensions/dm-lens/dist/clients/post-autofix-notice.js +0 -19
- package/extensions/dm-lens/dist/clients/print-mode.js +0 -3
- package/extensions/dm-lens/dist/clients/probe-home-state.js +0 -62
- package/extensions/dm-lens/dist/clients/process-bridge.js +0 -20
- package/extensions/dm-lens/dist/clients/process-singletons.js +0 -69
- package/extensions/dm-lens/dist/clients/process-snapshot.js +0 -22
- package/extensions/dm-lens/dist/clients/project-changes.js +0 -112
- package/extensions/dm-lens/dist/clients/project-conventions.js +0 -152
- package/extensions/dm-lens/dist/clients/project-diagnostics/cache.js +0 -91
- package/extensions/dm-lens/dist/clients/project-diagnostics/extractors.js +0 -53
- package/extensions/dm-lens/dist/clients/project-diagnostics/fresh-fetch.js +0 -408
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -64
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -38
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -21
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -23
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -28
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -45
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -34
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -32
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -79
- package/extensions/dm-lens/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -41
- package/extensions/dm-lens/dist/clients/project-diagnostics/scanner.js +0 -349
- package/extensions/dm-lens/dist/clients/project-diagnostics/types.js +0 -1
- package/extensions/dm-lens/dist/clients/project-lens-config.js +0 -437
- package/extensions/dm-lens/dist/clients/project-report.js +0 -527
- package/extensions/dm-lens/dist/clients/project-scale.js +0 -60
- package/extensions/dm-lens/dist/clients/project-scan-policy.js +0 -4
- package/extensions/dm-lens/dist/clients/project-snapshot-fingerprint.js +0 -39
- package/extensions/dm-lens/dist/clients/project-snapshot-persist-worker.js +0 -14
- package/extensions/dm-lens/dist/clients/project-snapshot.js +0 -1178
- package/extensions/dm-lens/dist/clients/project-trust.js +0 -104
- package/extensions/dm-lens/dist/clients/python-environment.js +0 -120
- package/extensions/dm-lens/dist/clients/python-provenance.js +0 -551
- package/extensions/dm-lens/dist/clients/quiet-window-config.js +0 -16
- package/extensions/dm-lens/dist/clients/quiet-window.js +0 -126
- package/extensions/dm-lens/dist/clients/read-bridge.js +0 -46
- package/extensions/dm-lens/dist/clients/read-expansion.js +0 -218
- package/extensions/dm-lens/dist/clients/read-guard-logger.js +0 -178
- package/extensions/dm-lens/dist/clients/read-guard-tool-lines.js +0 -1042
- package/extensions/dm-lens/dist/clients/read-guard.js +0 -1096
- package/extensions/dm-lens/dist/clients/recent-touches.js +0 -126
- package/extensions/dm-lens/dist/clients/redact/secrets.js +0 -386
- package/extensions/dm-lens/dist/clients/resource-sampler.js +0 -344
- package/extensions/dm-lens/dist/clients/reverse-deps.js +0 -227
- package/extensions/dm-lens/dist/clients/review-graph/builder.js +0 -3635
- package/extensions/dm-lens/dist/clients/review-graph/format.js +0 -30
- package/extensions/dm-lens/dist/clients/review-graph/git-identity.js +0 -115
- package/extensions/dm-lens/dist/clients/review-graph/import-resolvers.js +0 -325
- package/extensions/dm-lens/dist/clients/review-graph/persist-worker.js +0 -8
- package/extensions/dm-lens/dist/clients/review-graph/query.js +0 -195
- package/extensions/dm-lens/dist/clients/review-graph/revision-drift.js +0 -3
- package/extensions/dm-lens/dist/clients/review-graph/service.js +0 -60
- package/extensions/dm-lens/dist/clients/review-graph/shared-extraction-ir.js +0 -54
- package/extensions/dm-lens/dist/clients/review-graph/symbol-id.js +0 -29
- package/extensions/dm-lens/dist/clients/review-graph/tsconfig-paths.js +0 -253
- package/extensions/dm-lens/dist/clients/review-graph/types.js +0 -1
- package/extensions/dm-lens/dist/clients/review-graph/workspace-modules.js +0 -374
- package/extensions/dm-lens/dist/clients/review-graph-logger.js +0 -51
- package/extensions/dm-lens/dist/clients/ruff-client.js +0 -109
- package/extensions/dm-lens/dist/clients/rules-scanner.js +0 -113
- package/extensions/dm-lens/dist/clients/run-duration.js +0 -9
- package/extensions/dm-lens/dist/clients/runtime-agent-end.js +0 -608
- package/extensions/dm-lens/dist/clients/runtime-config.js +0 -39
- package/extensions/dm-lens/dist/clients/runtime-context.js +0 -155
- package/extensions/dm-lens/dist/clients/runtime-coordinator.js +0 -817
- package/extensions/dm-lens/dist/clients/runtime-session.js +0 -1625
- package/extensions/dm-lens/dist/clients/runtime-tool-call.js +0 -868
- package/extensions/dm-lens/dist/clients/runtime-tool-result.js +0 -1317
- package/extensions/dm-lens/dist/clients/runtime-turn.js +0 -2333
- package/extensions/dm-lens/dist/clients/rust-client.js +0 -48
- package/extensions/dm-lens/dist/clients/safe-spawn.js +0 -1267
- package/extensions/dm-lens/dist/clients/sanitize.js +0 -174
- package/extensions/dm-lens/dist/clients/scan-utils.js +0 -7
- package/extensions/dm-lens/dist/clients/scratch-tree-policy.js +0 -57
- package/extensions/dm-lens/dist/clients/search-read-registration.js +0 -59
- package/extensions/dm-lens/dist/clients/secret-findings.js +0 -69
- package/extensions/dm-lens/dist/clients/security-scan-client.js +0 -208
- package/extensions/dm-lens/dist/clients/session-event-guard.js +0 -111
- package/extensions/dm-lens/dist/clients/session-lifecycle.js +0 -173
- package/extensions/dm-lens/dist/clients/session-start-observability.js +0 -47
- package/extensions/dm-lens/dist/clients/session-state-store.js +0 -63
- package/extensions/dm-lens/dist/clients/session-summary.js +0 -8
- package/extensions/dm-lens/dist/clients/sessionstart-logger.js +0 -22
- package/extensions/dm-lens/dist/clients/sg-runner.js +0 -627
- package/extensions/dm-lens/dist/clients/sgconfig.js +0 -280
- package/extensions/dm-lens/dist/clients/shared-checkout-guard.js +0 -177
- package/extensions/dm-lens/dist/clients/single-flight.js +0 -52
- package/extensions/dm-lens/dist/clients/situational-tool-telemetry.js +0 -85
- package/extensions/dm-lens/dist/clients/skills-resolver.js +0 -38
- package/extensions/dm-lens/dist/clients/slow-fs.js +0 -77
- package/extensions/dm-lens/dist/clients/smells-rollup.js +0 -118
- package/extensions/dm-lens/dist/clients/source-filter.js +0 -320
- package/extensions/dm-lens/dist/clients/source-walker.js +0 -96
- package/extensions/dm-lens/dist/clients/spawn-output-cap.js +0 -35
- package/extensions/dm-lens/dist/clients/spawn-timeout-cooldown.js +0 -51
- package/extensions/dm-lens/dist/clients/stale-marker.js +0 -1
- package/extensions/dm-lens/dist/clients/startup-scan.js +0 -222
- package/extensions/dm-lens/dist/clients/startup-timing.js +0 -67
- package/extensions/dm-lens/dist/clients/string-utils.js +0 -6
- package/extensions/dm-lens/dist/clients/subagent-mode.js +0 -40
- package/extensions/dm-lens/dist/clients/symbol-containment.js +0 -16
- package/extensions/dm-lens/dist/clients/symbol-types.js +0 -1
- package/extensions/dm-lens/dist/clients/test-runner-client.js +0 -1504
- package/extensions/dm-lens/dist/clients/test-runner-delivery.js +0 -260
- package/extensions/dm-lens/dist/clients/todo-scanner.js +0 -164
- package/extensions/dm-lens/dist/clients/tool-agreement.js +0 -377
- package/extensions/dm-lens/dist/clients/tool-config.js +0 -218
- package/extensions/dm-lens/dist/clients/tool-cwd.js +0 -235
- package/extensions/dm-lens/dist/clients/tool-definition.js +0 -22
- package/extensions/dm-lens/dist/clients/tool-event.js +0 -18
- package/extensions/dm-lens/dist/clients/tool-policy.js +0 -2393
- package/extensions/dm-lens/dist/clients/tool-probe.js +0 -5
- package/extensions/dm-lens/dist/clients/tool-render.js +0 -79
- package/extensions/dm-lens/dist/clients/tool-set-policy.js +0 -70
- package/extensions/dm-lens/dist/clients/tree-sitter-cache.js +0 -206
- package/extensions/dm-lens/dist/clients/tree-sitter-client.js +0 -2787
- package/extensions/dm-lens/dist/clients/tree-sitter-logger.js +0 -77
- package/extensions/dm-lens/dist/clients/tree-sitter-navigator.js +0 -198
- package/extensions/dm-lens/dist/clients/tree-sitter-query-loader.js +0 -318
- package/extensions/dm-lens/dist/clients/tree-sitter-shared.js +0 -87
- package/extensions/dm-lens/dist/clients/tree-sitter-symbol-extractor.js +0 -957
- package/extensions/dm-lens/dist/clients/trivy-client.js +0 -305
- package/extensions/dm-lens/dist/clients/tui-fit.js +0 -26
- package/extensions/dm-lens/dist/clients/turn-context.js +0 -48
- package/extensions/dm-lens/dist/clients/turn-end/lane.js +0 -1
- package/extensions/dm-lens/dist/clients/turn-end/lanes/govulncheck.js +0 -54
- package/extensions/dm-lens/dist/clients/turn-end/lanes/secrets.js +0 -131
- package/extensions/dm-lens/dist/clients/turn-summary-render.js +0 -56
- package/extensions/dm-lens/dist/clients/turn-summary.js +0 -84
- package/extensions/dm-lens/dist/clients/typos-config.js +0 -9
- package/extensions/dm-lens/dist/clients/user-notify.js +0 -15
- package/extensions/dm-lens/dist/clients/vanished-instance-marker.js +0 -18
- package/extensions/dm-lens/dist/clients/warm-attach.js +0 -210
- package/extensions/dm-lens/dist/clients/widget-state.js +0 -963
- package/extensions/dm-lens/dist/clients/word-index-logger.js +0 -21
- package/extensions/dm-lens/dist/clients/word-index-store.js +0 -222
- package/extensions/dm-lens/dist/clients/word-index.js +0 -1426
- package/extensions/dm-lens/dist/clients/workspace-topology.js +0 -261
- package/extensions/dm-lens/dist/clients/write-ordering-guard.js +0 -24
- package/extensions/dm-lens/dist/clients/zizmor-config.js +0 -160
- package/extensions/dm-lens/dist/i18n.js +0 -67
- package/extensions/dm-lens/dist/index.js +0 -1782
- package/extensions/dm-lens/dist/mcp/analyze-cli.js +0 -178
- package/extensions/dm-lens/dist/mcp/build-staleness.js +0 -55
- package/extensions/dm-lens/dist/mcp/cli.js +0 -78
- package/extensions/dm-lens/dist/mcp/server.js +0 -1208
- package/extensions/dm-lens/dist/mcp/worker.js +0 -33
- package/extensions/dm-lens/dist/scripts/lib/process-scan.mjs +0 -256
- package/extensions/dm-lens/dist/scripts/lib/skills-predicate.mjs +0 -53
- package/extensions/dm-lens/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -45
- package/extensions/dm-lens/dist/tools/activate-tools.js +0 -61
- package/extensions/dm-lens/dist/tools/ast-grep-outline.js +0 -127
- package/extensions/dm-lens/dist/tools/ast-grep-replace.js +0 -226
- package/extensions/dm-lens/dist/tools/ast-grep-search.js +0 -687
- package/extensions/dm-lens/dist/tools/effective-config.js +0 -59
- package/extensions/dm-lens/dist/tools/lens-diagnostic-mark.js +0 -240
- package/extensions/dm-lens/dist/tools/lens-diagnostics.js +0 -1484
- package/extensions/dm-lens/dist/tools/lsp-diagnostics.js +0 -1088
- package/extensions/dm-lens/dist/tools/lsp-navigation.js +0 -1194
- package/extensions/dm-lens/dist/tools/lsp-structured-output.js +0 -277
- package/extensions/dm-lens/dist/tools/module-report.js +0 -332
- package/extensions/dm-lens/dist/tools/project-report.js +0 -72
- package/extensions/dm-lens/dist/tools/render-compact.js +0 -272
- package/extensions/dm-lens/dist/tools/scan-progress.js +0 -31
- package/extensions/dm-lens/dist/tools/shared.js +0 -26
- package/extensions/dm-lens/dist/tools/symbol-search.js +0 -133
- package/extensions/dm-mcp-adapter/dist/abort.js +0 -35
- package/extensions/dm-mcp-adapter/dist/agent-dir.js +0 -45
- package/extensions/dm-mcp-adapter/dist/agent-plugin-loader.js +0 -392
- package/extensions/dm-mcp-adapter/dist/agent-plugin-provenance.js +0 -26
- package/extensions/dm-mcp-adapter/dist/bearer-command-resolver.js +0 -182
- package/extensions/dm-mcp-adapter/dist/claude-plugin-loader.js +0 -350
- package/extensions/dm-mcp-adapter/dist/config.js +0 -1310
- package/extensions/dm-mcp-adapter/dist/consent-manager.js +0 -84
- package/extensions/dm-mcp-adapter/dist/elicitation-handler.js +0 -308
- package/extensions/dm-mcp-adapter/dist/errors.js +0 -225
- package/extensions/dm-mcp-adapter/dist/http-ca.js +0 -71
- package/extensions/dm-mcp-adapter/dist/jev-client.js +0 -359
- package/extensions/dm-mcp-adapter/dist/jev-contracts.js +0 -1
- package/extensions/dm-mcp-adapter/dist/jev-key-store.js +0 -176
- package/extensions/dm-mcp-adapter/dist/json-schema-validator.js +0 -70
- package/extensions/dm-mcp-adapter/dist/lifecycle.js +0 -387
- package/extensions/dm-mcp-adapter/dist/logger.js +0 -115
- package/extensions/dm-mcp-adapter/dist/mcp-auth-fetch.js +0 -86
- package/extensions/dm-mcp-adapter/dist/mcp-auth-flow.js +0 -949
- package/extensions/dm-mcp-adapter/dist/mcp-auth.js +0 -997
- package/extensions/dm-mcp-adapter/dist/mcp-bearer-store.js +0 -182
- package/extensions/dm-mcp-adapter/dist/mcp-callback-server.js +0 -381
- package/extensions/dm-mcp-adapter/dist/mcp-oauth-provider.js +0 -524
- package/extensions/dm-mcp-adapter/dist/mcp-probe.js +0 -151
- package/extensions/dm-mcp-adapter/dist/mcp-tasks.js +0 -300
- package/extensions/dm-mcp-adapter/dist/mcp-trace.js +0 -215
- package/extensions/dm-mcp-adapter/dist/metadata-cache.js +0 -267
- package/extensions/dm-mcp-adapter/dist/npx-resolver.js +0 -465
- package/extensions/dm-mcp-adapter/dist/package-mcp-loader.js +0 -135
- package/extensions/dm-mcp-adapter/dist/request-headers-command.js +0 -294
- package/extensions/dm-mcp-adapter/dist/resource-tools.js +0 -7
- package/extensions/dm-mcp-adapter/dist/runtime-owner.js +0 -94
- package/extensions/dm-mcp-adapter/dist/sampling-handler.js +0 -213
- package/extensions/dm-mcp-adapter/dist/secure-keyring.js +0 -191
- package/extensions/dm-mcp-adapter/dist/server-manager.js +0 -1514
- package/extensions/dm-mcp-adapter/dist/session-approvals.js +0 -127
- package/extensions/dm-mcp-adapter/dist/session-recovery.js +0 -82
- package/extensions/dm-mcp-adapter/dist/state.js +0 -1
- package/extensions/dm-mcp-adapter/dist/types.js +0 -286
- package/extensions/dm-mcp-adapter/dist/ui-app-bridge-helpers.js +0 -36
- package/extensions/dm-mcp-adapter/dist/ui-resource-handler.js +0 -204
- package/extensions/dm-mcp-adapter/dist/ui-stream-types.js +0 -58
- package/extensions/dm-mcp-adapter/dist/ui-tool-visibility.js +0 -26
- package/extensions/dm-mcp-adapter/dist/unix-socket-transport.js +0 -83
- package/extensions/dm-mcp-adapter/dist/utils.js +0 -382
|
@@ -1,2709 +0,0 @@
|
|
|
1
|
-
import { spawn as nodeSpawn } from "node:child_process";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
|
-
import { access, lstat, readFile } from "node:fs/promises";
|
|
4
|
-
import * as os from "node:os";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { emitBounded } from "../bounded-telemetry.js";
|
|
7
|
-
import { withTimeout } from "../deadline-utils.js";
|
|
8
|
-
import { minimatch } from "../deps/minimatch.js";
|
|
9
|
-
import { mapWithConcurrency } from "../map-with-concurrency.js";
|
|
10
|
-
import { incrementDegradationCount, recordDegradationOnce } from "../degradation-ledger.js";
|
|
11
|
-
import { CancellationTokenSource, createMessageConnection, StreamMessageReader, StreamMessageWriter } from "../deps/vscode-jsonrpc.js";
|
|
12
|
-
import { logExtension } from "../extension-log.js";
|
|
13
|
-
import { recordLspChild, removeLspChild } from "../instance-registry.js";
|
|
14
|
-
import { workspaceDiagnosticsCacheSessionStart } from "./workspace-diagnostics-session.js";
|
|
15
|
-
import { logLatency } from "../latency-logger.js";
|
|
16
|
-
import { newLspMutationCorrelationId } from "../lsp-mutation.js";
|
|
17
|
-
import { getProcessSingleton } from "../process-singletons.js";
|
|
18
|
-
import { getAmbientAbortSignal, isOwnLiveChild, releaseOwnChildPid } from "../safe-spawn.js";
|
|
19
|
-
import { raceToCompletion } from "./aggregation.js";
|
|
20
|
-
import { hashDiagnosticContent } from "./diagnostic-binding.js";
|
|
21
|
-
import { applyWorkspaceEdit, normalizeWorkspaceEditToUtf16 } from "./edits.js";
|
|
22
|
-
import { normalizeMapKey, uriToPath } from "./path-utils.js";
|
|
23
|
-
import { ADVERTISED_POSITION_ENCODINGS, convertCharacterOffset, lineTextAt, negotiatePositionEncoding } from "./position-encoding.js";
|
|
24
|
-
import { negotiateSaveOptions, negotiateSyncKind, TEXT_DOCUMENT_SYNC_KIND_FULL, TEXT_DOCUMENT_SYNC_KIND_INCREMENTAL } from "./sync-kind.js";
|
|
25
|
-
import { exceedsLspSyncLimits } from "./content-limits.js";
|
|
26
|
-
import { probeTsserverProjectIdentity } from "./tsserver-sync.js";
|
|
27
|
-
import { getStrategy } from "./wait-policy/index.js";
|
|
28
|
-
import { WatchedFilesQueue } from "./watch-queue.js";
|
|
29
|
-
import { clearAllWorkspaceDiagnosticsCaches, clearWorkspaceDiagnosticsCacheAtAndAbove } from "./workspace-diagnostics-cache.js";
|
|
30
|
-
const PUB_DEBUG = Boolean(process.env.PILENS_PUB_DEBUG);
|
|
31
|
-
function extractSpawnMarker(args) {
|
|
32
|
-
if (!args)
|
|
33
|
-
return;
|
|
34
|
-
const tmpDir = os.tmpdir();
|
|
35
|
-
for (let i = 0;i < args.length - 1; i++) {
|
|
36
|
-
const flag = args[i];
|
|
37
|
-
if (flag === "--config" || flag === "-c") {
|
|
38
|
-
const value = args[i + 1];
|
|
39
|
-
if (value?.startsWith(tmpDir))
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
export const INITIALIZE_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_INIT_TIMEOUT_MS", 15000);
|
|
46
|
-
export const CLIENT_CAPABILITIES = {
|
|
47
|
-
general: { positionEncodings: ADVERTISED_POSITION_ENCODINGS },
|
|
48
|
-
window: {},
|
|
49
|
-
workspace: {
|
|
50
|
-
workspaceFolders: true,
|
|
51
|
-
configuration: true,
|
|
52
|
-
didChangeWatchedFiles: { dynamicRegistration: true },
|
|
53
|
-
fileOperations: {
|
|
54
|
-
dynamicRegistration: false,
|
|
55
|
-
willRename: true,
|
|
56
|
-
didRename: true
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
textDocument: {
|
|
60
|
-
synchronization: {
|
|
61
|
-
dynamicRegistration: false,
|
|
62
|
-
willSave: false,
|
|
63
|
-
willSaveWaitUntil: false,
|
|
64
|
-
didSave: true
|
|
65
|
-
},
|
|
66
|
-
completion: {
|
|
67
|
-
dynamicRegistration: false,
|
|
68
|
-
completionItem: { snippetSupport: false }
|
|
69
|
-
},
|
|
70
|
-
hover: { dynamicRegistration: false },
|
|
71
|
-
signatureHelp: { dynamicRegistration: false },
|
|
72
|
-
definition: { dynamicRegistration: false },
|
|
73
|
-
typeDefinition: { dynamicRegistration: false },
|
|
74
|
-
implementation: { dynamicRegistration: false },
|
|
75
|
-
references: { dynamicRegistration: false },
|
|
76
|
-
documentSymbol: { dynamicRegistration: false },
|
|
77
|
-
codeAction: {
|
|
78
|
-
dynamicRegistration: false,
|
|
79
|
-
dataSupport: true,
|
|
80
|
-
resolveSupport: { properties: ["edit", "command"] },
|
|
81
|
-
codeActionLiteralSupport: {
|
|
82
|
-
codeActionKind: {
|
|
83
|
-
valueSet: [
|
|
84
|
-
"quickfix",
|
|
85
|
-
"refactor",
|
|
86
|
-
"refactor.extract",
|
|
87
|
-
"refactor.inline",
|
|
88
|
-
"refactor.rewrite",
|
|
89
|
-
"source",
|
|
90
|
-
"source.organizeImports"
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
rename: { dynamicRegistration: false },
|
|
96
|
-
publishDiagnostics: {
|
|
97
|
-
relatedInformation: true,
|
|
98
|
-
versionSupport: true
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
const NAV_REQUEST_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_NAV_REQUEST_TIMEOUT_MS", 1e4);
|
|
103
|
-
const DIAGNOSTICS_WAIT_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_DIAGNOSTICS_WAIT_MS", 1e4);
|
|
104
|
-
const PULL_DIAGNOSTICS_RETRY_INTERVAL_MS = positiveIntFromEnv("DM_LENS_LSP_PULL_RETRY_INTERVAL_MS", 250);
|
|
105
|
-
const PULL_REQUEST_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_PULL_REQUEST_TIMEOUT_MS", 1e4);
|
|
106
|
-
const PULL_MIN_USABLE_BUDGET_MS = 5;
|
|
107
|
-
const SHUTDOWN_REQUEST_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_SHUTDOWN_TIMEOUT_MS", 1000);
|
|
108
|
-
const EXIT_NOTIFY_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_EXIT_NOTIFY_TIMEOUT_MS", 1000);
|
|
109
|
-
const LIVENESS_PING_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_LIVENESS_PING_TIMEOUT_MS", 300);
|
|
110
|
-
const LIVENESS_PING_QUERY = "__dm_lens_liveness_ping__";
|
|
111
|
-
const WORKSPACE_PULL_RESULT_CACHE_MAX = 4096;
|
|
112
|
-
const WORKSPACE_PULL_SCOPE = "*workspace*";
|
|
113
|
-
const REFRESH_REPULL_CONCURRENCY = 4;
|
|
114
|
-
const EXECUTE_COMMAND_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_EXECUTE_COMMAND_TIMEOUT_MS", 30000);
|
|
115
|
-
const PROBE_COMMAND_TIMEOUT_MS = positiveIntFromEnv("DM_LENS_LSP_PROJECT_IDENTITY_PROBE_TIMEOUT_MS", 2500);
|
|
116
|
-
const LSP_CRASH_CODES = new Set([
|
|
117
|
-
"ERR_STREAM_DESTROYED",
|
|
118
|
-
"ERR_STREAM_WRITE_AFTER_END",
|
|
119
|
-
"EPIPE",
|
|
120
|
-
"ECONNRESET"
|
|
121
|
-
]);
|
|
122
|
-
let crashGuardInstalled = false;
|
|
123
|
-
function isIgnorableLspRuntimeCrash(err) {
|
|
124
|
-
if (!(err instanceof Error))
|
|
125
|
-
return false;
|
|
126
|
-
const code = err.code;
|
|
127
|
-
if (code && LSP_CRASH_CODES.has(code))
|
|
128
|
-
return true;
|
|
129
|
-
const msg = err.message.toLowerCase();
|
|
130
|
-
const stack = (err.stack ?? "").toLowerCase();
|
|
131
|
-
return msg.includes("stream") || msg.includes("write after end") || stack.includes("vscode-jsonrpc/lib/node/ril.js");
|
|
132
|
-
}
|
|
133
|
-
function installCrashGuard() {
|
|
134
|
-
if (crashGuardInstalled)
|
|
135
|
-
return;
|
|
136
|
-
crashGuardInstalled = true;
|
|
137
|
-
process.on("uncaughtException", (err) => {
|
|
138
|
-
if (isIgnorableLspRuntimeCrash(err)) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
throw err;
|
|
142
|
-
});
|
|
143
|
-
process.on("unhandledRejection", (reason) => {
|
|
144
|
-
if (isIgnorableLspRuntimeCrash(reason)) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
throw reason instanceof Error ? reason : new Error(String(reason));
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
export const MAX_INCREMENTAL_TEXT_RETAINED_ENTRIES = 128;
|
|
151
|
-
export const MAX_INCREMENTAL_TEXT_RETAINED_BYTES = 64 * 1024 * 1024;
|
|
152
|
-
const ACTIVE_CLIENTS_FAMILY = "lsp-client.active-clients";
|
|
153
|
-
const ACTIVE_CLIENTS_VERSION = 1;
|
|
154
|
-
function activeLspClientSet() {
|
|
155
|
-
return getProcessSingleton(ACTIVE_CLIENTS_FAMILY, ACTIVE_CLIENTS_VERSION, () => new Set);
|
|
156
|
-
}
|
|
157
|
-
export function getLspDocumentTextRetentionSnapshot() {
|
|
158
|
-
let entries = 0;
|
|
159
|
-
let bytes = 0;
|
|
160
|
-
const roots = new Set;
|
|
161
|
-
const active = activeLspClientSet();
|
|
162
|
-
for (const state of active) {
|
|
163
|
-
entries += state.incrementalTextRetainedEntries ?? 0;
|
|
164
|
-
bytes += state.incrementalTextRetainedBytes ?? 0;
|
|
165
|
-
if (typeof state.root === "string" && state.root.length > 0) {
|
|
166
|
-
roots.add(state.root);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return { clients: active.size, entries, bytes, roots: roots.size };
|
|
170
|
-
}
|
|
171
|
-
function isClientAlive(state) {
|
|
172
|
-
return state.isConnected && !state.isDestroyed && !state.lspProcess.process.killed;
|
|
173
|
-
}
|
|
174
|
-
function disposeClientConnection(state) {
|
|
175
|
-
if (state.connectionDisposed)
|
|
176
|
-
return;
|
|
177
|
-
state.connectionDisposed = true;
|
|
178
|
-
activeLspClientSet().delete(state);
|
|
179
|
-
try {
|
|
180
|
-
state.connection.dispose();
|
|
181
|
-
} catch {}
|
|
182
|
-
}
|
|
183
|
-
export async function killProcessTree(proc, pid, options = {}) {
|
|
184
|
-
if ((proc.exitCode != null || proc.signalCode != null) && !options.processExiting) {
|
|
185
|
-
releaseOwnChildPid(pid);
|
|
186
|
-
proc.unref?.();
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (process.platform === "win32" && pid > 0) {
|
|
190
|
-
if (options.processExiting) {
|
|
191
|
-
try {
|
|
192
|
-
proc.kill();
|
|
193
|
-
} catch {}
|
|
194
|
-
proc.unref?.();
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
try {
|
|
198
|
-
const taskkill = `${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\taskkill.exe`;
|
|
199
|
-
const killer = nodeSpawn(taskkill, ["/F", "/T", "/PID", String(pid)], {
|
|
200
|
-
shell: false,
|
|
201
|
-
windowsHide: true,
|
|
202
|
-
stdio: "ignore",
|
|
203
|
-
detached: !!options.fast
|
|
204
|
-
});
|
|
205
|
-
if (options.fast) {
|
|
206
|
-
killer.unref();
|
|
207
|
-
proc.unref?.();
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
await new Promise((resolve) => {
|
|
211
|
-
killer.once("close", () => resolve());
|
|
212
|
-
killer.once("error", (err) => {
|
|
213
|
-
logLatency({
|
|
214
|
-
type: "phase",
|
|
215
|
-
phase: "lsp_kill_escalation",
|
|
216
|
-
filePath: "",
|
|
217
|
-
durationMs: 0,
|
|
218
|
-
metadata: { pid, platform: "win32", taskkillError: String(err) }
|
|
219
|
-
});
|
|
220
|
-
resolve();
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
} catch (err) {
|
|
224
|
-
logLatency({
|
|
225
|
-
type: "phase",
|
|
226
|
-
phase: "lsp_kill_escalation",
|
|
227
|
-
filePath: "",
|
|
228
|
-
durationMs: 0,
|
|
229
|
-
metadata: { pid, platform: "win32", taskkillSpawnError: String(err) }
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
const killPosixProcessGroup = (signal) => {
|
|
235
|
-
if (!isOwnLiveChild(pid, "lsp-stop-posix-group"))
|
|
236
|
-
return false;
|
|
237
|
-
try {
|
|
238
|
-
process.kill(-pid, signal);
|
|
239
|
-
return true;
|
|
240
|
-
} catch {
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
const killDirectChild = (signal) => {
|
|
245
|
-
try {
|
|
246
|
-
proc.kill(signal);
|
|
247
|
-
} catch {}
|
|
248
|
-
};
|
|
249
|
-
try {
|
|
250
|
-
let exited = proc.exitCode != null || proc.signalCode != null;
|
|
251
|
-
proc.once?.("exit", () => {
|
|
252
|
-
exited = true;
|
|
253
|
-
});
|
|
254
|
-
if (!killPosixProcessGroup("SIGTERM")) {
|
|
255
|
-
killDirectChild("SIGTERM");
|
|
256
|
-
}
|
|
257
|
-
if (options.fast) {
|
|
258
|
-
const timer = setTimeout(() => {
|
|
259
|
-
if (!exited) {
|
|
260
|
-
logLatency({
|
|
261
|
-
type: "phase",
|
|
262
|
-
phase: "lsp_kill_escalation",
|
|
263
|
-
filePath: "",
|
|
264
|
-
durationMs: 1500,
|
|
265
|
-
metadata: { pid, platform: "posix", method: "SIGKILL", fast: true }
|
|
266
|
-
});
|
|
267
|
-
if (!killPosixProcessGroup("SIGKILL")) {
|
|
268
|
-
killDirectChild("SIGKILL");
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
releaseOwnChildPid(pid);
|
|
272
|
-
}, 1500);
|
|
273
|
-
timer.unref?.();
|
|
274
|
-
proc.unref?.();
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
const exitedInTime = await new Promise((resolve) => {
|
|
278
|
-
if (proc.exitCode != null || proc.signalCode != null) {
|
|
279
|
-
resolve(true);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const onExit = () => {
|
|
283
|
-
clearTimeout(timer);
|
|
284
|
-
resolve(true);
|
|
285
|
-
};
|
|
286
|
-
const timer = setTimeout(() => {
|
|
287
|
-
proc.off?.("exit", onExit);
|
|
288
|
-
resolve(false);
|
|
289
|
-
}, 1500);
|
|
290
|
-
proc.once?.("exit", onExit);
|
|
291
|
-
});
|
|
292
|
-
if (!exitedInTime && !exited) {
|
|
293
|
-
logLatency({
|
|
294
|
-
type: "phase",
|
|
295
|
-
phase: "lsp_kill_escalation",
|
|
296
|
-
filePath: "",
|
|
297
|
-
durationMs: 1500,
|
|
298
|
-
metadata: { pid, platform: "posix", method: "SIGKILL", fast: false }
|
|
299
|
-
});
|
|
300
|
-
if (!killPosixProcessGroup("SIGKILL")) {
|
|
301
|
-
killDirectChild("SIGKILL");
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
releaseOwnChildPid(pid);
|
|
305
|
-
} catch {}
|
|
306
|
-
}
|
|
307
|
-
export function stripDiagnosticNoiseLines(message) {
|
|
308
|
-
const cleaned = message.split(/\r?\n/).filter((line) => {
|
|
309
|
-
const trimmed = line.trim();
|
|
310
|
-
if (/^for further information visit\b/i.test(trimmed))
|
|
311
|
-
return false;
|
|
312
|
-
if (/^https?:\/\/\S+$/i.test(trimmed))
|
|
313
|
-
return false;
|
|
314
|
-
return true;
|
|
315
|
-
}).join(`
|
|
316
|
-
`).trim();
|
|
317
|
-
return cleaned || message.trim() || message;
|
|
318
|
-
}
|
|
319
|
-
function normalizeLspDiagnostic(diagnostic) {
|
|
320
|
-
const message = stripDiagnosticNoiseLines(diagnostic.message);
|
|
321
|
-
return message === diagnostic.message ? diagnostic : { ...diagnostic, message };
|
|
322
|
-
}
|
|
323
|
-
function normalizeLspDiagnostics(diagnostics) {
|
|
324
|
-
return diagnostics.map(normalizeLspDiagnostic);
|
|
325
|
-
}
|
|
326
|
-
function mergeDiagnosticLists(...lists) {
|
|
327
|
-
const merged = [];
|
|
328
|
-
const seen = new Set;
|
|
329
|
-
for (const diagnostic of lists.flatMap((list) => list ?? [])) {
|
|
330
|
-
const key = [
|
|
331
|
-
diagnostic.range.start.line,
|
|
332
|
-
diagnostic.range.start.character,
|
|
333
|
-
diagnostic.range.end.line,
|
|
334
|
-
diagnostic.range.end.character,
|
|
335
|
-
diagnostic.code ?? "",
|
|
336
|
-
diagnostic.source ?? "",
|
|
337
|
-
diagnostic.message
|
|
338
|
-
].join(":");
|
|
339
|
-
if (seen.has(key))
|
|
340
|
-
continue;
|
|
341
|
-
seen.add(key);
|
|
342
|
-
merged.push(diagnostic);
|
|
343
|
-
}
|
|
344
|
-
return merged;
|
|
345
|
-
}
|
|
346
|
-
function getMergedDiagnosticsForPath(state, normalizedPath) {
|
|
347
|
-
const legacy = state;
|
|
348
|
-
return mergeDiagnosticLists(state.pushDiagnostics?.get(normalizedPath) ?? legacy.diagnostics?.get(normalizedPath), state.documentPullDiagnostics?.get(normalizedPath));
|
|
349
|
-
}
|
|
350
|
-
const PULL_SOURCE_KEY_SEPARATOR = "\x00";
|
|
351
|
-
const BARE_PULL_SOURCE = "";
|
|
352
|
-
function pullSourceKey(normalizedPath, identifier) {
|
|
353
|
-
return identifier === undefined ? normalizedPath : `${normalizedPath}${PULL_SOURCE_KEY_SEPARATOR}${identifier}`;
|
|
354
|
-
}
|
|
355
|
-
function pullSourceKeysForPath(state, normalizedPath) {
|
|
356
|
-
const prefix = `${normalizedPath}${PULL_SOURCE_KEY_SEPARATOR}`;
|
|
357
|
-
return [
|
|
358
|
-
normalizedPath,
|
|
359
|
-
...[...state.pullResultIds?.keys() ?? []].filter((key) => key.startsWith(prefix))
|
|
360
|
-
];
|
|
361
|
-
}
|
|
362
|
-
function pullSourcesFor(state, normalizedPath) {
|
|
363
|
-
if (!state.documentPullDiagnosticsBySource) {
|
|
364
|
-
state.documentPullDiagnosticsBySource = new Map;
|
|
365
|
-
}
|
|
366
|
-
let sources = state.documentPullDiagnosticsBySource.get(normalizedPath);
|
|
367
|
-
if (!sources) {
|
|
368
|
-
sources = new Map;
|
|
369
|
-
state.documentPullDiagnosticsBySource.set(normalizedPath, sources);
|
|
370
|
-
}
|
|
371
|
-
return sources;
|
|
372
|
-
}
|
|
373
|
-
function pullSourceDiagnostics(state, normalizedPath, identifier) {
|
|
374
|
-
const sources = state.documentPullDiagnosticsBySource?.get(normalizedPath);
|
|
375
|
-
const stored = sources?.get(identifier ?? BARE_PULL_SOURCE);
|
|
376
|
-
if (stored)
|
|
377
|
-
return stored;
|
|
378
|
-
return sources && sources.size > 0 ? [] : state.documentPullDiagnostics.get(normalizedPath) ?? [];
|
|
379
|
-
}
|
|
380
|
-
function storePullSourceDiagnostics(state, normalizedPath, identifier, diagnostics) {
|
|
381
|
-
const sources = pullSourcesFor(state, normalizedPath);
|
|
382
|
-
sources.set(identifier ?? BARE_PULL_SOURCE, diagnostics);
|
|
383
|
-
state.documentPullDiagnostics.set(normalizedPath, mergeDiagnosticLists(...sources.values()));
|
|
384
|
-
}
|
|
385
|
-
function pullGenerationFor(state, normalizedPath) {
|
|
386
|
-
return state.pullGenerations?.get(normalizedPath) ?? 0;
|
|
387
|
-
}
|
|
388
|
-
function bumpPullGeneration(state, normalizedPath) {
|
|
389
|
-
if (!state.pullGenerations)
|
|
390
|
-
state.pullGenerations = new Map;
|
|
391
|
-
state.pullGenerations.set(normalizedPath, (state.pullGenerations.get(normalizedPath) ?? 0) + 1);
|
|
392
|
-
}
|
|
393
|
-
function claimPullRequestSequence(state, sourceKey) {
|
|
394
|
-
if (!state.pullRequestSequences)
|
|
395
|
-
state.pullRequestSequences = new Map;
|
|
396
|
-
const next = (state.pullRequestSequences.get(sourceKey) ?? 0) + 1;
|
|
397
|
-
state.pullRequestSequences.set(sourceKey, next);
|
|
398
|
-
return next;
|
|
399
|
-
}
|
|
400
|
-
function isNewestPullRequest(state, sourceKey, sequence) {
|
|
401
|
-
return (state.pullRequestSequences?.get(sourceKey) ?? 0) === sequence;
|
|
402
|
-
}
|
|
403
|
-
function retirePullSource(state, identifier) {
|
|
404
|
-
const suffix = `${PULL_SOURCE_KEY_SEPARATOR}${identifier}`;
|
|
405
|
-
for (const source of [state.pullResultIds, state.pullRequestSequences]) {
|
|
406
|
-
if (!source)
|
|
407
|
-
continue;
|
|
408
|
-
for (const key of source.keys()) {
|
|
409
|
-
if (key.endsWith(suffix))
|
|
410
|
-
source.delete(key);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
for (const [normalizedPath, sources] of state.documentPullDiagnosticsBySource ?? []) {
|
|
414
|
-
if (!sources.delete(identifier))
|
|
415
|
-
continue;
|
|
416
|
-
state.documentPullDiagnostics.set(normalizedPath, mergeDiagnosticLists(...sources.values()));
|
|
417
|
-
bumpDiagnosticsVersion(state, normalizedPath);
|
|
418
|
-
state.diagnosticEmitter.emit("diagnostics", normalizedPath);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
export function pullDiagnosticSources(state) {
|
|
422
|
-
const identifiers = new Set;
|
|
423
|
-
for (const registration of state.dynamicRegistrations?.values() ?? []) {
|
|
424
|
-
if (registration.method !== "textDocument/diagnostic")
|
|
425
|
-
continue;
|
|
426
|
-
if (registration.identifier)
|
|
427
|
-
identifiers.add(registration.identifier);
|
|
428
|
-
}
|
|
429
|
-
return [undefined, ...[...identifiers].sort((a, b) => a.localeCompare(b))];
|
|
430
|
-
}
|
|
431
|
-
export function bumpDiagnosticsVersion(state, normalizedPath) {
|
|
432
|
-
state.diagnosticsVersion += 1;
|
|
433
|
-
state.diagnosticsVersionsByPath?.set(normalizedPath, state.diagnosticsVersion);
|
|
434
|
-
}
|
|
435
|
-
export function diagnosticsVersionForPath(state, normalizedPath) {
|
|
436
|
-
return state.diagnosticsVersionsByPath?.get(normalizedPath) ?? 0;
|
|
437
|
-
}
|
|
438
|
-
export function clearDiagnosticsForPath(state, normalizedPath) {
|
|
439
|
-
const legacy = state;
|
|
440
|
-
state.pushDiagnostics?.delete(normalizedPath);
|
|
441
|
-
const pending = state.pendingDiagnostics?.get(normalizedPath);
|
|
442
|
-
if (pending)
|
|
443
|
-
clearTimeout(pending);
|
|
444
|
-
state.pendingDiagnostics?.delete(normalizedPath);
|
|
445
|
-
state.pushDiagnosticTimestamps?.delete(normalizedPath);
|
|
446
|
-
state.documentPullDiagnostics?.delete(normalizedPath);
|
|
447
|
-
state.documentPullDiagnosticTimestamps?.delete(normalizedPath);
|
|
448
|
-
state.diagnosticDocVersions?.delete(normalizedPath);
|
|
449
|
-
state.diagnosticBindings?.delete(normalizedPath);
|
|
450
|
-
state.diagnosticsVersionsByPath?.delete(normalizedPath);
|
|
451
|
-
for (const key of pullSourceKeysForPath(state, normalizedPath)) {
|
|
452
|
-
state.pullResultIds?.delete(key);
|
|
453
|
-
}
|
|
454
|
-
state.documentPullDiagnosticsBySource?.delete(normalizedPath);
|
|
455
|
-
bumpPullGeneration(state, normalizedPath);
|
|
456
|
-
state.workspacePullResultCache?.delete(normalizedPath);
|
|
457
|
-
legacy.diagnostics?.delete(normalizedPath);
|
|
458
|
-
legacy.diagnosticTimestamps?.delete(normalizedPath);
|
|
459
|
-
}
|
|
460
|
-
function logTypeScriptPullSettle(state, normalizedPath, durationMs, pullSettleSource) {
|
|
461
|
-
if (state.serverId !== "typescript")
|
|
462
|
-
return;
|
|
463
|
-
const diagnostics = state.documentPullDiagnostics.get(normalizedPath) ?? [];
|
|
464
|
-
const elapsedSinceDidOpenMs = Math.max(0, Date.now() - (state.documentOpenedAt.get(normalizedPath) ?? Date.now()));
|
|
465
|
-
const diagnosticCodes = [
|
|
466
|
-
...new Set(diagnostics.map((diagnostic) => diagnostic.code).filter((code) => code !== undefined).map(String))
|
|
467
|
-
].slice(0, 8);
|
|
468
|
-
const trackedVersion = state.diagnosticsVersionsByPath?.get(normalizedPath);
|
|
469
|
-
logLatency({
|
|
470
|
-
type: "phase",
|
|
471
|
-
phase: "lsp_typescript_diagnostic_sequence",
|
|
472
|
-
filePath: normalizedPath,
|
|
473
|
-
durationMs,
|
|
474
|
-
metadata: {
|
|
475
|
-
serverId: state.serverId,
|
|
476
|
-
outcome: "settled",
|
|
477
|
-
launchVariant: state.launchVariant ?? "unknown",
|
|
478
|
-
publicationIndex: state.diagnosticPublicationCounts.get(normalizedPath) ?? 0,
|
|
479
|
-
version: trackedVersion ?? "pull-unversioned",
|
|
480
|
-
diagnosticCount: diagnostics.length,
|
|
481
|
-
diagnosticCodes,
|
|
482
|
-
elapsedSinceDidOpenMs,
|
|
483
|
-
settledReturn: true,
|
|
484
|
-
settleSource: pullSettleSource
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
function scanSentContent(content) {
|
|
489
|
-
let lfNewlineCount = 0;
|
|
490
|
-
let index = 0;
|
|
491
|
-
let start = 0;
|
|
492
|
-
for (let i = 0;i < content.length; i++) {
|
|
493
|
-
const code = content.charCodeAt(i);
|
|
494
|
-
if (code !== 10 && code !== 13)
|
|
495
|
-
continue;
|
|
496
|
-
if (code === 10) {
|
|
497
|
-
lfNewlineCount++;
|
|
498
|
-
} else if (content.charCodeAt(i + 1) === 10) {
|
|
499
|
-
lfNewlineCount++;
|
|
500
|
-
i++;
|
|
501
|
-
}
|
|
502
|
-
index++;
|
|
503
|
-
start = i + 1;
|
|
504
|
-
}
|
|
505
|
-
return { lfNewlineCount, lastLine: { index, start } };
|
|
506
|
-
}
|
|
507
|
-
function recordSentContent(state, normalizedPath, version, content, coalescedCount = 0) {
|
|
508
|
-
const scan = scanSentContent(content);
|
|
509
|
-
const previous = state.documentContentHashes.get(normalizedPath);
|
|
510
|
-
if (previous && previous.version > version) {
|
|
511
|
-
incrementDegradationCount({
|
|
512
|
-
kind: "lsp-document-send-order",
|
|
513
|
-
subject: `${state.serverId}:${normalizedPath}`,
|
|
514
|
-
reason: `recorded version ${version} after newer version ${previous.version}`
|
|
515
|
-
});
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
if (previous?.text !== undefined) {
|
|
519
|
-
state.incrementalTextRetainedEntries = Math.max(0, (state.incrementalTextRetainedEntries ?? 1) - 1);
|
|
520
|
-
state.incrementalTextRetainedBytes = Math.max(0, (state.incrementalTextRetainedBytes ?? previous.text.length * 2) - previous.text.length * 2);
|
|
521
|
-
state.incrementalTextBearingPaths?.delete(normalizedPath);
|
|
522
|
-
}
|
|
523
|
-
state.documentContentHashes.delete(normalizedPath);
|
|
524
|
-
state.documentContentHashes.set(normalizedPath, {
|
|
525
|
-
version,
|
|
526
|
-
hash: hashDiagnosticContent(content),
|
|
527
|
-
...state.syncKind === TEXT_DOCUMENT_SYNC_KIND_INCREMENTAL && {
|
|
528
|
-
text: content,
|
|
529
|
-
lastLine: scan.lastLine
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
if (state.syncKind === TEXT_DOCUMENT_SYNC_KIND_INCREMENTAL) {
|
|
533
|
-
state.incrementalTextRetainedEntries = (state.incrementalTextRetainedEntries ?? 0) + 1;
|
|
534
|
-
state.incrementalTextRetainedBytes = (state.incrementalTextRetainedBytes ?? 0) + content.length * 2;
|
|
535
|
-
if (!state.incrementalTextBearingPaths) {
|
|
536
|
-
state.incrementalTextBearingPaths = new Set;
|
|
537
|
-
}
|
|
538
|
-
state.incrementalTextBearingPaths.add(normalizedPath);
|
|
539
|
-
while ((state.incrementalTextRetainedEntries ?? 0) > MAX_INCREMENTAL_TEXT_RETAINED_ENTRIES || (state.incrementalTextRetainedBytes ?? 0) > MAX_INCREMENTAL_TEXT_RETAINED_BYTES) {
|
|
540
|
-
const oldestPath = state.incrementalTextBearingPaths?.values().next().value;
|
|
541
|
-
if (oldestPath === undefined)
|
|
542
|
-
break;
|
|
543
|
-
const binding = state.documentContentHashes.get(oldestPath);
|
|
544
|
-
state.incrementalTextBearingPaths.delete(oldestPath);
|
|
545
|
-
if (!binding || binding.text === undefined)
|
|
546
|
-
continue;
|
|
547
|
-
state.documentContentHashes.set(oldestPath, {
|
|
548
|
-
version: binding.version,
|
|
549
|
-
hash: binding.hash
|
|
550
|
-
});
|
|
551
|
-
state.incrementalTextRetainedEntries = Math.max(0, (state.incrementalTextRetainedEntries ?? 1) - 1);
|
|
552
|
-
state.incrementalTextRetainedBytes = Math.max(0, (state.incrementalTextRetainedBytes ?? 0) - binding.text.length * 2);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
logLatency({
|
|
556
|
-
type: "phase",
|
|
557
|
-
phase: "lsp_document_send",
|
|
558
|
-
filePath: normalizedPath,
|
|
559
|
-
durationMs: 0,
|
|
560
|
-
metadata: {
|
|
561
|
-
version,
|
|
562
|
-
contentLength: content.length,
|
|
563
|
-
contentLineCount: scan.lfNewlineCount + 1,
|
|
564
|
-
...coalescedCount > 0 && { coalescedCount }
|
|
565
|
-
}
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
function buildContentChanges(state, normalizedPath, content) {
|
|
569
|
-
if (state.syncKind !== TEXT_DOCUMENT_SYNC_KIND_INCREMENTAL) {
|
|
570
|
-
return [{ text: content }];
|
|
571
|
-
}
|
|
572
|
-
const previous = state.documentContentHashes.get(normalizedPath);
|
|
573
|
-
if (previous?.text === undefined) {
|
|
574
|
-
return [{ text: content }];
|
|
575
|
-
}
|
|
576
|
-
const previousText = previous.text;
|
|
577
|
-
const lastLine = previous.lastLine ?? scanSentContent(previousText).lastLine;
|
|
578
|
-
const lastLineText = previousText.slice(lastLine.start);
|
|
579
|
-
return [
|
|
580
|
-
{
|
|
581
|
-
range: {
|
|
582
|
-
start: { line: 0, character: 0 },
|
|
583
|
-
end: {
|
|
584
|
-
line: lastLine.index,
|
|
585
|
-
character: convertCharacterOffset(state.positionEncoding, lastLineText, lastLineText.length)
|
|
586
|
-
}
|
|
587
|
-
},
|
|
588
|
-
text: content
|
|
589
|
-
}
|
|
590
|
-
];
|
|
591
|
-
}
|
|
592
|
-
const DYNAMIC_OPERATION_METHOD_MAP = {
|
|
593
|
-
"textDocument/definition": "definition",
|
|
594
|
-
"textDocument/typeDefinition": "typeDefinition",
|
|
595
|
-
"textDocument/declaration": "declaration",
|
|
596
|
-
"textDocument/references": "references",
|
|
597
|
-
"textDocument/hover": "hover",
|
|
598
|
-
"textDocument/signatureHelp": "signatureHelp",
|
|
599
|
-
"textDocument/documentSymbol": "documentSymbol",
|
|
600
|
-
"workspace/symbol": "workspaceSymbol",
|
|
601
|
-
"textDocument/codeAction": "codeAction",
|
|
602
|
-
"textDocument/rename": "rename",
|
|
603
|
-
"textDocument/implementation": "implementation",
|
|
604
|
-
"textDocument/prepareCallHierarchy": "callHierarchy"
|
|
605
|
-
};
|
|
606
|
-
export function applyDynamicCapabilities(state) {
|
|
607
|
-
const registrations = [...state.dynamicRegistrations.values()];
|
|
608
|
-
const registeredMethods = new Set(registrations.map((r) => r.method));
|
|
609
|
-
const hasDynamicPull = registeredMethods.has("textDocument/diagnostic") || registeredMethods.has("workspace/diagnostic");
|
|
610
|
-
if (hasDynamicPull) {
|
|
611
|
-
state.workspaceDiagnosticsSupport = {
|
|
612
|
-
advertised: true,
|
|
613
|
-
mode: "pull",
|
|
614
|
-
workspaceDiagnostics: registrations.some((r) => r.workspaceDiagnostics === true) || registeredMethods.has("workspace/diagnostic"),
|
|
615
|
-
diagnosticProviderKind: "dynamic"
|
|
616
|
-
};
|
|
617
|
-
} else if (state.staticDiagnosticsMode === "push-only" && state.workspaceDiagnosticsSupport.diagnosticProviderKind === "dynamic") {
|
|
618
|
-
state.workspaceDiagnosticsSupport = {
|
|
619
|
-
advertised: false,
|
|
620
|
-
mode: "push-only",
|
|
621
|
-
workspaceDiagnostics: false,
|
|
622
|
-
diagnosticProviderKind: "none"
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
for (const [method, key] of Object.entries(DYNAMIC_OPERATION_METHOD_MAP)) {
|
|
626
|
-
if (registeredMethods.has(method)) {
|
|
627
|
-
state.operationSupport[key] = true;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
export function resolveConfigurationSection(initialization, section) {
|
|
632
|
-
if (!initialization)
|
|
633
|
-
return {};
|
|
634
|
-
if (!section)
|
|
635
|
-
return initialization;
|
|
636
|
-
let cur = initialization;
|
|
637
|
-
for (const part of section.split(".")) {
|
|
638
|
-
if (typeof cur !== "object" || cur === null || !Object.hasOwn(cur, part)) {
|
|
639
|
-
return {};
|
|
640
|
-
}
|
|
641
|
-
cur = cur[part];
|
|
642
|
-
}
|
|
643
|
-
return cur;
|
|
644
|
-
}
|
|
645
|
-
export function setupIncomingHandlers(state, initialization, onDiagnosticsPublished) {
|
|
646
|
-
state.connection.onNotification("textDocument/publishDiagnostics", (params) => {
|
|
647
|
-
const publishReceivedAt = Date.now();
|
|
648
|
-
const filePath = uriToPath(params.uri);
|
|
649
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
650
|
-
if (state.closedDocuments?.has(normalizedPath))
|
|
651
|
-
return;
|
|
652
|
-
onDiagnosticsPublished?.(state.serverId);
|
|
653
|
-
const newDiags = normalizeLspDiagnostics(params.diagnostics || []);
|
|
654
|
-
const docVersion = params.version;
|
|
655
|
-
if (PUB_DEBUG) {
|
|
656
|
-
logExtension({
|
|
657
|
-
subsystem: "lsp-pub",
|
|
658
|
-
level: "debug",
|
|
659
|
-
message: `server=${state.serverId} pubVersion=${docVersion} docVersion=${state.documentVersions?.get(normalizedPath)} diags=${newDiags.length}`
|
|
660
|
-
});
|
|
661
|
-
}
|
|
662
|
-
const strategy = getStrategy(state.serverId, state.launchVariant);
|
|
663
|
-
const isTypeScriptTelemetry = state.serverId === "typescript";
|
|
664
|
-
const publicationIndex = isTypeScriptTelemetry ? (state.diagnosticPublicationCounts.get(normalizedPath) ?? 0) + 1 : 0;
|
|
665
|
-
if (isTypeScriptTelemetry) {
|
|
666
|
-
state.diagnosticPublicationCounts.set(normalizedPath, publicationIndex);
|
|
667
|
-
}
|
|
668
|
-
const diagnosticCodes = isTypeScriptTelemetry ? [
|
|
669
|
-
...new Set(newDiags.map((diagnostic) => diagnostic.code).filter((code) => code !== undefined).map(String))
|
|
670
|
-
].slice(0, 8) : [];
|
|
671
|
-
const logSequence = (settledReturn, settleSource, durationMs) => {
|
|
672
|
-
if (state.serverId !== "typescript")
|
|
673
|
-
return;
|
|
674
|
-
const elapsedSinceDidOpenMs = Math.max(0, Date.now() - (state.documentOpenedAt.get(normalizedPath) ?? Date.now()));
|
|
675
|
-
logLatency({
|
|
676
|
-
type: "phase",
|
|
677
|
-
phase: "lsp_typescript_diagnostic_sequence",
|
|
678
|
-
filePath: normalizedPath,
|
|
679
|
-
durationMs,
|
|
680
|
-
metadata: {
|
|
681
|
-
serverId: state.serverId,
|
|
682
|
-
outcome: settledReturn ? "settled" : "published",
|
|
683
|
-
launchVariant: state.launchVariant ?? "unknown",
|
|
684
|
-
publicationIndex,
|
|
685
|
-
version: docVersion ?? "push-unversioned",
|
|
686
|
-
diagnosticCount: newDiags.length,
|
|
687
|
-
diagnosticCodes,
|
|
688
|
-
elapsedSinceDidOpenMs,
|
|
689
|
-
settledReturn,
|
|
690
|
-
settleSource
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
};
|
|
694
|
-
const recordDocVersion = () => {
|
|
695
|
-
if (docVersion !== undefined) {
|
|
696
|
-
state.diagnosticDocVersions.set(normalizedPath, docVersion);
|
|
697
|
-
}
|
|
698
|
-
recordBinding();
|
|
699
|
-
};
|
|
700
|
-
const recordBinding = () => {
|
|
701
|
-
if (docVersion === undefined) {
|
|
702
|
-
state.diagnosticBindings.delete(normalizedPath);
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
const sent = state.documentContentHashes.get(normalizedPath);
|
|
706
|
-
state.diagnosticBindings.set(normalizedPath, {
|
|
707
|
-
version: docVersion,
|
|
708
|
-
contentHash: sent && sent.version === docVersion ? sent.hash : undefined
|
|
709
|
-
});
|
|
710
|
-
};
|
|
711
|
-
const isSupersededPush = () => {
|
|
712
|
-
if (docVersion === undefined)
|
|
713
|
-
return false;
|
|
714
|
-
const currentVersion = state.documentVersions.get(normalizedPath);
|
|
715
|
-
return currentVersion !== undefined && docVersion < currentVersion;
|
|
716
|
-
};
|
|
717
|
-
if (strategy.emptyFirstPublish === "indexing" && !state.emptyFirstPublishHoldSpent && newDiags.length === 0 && !state.pushDiagnostics.has(normalizedPath) && !state.pendingDiagnostics.has(normalizedPath)) {
|
|
718
|
-
state.emptyFirstPublishHoldSpent = true;
|
|
719
|
-
logLatency({
|
|
720
|
-
type: "phase",
|
|
721
|
-
phase: "lsp_empty_first_publish_held",
|
|
722
|
-
filePath: normalizedPath,
|
|
723
|
-
durationMs: Math.max(0, publishReceivedAt - (state.documentOpenedAt.get(normalizedPath) ?? publishReceivedAt)),
|
|
724
|
-
metadata: {
|
|
725
|
-
serverId: state.serverId,
|
|
726
|
-
emptyFirstPublish: strategy.emptyFirstPublish,
|
|
727
|
-
pubVersion: docVersion ?? "push-unversioned"
|
|
728
|
-
}
|
|
729
|
-
});
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
if (strategy.seedFirstPush && !state.pushDiagnostics.has(normalizedPath)) {
|
|
733
|
-
if (isSupersededPush())
|
|
734
|
-
return;
|
|
735
|
-
state.pushDiagnostics.set(normalizedPath, newDiags);
|
|
736
|
-
state.pushDiagnosticTimestamps.set(normalizedPath, Date.now());
|
|
737
|
-
recordDocVersion();
|
|
738
|
-
bumpDiagnosticsVersion(state, normalizedPath);
|
|
739
|
-
state.diagnosticEmitter.emit("diagnostics", normalizedPath);
|
|
740
|
-
logSequence(true, "first-push", 0);
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
logSequence(false, "publication", 0);
|
|
744
|
-
const existingTimer = state.pendingDiagnostics.get(normalizedPath);
|
|
745
|
-
if (existingTimer)
|
|
746
|
-
clearTimeout(existingTimer);
|
|
747
|
-
const timer = setTimeout(() => {
|
|
748
|
-
state.pendingDiagnostics.delete(normalizedPath);
|
|
749
|
-
if (isSupersededPush())
|
|
750
|
-
return;
|
|
751
|
-
state.pushDiagnostics.set(normalizedPath, newDiags);
|
|
752
|
-
state.pushDiagnosticTimestamps.set(normalizedPath, Date.now());
|
|
753
|
-
recordDocVersion();
|
|
754
|
-
bumpDiagnosticsVersion(state, normalizedPath);
|
|
755
|
-
state.diagnosticEmitter.emit("diagnostics", normalizedPath);
|
|
756
|
-
logSequence(true, "quiet-window", Date.now() - publishReceivedAt);
|
|
757
|
-
}, strategy.debounceMs);
|
|
758
|
-
state.pendingDiagnostics.set(normalizedPath, timer);
|
|
759
|
-
});
|
|
760
|
-
state.connection.onRequest("workspace/workspaceFolders", () => [
|
|
761
|
-
{ name: "workspace", uri: pathToFileURL(state.root).href }
|
|
762
|
-
]);
|
|
763
|
-
state.connection.onRequest("client/registerCapability", async (params) => {
|
|
764
|
-
for (const reg of params?.registrations ?? []) {
|
|
765
|
-
const options = reg.registerOptions;
|
|
766
|
-
const commands = Array.isArray(options?.commands) ? options.commands.filter((cmd) => typeof cmd === "string") : undefined;
|
|
767
|
-
if (reg.id && reg.method) {
|
|
768
|
-
state.dynamicRegistrations.set(reg.id, {
|
|
769
|
-
method: reg.method,
|
|
770
|
-
...typeof options?.identifier === "string" && options.identifier ? { identifier: options.identifier } : {},
|
|
771
|
-
...typeof options?.workspaceDiagnostics === "boolean" ? { workspaceDiagnostics: options.workspaceDiagnostics } : {},
|
|
772
|
-
...commands ? { commands } : {}
|
|
773
|
-
});
|
|
774
|
-
}
|
|
775
|
-
if (reg.method === "workspace/executeCommand" && commands) {
|
|
776
|
-
for (const cmd of commands)
|
|
777
|
-
state.advertisedCommands.add(cmd);
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
applyDynamicCapabilities(state);
|
|
781
|
-
});
|
|
782
|
-
state.connection.onRequest("client/unregisterCapability", async (params) => {
|
|
783
|
-
const retiredIdentifiers = new Set;
|
|
784
|
-
for (const unreg of params?.unregisterations ?? []) {
|
|
785
|
-
if (!unreg.id)
|
|
786
|
-
continue;
|
|
787
|
-
const registration = state.dynamicRegistrations.get(unreg.id);
|
|
788
|
-
state.dynamicRegistrations.delete(unreg.id);
|
|
789
|
-
if (registration?.method === "textDocument/diagnostic" && registration.identifier) {
|
|
790
|
-
retiredIdentifiers.add(registration.identifier);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
const stillRegistered = new Set([...state.dynamicRegistrations.values()].filter((r) => r.method === "textDocument/diagnostic").map((r) => r.identifier));
|
|
794
|
-
for (const identifier of retiredIdentifiers) {
|
|
795
|
-
if (!stillRegistered.has(identifier)) {
|
|
796
|
-
retirePullSource(state, identifier);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
applyDynamicCapabilities(state);
|
|
800
|
-
});
|
|
801
|
-
state.connection.onRequest("workspace/applyEdit", async (params) => {
|
|
802
|
-
if (state.serverEditsAllowed <= 0 || !params?.edit) {
|
|
803
|
-
return { applied: false, failureReason: "edit not solicited" };
|
|
804
|
-
}
|
|
805
|
-
const context = state.activeMutationContext;
|
|
806
|
-
const telemetryContext = context ?? {
|
|
807
|
-
cwd: state.root,
|
|
808
|
-
correlationId: newLspMutationCorrelationId(),
|
|
809
|
-
tool: "lsp-workspace-applyEdit",
|
|
810
|
-
source: "lsp-execute-command"
|
|
811
|
-
};
|
|
812
|
-
try {
|
|
813
|
-
await applyWorkspaceEdit(params.edit, state.root, {
|
|
814
|
-
positionEncoding: state.positionEncoding,
|
|
815
|
-
documentVersions: state.documentVersions,
|
|
816
|
-
mutationContext: telemetryContext
|
|
817
|
-
});
|
|
818
|
-
return { applied: true };
|
|
819
|
-
} catch (err) {
|
|
820
|
-
return {
|
|
821
|
-
applied: false,
|
|
822
|
-
failureReason: err instanceof Error ? err.message : String(err)
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
});
|
|
826
|
-
state.connection.onRequest("workspace/configuration", async (params) => {
|
|
827
|
-
const items = params?.items ?? [];
|
|
828
|
-
return items.map((item) => resolveConfigurationSection(initialization, item?.section));
|
|
829
|
-
});
|
|
830
|
-
state.connection.onRequest("window/workDoneProgress/create", async () => {});
|
|
831
|
-
let refreshRepullRunning = false;
|
|
832
|
-
let refreshRepullRerunRequested = false;
|
|
833
|
-
const runRefreshRepullPool = async () => {
|
|
834
|
-
do {
|
|
835
|
-
refreshRepullRerunRequested = false;
|
|
836
|
-
const toRepull = state.workspaceDiagnosticsSupport.mode === "pull" ? [...state.openDocuments] : [];
|
|
837
|
-
if (toRepull.length > 0) {
|
|
838
|
-
try {
|
|
839
|
-
await mapWithConcurrency(toRepull, REFRESH_REPULL_CONCURRENCY, async (normalizedPath) => {
|
|
840
|
-
const uri = state.openDocumentUris?.get(normalizedPath);
|
|
841
|
-
const filePath = uri ? uriToPath(uri) : normalizedPath;
|
|
842
|
-
await clientRequestPullDiagnostics(state, filePath);
|
|
843
|
-
});
|
|
844
|
-
} catch (err) {
|
|
845
|
-
incrementDegradationCount({
|
|
846
|
-
kind: "lsp-pull-unconfirmed",
|
|
847
|
-
subject: state.serverId,
|
|
848
|
-
reason: `refresh re-pull threw: ${err instanceof Error ? err.message : String(err)}`
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
} while (refreshRepullRerunRequested);
|
|
853
|
-
refreshRepullRunning = false;
|
|
854
|
-
};
|
|
855
|
-
state.connection.onRequest("workspace/diagnostic/refresh", async () => {
|
|
856
|
-
state.workspacePullResultCache.clear();
|
|
857
|
-
clearAllWorkspaceDiagnosticsCaches();
|
|
858
|
-
clearWorkspaceDiagnosticsCacheAtAndAbove(state.root);
|
|
859
|
-
for (const normalizedPath of state.openDocuments) {
|
|
860
|
-
clearDiagnosticsForPath(state, normalizedPath);
|
|
861
|
-
}
|
|
862
|
-
if (state.workspaceDiagnosticsSupport.mode === "pull" && state.openDocuments.size > 0) {
|
|
863
|
-
if (refreshRepullRunning) {
|
|
864
|
-
refreshRepullRerunRequested = true;
|
|
865
|
-
} else {
|
|
866
|
-
refreshRepullRunning = true;
|
|
867
|
-
setImmediate(() => {
|
|
868
|
-
runRefreshRepullPool();
|
|
869
|
-
});
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
return null;
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
function markExitedIfUnset(state) {
|
|
876
|
-
if (state.exitedAt === undefined) {
|
|
877
|
-
state.exitedAt = Date.now();
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
function setupConnectionLifecycle(state, recentStderr) {
|
|
881
|
-
state.connection.onError(([error]) => {
|
|
882
|
-
state.lastError = error instanceof Error ? error : new Error(String(error));
|
|
883
|
-
state.isConnected = false;
|
|
884
|
-
state.isDestroyed = true;
|
|
885
|
-
markExitedIfUnset(state);
|
|
886
|
-
disposeClientConnection(state);
|
|
887
|
-
});
|
|
888
|
-
state.connection.onClose(() => {
|
|
889
|
-
state.isConnected = false;
|
|
890
|
-
state.isDestroyed = true;
|
|
891
|
-
markExitedIfUnset(state);
|
|
892
|
-
disposeClientConnection(state);
|
|
893
|
-
});
|
|
894
|
-
state.lspProcess.process.on("exit", (code, signal) => {
|
|
895
|
-
const wasIntentional = state.shutdownRequested;
|
|
896
|
-
state.isConnected = false;
|
|
897
|
-
state.isDestroyed = true;
|
|
898
|
-
markExitedIfUnset(state);
|
|
899
|
-
disposeClientConnection(state);
|
|
900
|
-
if (!wasIntentional) {
|
|
901
|
-
logLatency({
|
|
902
|
-
type: "phase",
|
|
903
|
-
phase: "lsp_server_unexpected_exit",
|
|
904
|
-
filePath: state.root,
|
|
905
|
-
durationMs: 0,
|
|
906
|
-
metadata: {
|
|
907
|
-
serverId: state.serverId,
|
|
908
|
-
pid: state.lspProcess.pid,
|
|
909
|
-
exitCode: code ?? null,
|
|
910
|
-
exitSignal: signal ?? null,
|
|
911
|
-
stderrTail: recentStderr(20)
|
|
912
|
-
}
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
});
|
|
916
|
-
state.lspProcess.process.on("close", (code, signal) => {
|
|
917
|
-
if (state.shutdownRequested)
|
|
918
|
-
return;
|
|
919
|
-
try {
|
|
920
|
-
const stderrTail = recentStderr(20);
|
|
921
|
-
incrementDegradationCount({
|
|
922
|
-
kind: "lsp-server-unexpected-close",
|
|
923
|
-
subject: state.serverId,
|
|
924
|
-
reason: `closed unprompted: code=${code ?? "none"} signal=${signal ?? "none"} ` + `stderr=${stderrTail.length > 0 ? `present(${stderrTail.length}b)` : "empty"} root=${state.root}`
|
|
925
|
-
});
|
|
926
|
-
} catch {}
|
|
927
|
-
});
|
|
928
|
-
}
|
|
929
|
-
const PULL_RACE_BACKSTOP_MARGIN_MS = 25;
|
|
930
|
-
async function clientRequestPullDiagnostics(state, filePath, budgetMs = PULL_REQUEST_TIMEOUT_MS) {
|
|
931
|
-
if (!isClientAlive(state))
|
|
932
|
-
return { status: "unavailable" };
|
|
933
|
-
const sources = pullDiagnosticSources(state);
|
|
934
|
-
if (sources.length === 1) {
|
|
935
|
-
return aggregatePullOutcomes(state, filePath, [
|
|
936
|
-
await pullDiagnosticSource(state, filePath, budgetMs, undefined)
|
|
937
|
-
]);
|
|
938
|
-
}
|
|
939
|
-
const attempts = sources.map((identifier) => pullDiagnosticSource(state, filePath, budgetMs, identifier));
|
|
940
|
-
const settled = await raceToCompletion(attempts, (results) => results.some((r) => r.status === "found" && r.primaryCount > 0), {
|
|
941
|
-
timeoutMs: Math.max(1, Math.min(PULL_REQUEST_TIMEOUT_MS, budgetMs)) + PULL_RACE_BACKSTOP_MARGIN_MS
|
|
942
|
-
});
|
|
943
|
-
return aggregatePullOutcomes(state, filePath, settled);
|
|
944
|
-
}
|
|
945
|
-
function aggregatePullOutcomes(state, filePath, outcomes) {
|
|
946
|
-
const found = outcomes.filter((o) => o.status === "found");
|
|
947
|
-
if (found.length > 0) {
|
|
948
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
949
|
-
const unionCount = state.documentPullDiagnostics.get(normalizedPath)?.length ?? 0;
|
|
950
|
-
return {
|
|
951
|
-
status: "found",
|
|
952
|
-
count: Math.max(unionCount, ...found.map((o) => o.count))
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
if (outcomes.some((o) => o.status === "unavailable")) {
|
|
956
|
-
return { status: "unavailable" };
|
|
957
|
-
}
|
|
958
|
-
return outcomes.some((o) => o.status === "clean") ? { status: "clean" } : { status: "unavailable" };
|
|
959
|
-
}
|
|
960
|
-
async function pullDiagnosticSource(state, filePath, budgetMs, identifier) {
|
|
961
|
-
if (!isClientAlive(state))
|
|
962
|
-
return { status: "unavailable" };
|
|
963
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
964
|
-
if (budgetMs < PULL_MIN_USABLE_BUDGET_MS) {
|
|
965
|
-
emitBounded("lsp_pull_skipped_budget_exhausted", `${state.serverId}::${pullSourceKey(normalizedPath, identifier)}`, {
|
|
966
|
-
type: "phase",
|
|
967
|
-
filePath: normalizedPath,
|
|
968
|
-
durationMs: 0,
|
|
969
|
-
metadata: {
|
|
970
|
-
identifier: identifier ?? "bare",
|
|
971
|
-
remainingBudgetMs: budgetMs,
|
|
972
|
-
server: state.serverId
|
|
973
|
-
}
|
|
974
|
-
}, {
|
|
975
|
-
ledgerKind: "lsp-pull-skipped-budget-exhausted",
|
|
976
|
-
reason: `pull skipped on ${state.serverId}: budget already exhausted (${budgetMs}ms remaining)`
|
|
977
|
-
});
|
|
978
|
-
return { status: "unavailable" };
|
|
979
|
-
}
|
|
980
|
-
const uri = pathToFileURL(filePath).href;
|
|
981
|
-
const sourceKey = pullSourceKey(normalizedPath, identifier);
|
|
982
|
-
if (state.abandonedPullRequests?.has(sourceKey)) {
|
|
983
|
-
return { status: "unavailable" };
|
|
984
|
-
}
|
|
985
|
-
const previousResultId = state.pullResultIds.get(sourceKey);
|
|
986
|
-
const generation = pullGenerationFor(state, normalizedPath);
|
|
987
|
-
const requestSequence = claimPullRequestSequence(state, sourceKey);
|
|
988
|
-
const requestStartedAt = Date.now();
|
|
989
|
-
const effectiveTimeoutMs = Math.max(1, Math.min(PULL_REQUEST_TIMEOUT_MS, budgetMs));
|
|
990
|
-
const pullAbort = new AbortController;
|
|
991
|
-
const pullSettlement = { cancelled: false };
|
|
992
|
-
const requestPromise = safeSendRequest(state.connection, "textDocument/diagnostic", {
|
|
993
|
-
textDocument: { uri },
|
|
994
|
-
...identifier !== undefined && { identifier },
|
|
995
|
-
...previousResultId !== undefined && { previousResultId }
|
|
996
|
-
}, pullAbort.signal, pullSettlement);
|
|
997
|
-
try {
|
|
998
|
-
const report = await withTimeout(requestPromise, effectiveTimeoutMs);
|
|
999
|
-
if (!report) {
|
|
1000
|
-
recordPullFailure(state, "textDocument/diagnostic", new Error("empty response"));
|
|
1001
|
-
return { status: "unavailable" };
|
|
1002
|
-
}
|
|
1003
|
-
const now = Date.now();
|
|
1004
|
-
const sentHash = state.documentContentHashes.get(normalizedPath)?.hash;
|
|
1005
|
-
if (pullGenerationFor(state, normalizedPath) !== generation || !isNewestPullRequest(state, sourceKey, requestSequence)) {
|
|
1006
|
-
return { status: "unavailable" };
|
|
1007
|
-
}
|
|
1008
|
-
let primaryCount;
|
|
1009
|
-
if (report.kind === "unchanged") {
|
|
1010
|
-
primaryCount = pullSourceDiagnostics(state, normalizedPath, identifier).length;
|
|
1011
|
-
state.documentPullDiagnosticTimestamps.set(normalizedPath, now);
|
|
1012
|
-
} else {
|
|
1013
|
-
const primaryItems = normalizeLspDiagnostics(report.items ?? []);
|
|
1014
|
-
storePullSourceDiagnostics(state, normalizedPath, identifier, primaryItems);
|
|
1015
|
-
state.documentPullDiagnosticTimestamps.set(normalizedPath, now);
|
|
1016
|
-
bumpDiagnosticsVersion(state, normalizedPath);
|
|
1017
|
-
state.diagnosticBindings.set(normalizedPath, { contentHash: sentHash });
|
|
1018
|
-
primaryCount = primaryItems.length;
|
|
1019
|
-
}
|
|
1020
|
-
let totalCount = primaryCount;
|
|
1021
|
-
if (report.resultId !== undefined) {
|
|
1022
|
-
state.pullResultIds.set(sourceKey, report.resultId);
|
|
1023
|
-
} else {
|
|
1024
|
-
state.pullResultIds.delete(sourceKey);
|
|
1025
|
-
}
|
|
1026
|
-
if (report.relatedDocuments) {
|
|
1027
|
-
for (const [relatedUri, related] of Object.entries(report.relatedDocuments)) {
|
|
1028
|
-
const relatedPath = uriToPath(relatedUri);
|
|
1029
|
-
const relatedNormalized = normalizeMapKey(relatedPath);
|
|
1030
|
-
if (related?.kind === "unchanged") {
|
|
1031
|
-
totalCount += pullSourceDiagnostics(state, relatedNormalized, identifier).length;
|
|
1032
|
-
state.documentPullDiagnosticTimestamps.set(relatedNormalized, now);
|
|
1033
|
-
} else {
|
|
1034
|
-
const relatedItems = normalizeLspDiagnostics(related?.items ?? []);
|
|
1035
|
-
storePullSourceDiagnostics(state, relatedNormalized, identifier, relatedItems);
|
|
1036
|
-
state.documentPullDiagnosticTimestamps.set(relatedNormalized, now);
|
|
1037
|
-
state.diagnosticBindings.delete(relatedNormalized);
|
|
1038
|
-
totalCount += relatedItems.length;
|
|
1039
|
-
}
|
|
1040
|
-
if (related?.resultId !== undefined) {
|
|
1041
|
-
state.pullResultIds.set(pullSourceKey(relatedNormalized, identifier), related.resultId);
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
state.diagnosticEmitter.emit("diagnostics", normalizedPath);
|
|
1046
|
-
return totalCount > 0 ? { status: "found", count: totalCount, primaryCount } : { status: "clean" };
|
|
1047
|
-
} catch (err) {
|
|
1048
|
-
if (isPullTimeoutError(err, effectiveTimeoutMs)) {
|
|
1049
|
-
trackAbandonedPullRequest(state, sourceKey, requestPromise);
|
|
1050
|
-
pullAbort.abort();
|
|
1051
|
-
recordPullTimeoutTelemetry({
|
|
1052
|
-
scope: normalizedPath,
|
|
1053
|
-
identifier,
|
|
1054
|
-
effectiveBudgetMs: effectiveTimeoutMs,
|
|
1055
|
-
hadPreviousResultId: previousResultId !== undefined,
|
|
1056
|
-
elapsedMs: Date.now() - requestStartedAt,
|
|
1057
|
-
server: state.serverId
|
|
1058
|
-
});
|
|
1059
|
-
armLateAnswerTelemetry({
|
|
1060
|
-
requestPromise,
|
|
1061
|
-
scope: normalizedPath,
|
|
1062
|
-
identifier,
|
|
1063
|
-
subject: sourceKey,
|
|
1064
|
-
requestStartedAt,
|
|
1065
|
-
server: state.serverId,
|
|
1066
|
-
settlement: pullSettlement
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
recordPullFailure(state, "textDocument/diagnostic", err);
|
|
1070
|
-
return { status: "unavailable" };
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
function trackAbandonedPullRequest(state, sourceKey, requestPromise) {
|
|
1074
|
-
if (!state.abandonedPullRequests)
|
|
1075
|
-
state.abandonedPullRequests = new Map;
|
|
1076
|
-
state.abandonedPullRequests.set(sourceKey, requestPromise);
|
|
1077
|
-
const release = () => {
|
|
1078
|
-
if (state.abandonedPullRequests?.get(sourceKey) === requestPromise) {
|
|
1079
|
-
state.abandonedPullRequests.delete(sourceKey);
|
|
1080
|
-
}
|
|
1081
|
-
};
|
|
1082
|
-
requestPromise.then(release, release);
|
|
1083
|
-
}
|
|
1084
|
-
function isPullTimeoutError(err, timeoutMs) {
|
|
1085
|
-
return err instanceof Error && err.message === `Timeout after ${timeoutMs}ms`;
|
|
1086
|
-
}
|
|
1087
|
-
function recordPullTimeoutTelemetry(args) {
|
|
1088
|
-
emitBounded("lsp_pull_diagnostic_timeout", `${args.server}::${args.scope}::${args.identifier ?? "bare"}`, {
|
|
1089
|
-
type: "phase",
|
|
1090
|
-
filePath: args.scope,
|
|
1091
|
-
durationMs: args.elapsedMs,
|
|
1092
|
-
metadata: {
|
|
1093
|
-
identifier: args.identifier ?? "bare",
|
|
1094
|
-
effectiveBudgetMs: args.effectiveBudgetMs,
|
|
1095
|
-
hadPreviousResultId: args.hadPreviousResultId,
|
|
1096
|
-
server: args.server
|
|
1097
|
-
}
|
|
1098
|
-
}, {
|
|
1099
|
-
ledgerKind: "lsp-pull-diagnostic-timeout",
|
|
1100
|
-
reason: `pull timeout on ${args.server} after ${args.elapsedMs}ms (budget ${args.effectiveBudgetMs}ms)`
|
|
1101
|
-
});
|
|
1102
|
-
}
|
|
1103
|
-
function armLateAnswerTelemetry(args) {
|
|
1104
|
-
args.requestPromise.then(() => {
|
|
1105
|
-
if (args.settlement.cancelled)
|
|
1106
|
-
return;
|
|
1107
|
-
try {
|
|
1108
|
-
const elapsedMs = Date.now() - args.requestStartedAt;
|
|
1109
|
-
emitBounded("lsp_pull_late_answer_discarded", args.subject, {
|
|
1110
|
-
type: "phase",
|
|
1111
|
-
filePath: args.scope,
|
|
1112
|
-
durationMs: elapsedMs,
|
|
1113
|
-
metadata: { identifier: args.identifier ?? "bare" }
|
|
1114
|
-
}, {
|
|
1115
|
-
ledgerKind: "lsp-pull-late-answer",
|
|
1116
|
-
reason: `late pull answer discarded after ${elapsedMs}ms`
|
|
1117
|
-
});
|
|
1118
|
-
} catch {}
|
|
1119
|
-
}, (err) => {
|
|
1120
|
-
try {
|
|
1121
|
-
const elapsedMs = Date.now() - args.requestStartedAt;
|
|
1122
|
-
const candidate = err;
|
|
1123
|
-
const code = typeof candidate?.code === "number" || typeof candidate?.code === "string" ? candidate.code : undefined;
|
|
1124
|
-
emitBounded("lsp_pull_late_rejection", `${args.server}::${args.subject}`, {
|
|
1125
|
-
type: "phase",
|
|
1126
|
-
filePath: args.scope,
|
|
1127
|
-
durationMs: elapsedMs,
|
|
1128
|
-
metadata: {
|
|
1129
|
-
identifier: args.identifier ?? "bare",
|
|
1130
|
-
server: args.server,
|
|
1131
|
-
...code !== undefined && { code }
|
|
1132
|
-
}
|
|
1133
|
-
}, {
|
|
1134
|
-
ledgerKind: "lsp-pull-late-rejection",
|
|
1135
|
-
reason: `late pull rejection ${elapsedMs}ms after timeout${code !== undefined ? ` (code ${code})` : ""}`
|
|
1136
|
-
});
|
|
1137
|
-
} catch {}
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
const PULL_FAILURE_HISTORY_LIMIT = 10;
|
|
1141
|
-
function recordPullFailure(state, method, error) {
|
|
1142
|
-
const candidate = error;
|
|
1143
|
-
const message = typeof candidate.message === "string" ? candidate.message : "";
|
|
1144
|
-
const unsupportedMessage = /^(?:method not found|unknown method|unsupported method)(?::|$)/i;
|
|
1145
|
-
if (candidate.code === -32601 || candidate.code === "-32601" || unsupportedMessage.test(message.trim()))
|
|
1146
|
-
return;
|
|
1147
|
-
state.pullFailureHistory.push({
|
|
1148
|
-
timestamp: Date.now(),
|
|
1149
|
-
method,
|
|
1150
|
-
...typeof candidate.code === "number" || typeof candidate.code === "string" ? { code: candidate.code } : {},
|
|
1151
|
-
message: typeof candidate.message === "string" ? candidate.message : String(error)
|
|
1152
|
-
});
|
|
1153
|
-
if (state.pullFailureHistory.length > PULL_FAILURE_HISTORY_LIMIT) {
|
|
1154
|
-
state.pullFailureHistory.splice(0, state.pullFailureHistory.length - PULL_FAILURE_HISTORY_LIMIT);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
export async function clientRequestWorkspaceDiagnostics(state, budgetMs) {
|
|
1158
|
-
if (!isClientAlive(state))
|
|
1159
|
-
return;
|
|
1160
|
-
if (!state.workspaceDiagnosticsSupport.workspaceDiagnostics)
|
|
1161
|
-
return;
|
|
1162
|
-
if (state.abandonedPullRequests?.has(WORKSPACE_PULL_SCOPE))
|
|
1163
|
-
return;
|
|
1164
|
-
const previousResultIds = Array.from(state.workspacePullResultCache.values()).map((entry) => ({ uri: entry.uri, value: entry.resultId }));
|
|
1165
|
-
if (budgetMs < PULL_MIN_USABLE_BUDGET_MS) {
|
|
1166
|
-
emitBounded("lsp_pull_skipped_budget_exhausted", `${state.serverId}::${WORKSPACE_PULL_SCOPE}`, {
|
|
1167
|
-
type: "phase",
|
|
1168
|
-
filePath: WORKSPACE_PULL_SCOPE,
|
|
1169
|
-
durationMs: 0,
|
|
1170
|
-
metadata: {
|
|
1171
|
-
identifier: "bare",
|
|
1172
|
-
remainingBudgetMs: budgetMs,
|
|
1173
|
-
server: state.serverId
|
|
1174
|
-
}
|
|
1175
|
-
}, {
|
|
1176
|
-
ledgerKind: "lsp-pull-skipped-budget-exhausted",
|
|
1177
|
-
reason: `workspace pull skipped on ${state.serverId}: budget already exhausted (${budgetMs}ms remaining)`
|
|
1178
|
-
});
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
1181
|
-
const workspacePullStartedAt = Date.now();
|
|
1182
|
-
const workspacePullTimeoutMs = Math.max(1, budgetMs);
|
|
1183
|
-
const workspacePullAbort = new AbortController;
|
|
1184
|
-
const workspacePullSettlement = { cancelled: false };
|
|
1185
|
-
const workspaceRequestPromise = safeSendRequest(state.connection, "workspace/diagnostic", { previousResultIds }, workspacePullAbort.signal, workspacePullSettlement);
|
|
1186
|
-
try {
|
|
1187
|
-
const report = await withTimeout(workspaceRequestPromise, workspacePullTimeoutMs);
|
|
1188
|
-
if (!report || !Array.isArray(report.items))
|
|
1189
|
-
return;
|
|
1190
|
-
const out = [];
|
|
1191
|
-
const fallbackPullDeadline = Date.now() + Math.max(1, budgetMs);
|
|
1192
|
-
for (const item of report.items) {
|
|
1193
|
-
if (!item?.uri)
|
|
1194
|
-
continue;
|
|
1195
|
-
const filePath = uriToPath(item.uri);
|
|
1196
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
1197
|
-
if (item.kind === "unchanged") {
|
|
1198
|
-
const prior = state.workspacePullResultCache.get(normalizedPath);
|
|
1199
|
-
if (!prior) {
|
|
1200
|
-
const remainingMs = fallbackPullDeadline - Date.now();
|
|
1201
|
-
if (remainingMs <= 0) {
|
|
1202
|
-
incrementDegradationCount({
|
|
1203
|
-
kind: "lsp-pull-unconfirmed",
|
|
1204
|
-
subject: state.serverId,
|
|
1205
|
-
reason: "unchanged report with no workspacePullResultCache basis; the shared fallback-pull deadline was already exhausted by earlier files this sweep"
|
|
1206
|
-
});
|
|
1207
|
-
continue;
|
|
1208
|
-
}
|
|
1209
|
-
const outcome = await clientRequestPullDiagnostics(state, filePath, remainingMs);
|
|
1210
|
-
if (outcome.status === "found") {
|
|
1211
|
-
out.push({
|
|
1212
|
-
filePath,
|
|
1213
|
-
diagnostics: state.documentPullDiagnostics.get(normalizedPath) ?? [],
|
|
1214
|
-
contentHash: state.diagnosticBindings.get(normalizedPath)?.contentHash
|
|
1215
|
-
});
|
|
1216
|
-
} else if (outcome.status === "unavailable") {
|
|
1217
|
-
incrementDegradationCount({
|
|
1218
|
-
kind: "lsp-pull-unconfirmed",
|
|
1219
|
-
subject: state.serverId,
|
|
1220
|
-
reason: "unchanged report with no workspacePullResultCache basis, and the per-file fallback pull was unavailable"
|
|
1221
|
-
});
|
|
1222
|
-
}
|
|
1223
|
-
continue;
|
|
1224
|
-
}
|
|
1225
|
-
if (item.resultId !== undefined) {
|
|
1226
|
-
state.workspacePullResultCache.set(normalizedPath, {
|
|
1227
|
-
...prior,
|
|
1228
|
-
resultId: item.resultId
|
|
1229
|
-
});
|
|
1230
|
-
}
|
|
1231
|
-
out.push({
|
|
1232
|
-
filePath,
|
|
1233
|
-
diagnostics: prior.diagnostics,
|
|
1234
|
-
contentHash: prior.contentHash
|
|
1235
|
-
});
|
|
1236
|
-
continue;
|
|
1237
|
-
}
|
|
1238
|
-
const diagnostics = normalizeLspDiagnostics(item.items ?? []);
|
|
1239
|
-
let contentHash;
|
|
1240
|
-
try {
|
|
1241
|
-
contentHash = hashDiagnosticContent(await readFile(filePath, "utf-8"));
|
|
1242
|
-
} catch {
|
|
1243
|
-
contentHash = undefined;
|
|
1244
|
-
}
|
|
1245
|
-
if (item.resultId !== undefined) {
|
|
1246
|
-
if (state.workspacePullResultCache.size >= WORKSPACE_PULL_RESULT_CACHE_MAX) {
|
|
1247
|
-
state.workspacePullResultCache.clear();
|
|
1248
|
-
}
|
|
1249
|
-
state.workspacePullResultCache.set(normalizedPath, {
|
|
1250
|
-
uri: item.uri,
|
|
1251
|
-
resultId: item.resultId,
|
|
1252
|
-
diagnostics,
|
|
1253
|
-
contentHash
|
|
1254
|
-
});
|
|
1255
|
-
} else {
|
|
1256
|
-
state.workspacePullResultCache.delete(normalizedPath);
|
|
1257
|
-
}
|
|
1258
|
-
out.push({ filePath, diagnostics, contentHash });
|
|
1259
|
-
}
|
|
1260
|
-
return out;
|
|
1261
|
-
} catch (err) {
|
|
1262
|
-
if (isPullTimeoutError(err, workspacePullTimeoutMs)) {
|
|
1263
|
-
trackAbandonedPullRequest(state, WORKSPACE_PULL_SCOPE, workspaceRequestPromise);
|
|
1264
|
-
workspacePullAbort.abort();
|
|
1265
|
-
recordPullTimeoutTelemetry({
|
|
1266
|
-
scope: WORKSPACE_PULL_SCOPE,
|
|
1267
|
-
identifier: undefined,
|
|
1268
|
-
effectiveBudgetMs: workspacePullTimeoutMs,
|
|
1269
|
-
hadPreviousResultId: previousResultIds.length > 0,
|
|
1270
|
-
elapsedMs: Date.now() - workspacePullStartedAt,
|
|
1271
|
-
server: state.serverId
|
|
1272
|
-
});
|
|
1273
|
-
armLateAnswerTelemetry({
|
|
1274
|
-
requestPromise: workspaceRequestPromise,
|
|
1275
|
-
scope: WORKSPACE_PULL_SCOPE,
|
|
1276
|
-
identifier: undefined,
|
|
1277
|
-
subject: WORKSPACE_PULL_SCOPE,
|
|
1278
|
-
requestStartedAt: workspacePullStartedAt,
|
|
1279
|
-
server: state.serverId,
|
|
1280
|
-
settlement: workspacePullSettlement
|
|
1281
|
-
});
|
|
1282
|
-
}
|
|
1283
|
-
recordPullFailure(state, "workspace/diagnostic", err);
|
|
1284
|
-
return;
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
export async function clientWaitForDiagnostics(state, filePath, timeoutMs, options = {}) {
|
|
1288
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
1289
|
-
const minVersion = options.minVersion;
|
|
1290
|
-
const pullSettleSource = options.pullSettleSource ?? "pull";
|
|
1291
|
-
const hasFreshDiagnostics = () => minVersion === undefined || diagnosticsVersionForPath(state, normalizedPath) > minVersion;
|
|
1292
|
-
const isVersionStale = () => {
|
|
1293
|
-
const cachedVersion = state.diagnosticDocVersions?.get(normalizedPath);
|
|
1294
|
-
if (cachedVersion === undefined)
|
|
1295
|
-
return false;
|
|
1296
|
-
const currentVersion = state.documentVersions?.get(normalizedPath);
|
|
1297
|
-
return currentVersion !== undefined && cachedVersion < currentVersion;
|
|
1298
|
-
};
|
|
1299
|
-
if (state.workspaceDiagnosticsSupport.mode === "pull") {
|
|
1300
|
-
const pullSettleStartedAt = Date.now();
|
|
1301
|
-
let outcome = await clientRequestPullDiagnostics(state, filePath, timeoutMs);
|
|
1302
|
-
if (outcome.status === "found") {
|
|
1303
|
-
logTypeScriptPullSettle(state, normalizedPath, Date.now() - pullSettleStartedAt, pullSettleSource);
|
|
1304
|
-
return;
|
|
1305
|
-
}
|
|
1306
|
-
let sawClean = outcome.status === "clean";
|
|
1307
|
-
const strategy = getStrategy(state.serverId, state.launchVariant);
|
|
1308
|
-
const retryBudgetMs = strategy.pullRetryBudgetMs > 0 ? Math.min(timeoutMs, strategy.pullRetryBudgetMs) : 0;
|
|
1309
|
-
const startedAt = Date.now();
|
|
1310
|
-
while (outcome.status !== "found" && Date.now() - startedAt < retryBudgetMs) {
|
|
1311
|
-
await new Promise((resolve) => setTimeout(resolve, PULL_DIAGNOSTICS_RETRY_INTERVAL_MS));
|
|
1312
|
-
outcome = await clientRequestPullDiagnostics(state, filePath, Math.max(0, retryBudgetMs - (Date.now() - startedAt)));
|
|
1313
|
-
if (outcome.status === "clean")
|
|
1314
|
-
sawClean = true;
|
|
1315
|
-
}
|
|
1316
|
-
if (options.pullOnly) {
|
|
1317
|
-
if (outcome.status === "found" || sawClean) {
|
|
1318
|
-
logTypeScriptPullSettle(state, normalizedPath, Date.now() - pullSettleStartedAt, pullSettleSource);
|
|
1319
|
-
}
|
|
1320
|
-
return;
|
|
1321
|
-
}
|
|
1322
|
-
if (outcome.status === "found" || sawClean) {
|
|
1323
|
-
logTypeScriptPullSettle(state, normalizedPath, Date.now() - pullSettleStartedAt, pullSettleSource);
|
|
1324
|
-
return;
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
if (hasFreshDiagnostics() && !isVersionStale() && getMergedDiagnosticsForPath(state, normalizedPath).length > 0) {
|
|
1328
|
-
return;
|
|
1329
|
-
}
|
|
1330
|
-
return new Promise((resolve) => {
|
|
1331
|
-
let debounceTimer;
|
|
1332
|
-
const onDiagnostics = (fp) => {
|
|
1333
|
-
if (normalizeMapKey(fp) !== normalizedPath)
|
|
1334
|
-
return;
|
|
1335
|
-
if (!hasFreshDiagnostics() || isVersionStale())
|
|
1336
|
-
return;
|
|
1337
|
-
if (debounceTimer)
|
|
1338
|
-
clearTimeout(debounceTimer);
|
|
1339
|
-
const strategy = getStrategy(state.serverId, state.launchVariant);
|
|
1340
|
-
const hit = state.pushDiagnosticTimestamps.get(normalizedPath);
|
|
1341
|
-
const timeSincePush = hit ? Date.now() - hit : 1 / 0;
|
|
1342
|
-
const remaining = Math.max(0, strategy.debounceMs - timeSincePush);
|
|
1343
|
-
debounceTimer = setTimeout(() => {
|
|
1344
|
-
state.diagnosticEmitter.off("diagnostics", onDiagnostics);
|
|
1345
|
-
clearTimeout(timeout);
|
|
1346
|
-
resolve();
|
|
1347
|
-
}, remaining);
|
|
1348
|
-
};
|
|
1349
|
-
state.diagnosticEmitter.on("diagnostics", onDiagnostics);
|
|
1350
|
-
const timeout = setTimeout(() => {
|
|
1351
|
-
if (debounceTimer)
|
|
1352
|
-
clearTimeout(debounceTimer);
|
|
1353
|
-
state.diagnosticEmitter.off("diagnostics", onDiagnostics);
|
|
1354
|
-
resolve();
|
|
1355
|
-
}, timeoutMs);
|
|
1356
|
-
});
|
|
1357
|
-
}
|
|
1358
|
-
export function handleNotifyExternalChange(state, filePath, type) {
|
|
1359
|
-
if (!isClientAlive(state))
|
|
1360
|
-
return;
|
|
1361
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
1362
|
-
const uri = state.openDocumentUris?.get(normalizedPath) ?? pathToFileURL(filePath).href;
|
|
1363
|
-
state.watchQueue.enqueue(uri, type);
|
|
1364
|
-
}
|
|
1365
|
-
async function sendDidSave(state, uri, content) {
|
|
1366
|
-
const save = state.saveOptions;
|
|
1367
|
-
if (!save)
|
|
1368
|
-
return;
|
|
1369
|
-
if (!isClientAlive(state))
|
|
1370
|
-
return;
|
|
1371
|
-
const limit = save.includeText ? exceedsLspSyncLimits(content) : { tooLarge: false, reason: "" };
|
|
1372
|
-
if (limit.tooLarge) {
|
|
1373
|
-
recordDegradationOnce({
|
|
1374
|
-
kind: "lsp-did-save-text-omitted",
|
|
1375
|
-
subject: state.serverId,
|
|
1376
|
-
reason: limit.reason
|
|
1377
|
-
});
|
|
1378
|
-
}
|
|
1379
|
-
await safeSendNotification(state.connection, "textDocument/didSave", {
|
|
1380
|
-
textDocument: { uri },
|
|
1381
|
-
...save.includeText && !limit.tooLarge ? { text: content } : {}
|
|
1382
|
-
});
|
|
1383
|
-
}
|
|
1384
|
-
async function handleNotifyOpenOnce(state, filePath, content, languageId, preserveDiagnostics = false, silent = false, coalescedCount = 0, saved = false) {
|
|
1385
|
-
if (!isClientAlive(state))
|
|
1386
|
-
return;
|
|
1387
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
1388
|
-
const uri = state.openDocumentUris?.get(normalizedPath) ?? pathToFileURL(filePath).href;
|
|
1389
|
-
if (state.openDocuments.has(normalizedPath) || state.pendingOpens.has(normalizedPath)) {
|
|
1390
|
-
const version = (state.documentVersions.get(normalizedPath) ?? 0) + 1;
|
|
1391
|
-
state.documentVersions.set(normalizedPath, version);
|
|
1392
|
-
if (!preserveDiagnostics) {
|
|
1393
|
-
clearDiagnosticsForPath(state, normalizedPath);
|
|
1394
|
-
}
|
|
1395
|
-
if (getStrategy(state.serverId, state.launchVariant).reopenOnResync) {
|
|
1396
|
-
await safeSendNotification(state.connection, "textDocument/didClose", {
|
|
1397
|
-
textDocument: { uri }
|
|
1398
|
-
});
|
|
1399
|
-
state.openDocuments.delete(normalizedPath);
|
|
1400
|
-
state.openDocumentUris?.delete(normalizedPath);
|
|
1401
|
-
state.documentVersions.set(normalizedPath, version);
|
|
1402
|
-
state.documentOpenedAt.set(normalizedPath, Date.now());
|
|
1403
|
-
state.diagnosticPublicationCounts.set(normalizedPath, 0);
|
|
1404
|
-
if (!isClientAlive(state))
|
|
1405
|
-
return;
|
|
1406
|
-
const reopenSent = await safeSendNotification(state.connection, "textDocument/didOpen", { textDocument: { uri, languageId, version, text: content } });
|
|
1407
|
-
if (reopenSent)
|
|
1408
|
-
recordSentContent(state, normalizedPath, version, content, coalescedCount);
|
|
1409
|
-
state.openDocuments.add(normalizedPath);
|
|
1410
|
-
state.openDocumentUris?.set(normalizedPath, uri);
|
|
1411
|
-
if (saved && reopenSent)
|
|
1412
|
-
await sendDidSave(state, uri, content);
|
|
1413
|
-
return;
|
|
1414
|
-
}
|
|
1415
|
-
const changeSent = await safeSendNotification(state.connection, "textDocument/didChange", {
|
|
1416
|
-
textDocument: { uri, version },
|
|
1417
|
-
contentChanges: buildContentChanges(state, normalizedPath, content)
|
|
1418
|
-
});
|
|
1419
|
-
if (changeSent)
|
|
1420
|
-
recordSentContent(state, normalizedPath, version, content, coalescedCount);
|
|
1421
|
-
if (saved && changeSent)
|
|
1422
|
-
await sendDidSave(state, uri, content);
|
|
1423
|
-
return;
|
|
1424
|
-
}
|
|
1425
|
-
state.pendingOpens.add(normalizedPath);
|
|
1426
|
-
state.documentVersions.set(normalizedPath, 0);
|
|
1427
|
-
state.documentOpenedAt.set(normalizedPath, Date.now());
|
|
1428
|
-
state.diagnosticPublicationCounts.set(normalizedPath, 0);
|
|
1429
|
-
clearDiagnosticsForPath(state, normalizedPath);
|
|
1430
|
-
if (!silent) {
|
|
1431
|
-
let fileExists = true;
|
|
1432
|
-
try {
|
|
1433
|
-
await access(filePath);
|
|
1434
|
-
} catch {
|
|
1435
|
-
fileExists = false;
|
|
1436
|
-
}
|
|
1437
|
-
state.watchQueue.enqueue(uri, fileExists ? 2 : 1);
|
|
1438
|
-
}
|
|
1439
|
-
if (!isClientAlive(state))
|
|
1440
|
-
return;
|
|
1441
|
-
const openSent = await safeSendNotification(state.connection, "textDocument/didOpen", { textDocument: { uri, languageId, version: 0, text: content } });
|
|
1442
|
-
if (openSent)
|
|
1443
|
-
recordSentContent(state, normalizedPath, 0, content, coalescedCount);
|
|
1444
|
-
state.pendingOpens.delete(normalizedPath);
|
|
1445
|
-
state.openDocuments.add(normalizedPath);
|
|
1446
|
-
state.closedDocuments?.delete(normalizedPath);
|
|
1447
|
-
state.openDocumentUris?.set(normalizedPath, uri);
|
|
1448
|
-
if (saved && openSent)
|
|
1449
|
-
await sendDidSave(state, uri, content);
|
|
1450
|
-
probeTsserverProjectIdentity({
|
|
1451
|
-
serverId: state.serverId,
|
|
1452
|
-
launchVariant: state.launchVariant,
|
|
1453
|
-
clientRoot: state.root,
|
|
1454
|
-
file: filePath,
|
|
1455
|
-
normalizedFile: normalizedPath,
|
|
1456
|
-
probedFiles: state.projectIdentityProbedFiles ?? (state.projectIdentityProbedFiles = new Set),
|
|
1457
|
-
commandChannel: {
|
|
1458
|
-
executeCommand: (command, args) => runReadOnlyServerCommand(state, command, args)
|
|
1459
|
-
}
|
|
1460
|
-
});
|
|
1461
|
-
}
|
|
1462
|
-
function enqueueDocumentNotify(state, normalizedPath, run, saved = false) {
|
|
1463
|
-
let queue = state.notifyChangeQueues.get(normalizedPath);
|
|
1464
|
-
if (!queue) {
|
|
1465
|
-
queue = { running: false };
|
|
1466
|
-
state.notifyChangeQueues.set(normalizedPath, queue);
|
|
1467
|
-
}
|
|
1468
|
-
return new Promise((resolve, reject) => {
|
|
1469
|
-
const previous = queue?.pending;
|
|
1470
|
-
const waiters = previous?.waiters ?? [];
|
|
1471
|
-
waiters.push({ resolve, reject });
|
|
1472
|
-
queue.pending = {
|
|
1473
|
-
run,
|
|
1474
|
-
waiters,
|
|
1475
|
-
coalescedCount: (previous?.coalescedCount ?? 0) + (previous ? 1 : 0),
|
|
1476
|
-
saved: saved || previous?.saved === true
|
|
1477
|
-
};
|
|
1478
|
-
if (queue.running)
|
|
1479
|
-
return;
|
|
1480
|
-
queue.running = true;
|
|
1481
|
-
Promise.resolve().then(async () => {
|
|
1482
|
-
try {
|
|
1483
|
-
for (;; ) {
|
|
1484
|
-
const next = queue.pending;
|
|
1485
|
-
if (!next)
|
|
1486
|
-
break;
|
|
1487
|
-
queue.pending = undefined;
|
|
1488
|
-
try {
|
|
1489
|
-
await next.run(next.coalescedCount, next.saved);
|
|
1490
|
-
for (const waiter of next.waiters)
|
|
1491
|
-
waiter.resolve();
|
|
1492
|
-
} catch (error) {
|
|
1493
|
-
for (const waiter of next.waiters)
|
|
1494
|
-
waiter.reject(error);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
} finally {
|
|
1498
|
-
queue.running = false;
|
|
1499
|
-
if (!queue.pending && state.notifyChangeQueues.get(normalizedPath) === queue) {
|
|
1500
|
-
state.notifyChangeQueues.delete(normalizedPath);
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
});
|
|
1504
|
-
});
|
|
1505
|
-
}
|
|
1506
|
-
function cancelDocumentNotifyQueues(state) {
|
|
1507
|
-
for (const queue of state.notifyChangeQueues.values()) {
|
|
1508
|
-
for (const waiter of queue.pending?.waiters ?? [])
|
|
1509
|
-
waiter.resolve();
|
|
1510
|
-
queue.pending = undefined;
|
|
1511
|
-
}
|
|
1512
|
-
state.notifyChangeQueues.clear();
|
|
1513
|
-
}
|
|
1514
|
-
export function handleNotifyOpen(state, filePath, content, languageId, preserveDiagnostics = false, silent = false, saved = false) {
|
|
1515
|
-
if (!isClientAlive(state))
|
|
1516
|
-
return Promise.resolve();
|
|
1517
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
1518
|
-
return enqueueDocumentNotify(state, normalizedPath, (coalescedCount, queuedSaved) => handleNotifyOpenOnce(state, filePath, content, languageId, preserveDiagnostics, silent, coalescedCount, queuedSaved), saved);
|
|
1519
|
-
}
|
|
1520
|
-
async function handleNotifyChangeOnce(state, filePath, content, normalizedPath, coalescedCount = 0) {
|
|
1521
|
-
if (!isClientAlive(state))
|
|
1522
|
-
return;
|
|
1523
|
-
const uri = state.openDocumentUris?.get(normalizedPath) ?? pathToFileURL(filePath).href;
|
|
1524
|
-
if (!state.openDocuments.has(normalizedPath)) {
|
|
1525
|
-
const fallbackOpenSent = await safeSendNotification(state.connection, "textDocument/didOpen", {
|
|
1526
|
-
textDocument: {
|
|
1527
|
-
uri,
|
|
1528
|
-
languageId: "plaintext",
|
|
1529
|
-
version: 0,
|
|
1530
|
-
text: content
|
|
1531
|
-
}
|
|
1532
|
-
});
|
|
1533
|
-
state.documentVersions.set(normalizedPath, 0);
|
|
1534
|
-
state.documentOpenedAt.set(normalizedPath, Date.now());
|
|
1535
|
-
state.diagnosticPublicationCounts.set(normalizedPath, 0);
|
|
1536
|
-
if (fallbackOpenSent)
|
|
1537
|
-
recordSentContent(state, normalizedPath, 0, content, coalescedCount);
|
|
1538
|
-
state.openDocuments.add(normalizedPath);
|
|
1539
|
-
state.openDocumentUris?.set(normalizedPath, uri);
|
|
1540
|
-
return;
|
|
1541
|
-
}
|
|
1542
|
-
const version = (state.documentVersions.get(normalizedPath) ?? 0) + 1;
|
|
1543
|
-
state.documentVersions.set(normalizedPath, version);
|
|
1544
|
-
clearDiagnosticsForPath(state, normalizedPath);
|
|
1545
|
-
const changeSent = await safeSendNotification(state.connection, "textDocument/didChange", {
|
|
1546
|
-
textDocument: { uri, version },
|
|
1547
|
-
contentChanges: buildContentChanges(state, normalizedPath, content)
|
|
1548
|
-
});
|
|
1549
|
-
if (changeSent)
|
|
1550
|
-
recordSentContent(state, normalizedPath, version, content, coalescedCount);
|
|
1551
|
-
}
|
|
1552
|
-
export function handleNotifyChange(state, filePath, content) {
|
|
1553
|
-
if (!isClientAlive(state))
|
|
1554
|
-
return Promise.resolve();
|
|
1555
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
1556
|
-
return enqueueDocumentNotify(state, normalizedPath, (coalescedCount) => handleNotifyChangeOnce(state, filePath, content, normalizedPath, coalescedCount));
|
|
1557
|
-
}
|
|
1558
|
-
export async function closeDocument(state, filePath) {
|
|
1559
|
-
if (!isClientAlive(state))
|
|
1560
|
-
return;
|
|
1561
|
-
const normalizedPath = normalizeMapKey(filePath);
|
|
1562
|
-
if (!state.openDocuments.has(normalizedPath))
|
|
1563
|
-
return;
|
|
1564
|
-
await safeSendNotification(state.connection, "textDocument/didClose", {
|
|
1565
|
-
textDocument: {
|
|
1566
|
-
uri: state.openDocumentUris?.get(normalizedPath) ?? pathToFileURL(filePath).href
|
|
1567
|
-
}
|
|
1568
|
-
});
|
|
1569
|
-
state.openDocuments.delete(normalizedPath);
|
|
1570
|
-
state.closedDocuments?.add(normalizedPath);
|
|
1571
|
-
state.openDocumentUris?.delete(normalizedPath);
|
|
1572
|
-
state.documentVersions.delete(normalizedPath);
|
|
1573
|
-
state.documentOpenedAt.delete(normalizedPath);
|
|
1574
|
-
state.diagnosticPublicationCounts.delete(normalizedPath);
|
|
1575
|
-
state.projectIdentityProbedFiles?.delete(normalizedPath);
|
|
1576
|
-
const retained = state.documentContentHashes.get(normalizedPath);
|
|
1577
|
-
if (retained?.text !== undefined) {
|
|
1578
|
-
state.incrementalTextRetainedEntries = Math.max(0, (state.incrementalTextRetainedEntries ?? 1) - 1);
|
|
1579
|
-
state.incrementalTextRetainedBytes = Math.max(0, (state.incrementalTextRetainedBytes ?? retained.text.length * 2) - retained.text.length * 2);
|
|
1580
|
-
state.incrementalTextBearingPaths?.delete(normalizedPath);
|
|
1581
|
-
}
|
|
1582
|
-
state.documentContentHashes.delete(normalizedPath);
|
|
1583
|
-
clearDiagnosticsForPath(state, normalizedPath);
|
|
1584
|
-
}
|
|
1585
|
-
function isAtLeastAsAggressiveShutdown(existing, requested) {
|
|
1586
|
-
return !!existing.fast >= !!requested.fast && !!existing.processExiting >= !!requested.processExiting;
|
|
1587
|
-
}
|
|
1588
|
-
export function clientShutdown(state, options = {}) {
|
|
1589
|
-
if (state.shutdownPromise && state.shutdownOptions && isAtLeastAsAggressiveShutdown(state.shutdownOptions, options)) {
|
|
1590
|
-
return state.shutdownPromise;
|
|
1591
|
-
}
|
|
1592
|
-
state.shutdownOptions = options;
|
|
1593
|
-
const attempt = clientShutdownOnce(state, options);
|
|
1594
|
-
state.shutdownPromise = attempt;
|
|
1595
|
-
attempt.catch(() => {
|
|
1596
|
-
if (state.shutdownPromise === attempt) {
|
|
1597
|
-
state.shutdownPromise = undefined;
|
|
1598
|
-
state.shutdownOptions = undefined;
|
|
1599
|
-
}
|
|
1600
|
-
});
|
|
1601
|
-
return attempt;
|
|
1602
|
-
}
|
|
1603
|
-
async function clientShutdownOnce(state, options) {
|
|
1604
|
-
const shutdownStart = Date.now();
|
|
1605
|
-
state.shutdownRequested = true;
|
|
1606
|
-
state.isConnected = false;
|
|
1607
|
-
state.isDestroyed = true;
|
|
1608
|
-
for (const timer of state.pendingDiagnostics.values()) {
|
|
1609
|
-
clearTimeout(timer);
|
|
1610
|
-
}
|
|
1611
|
-
state.pendingDiagnostics.clear();
|
|
1612
|
-
state.pendingOpens.clear();
|
|
1613
|
-
state.openDocuments.clear();
|
|
1614
|
-
state.openDocumentUris?.clear();
|
|
1615
|
-
cancelDocumentNotifyQueues(state);
|
|
1616
|
-
state.projectIdentityProbedFiles?.clear();
|
|
1617
|
-
state.watchQueue?.cancel();
|
|
1618
|
-
state.diagnosticEmitter.removeAllListeners();
|
|
1619
|
-
let shutdownRequestTimedOut = false;
|
|
1620
|
-
let exitNotifyTimedOut = false;
|
|
1621
|
-
let shutdownRequestUndelivered = false;
|
|
1622
|
-
let exitNotifyUndelivered = false;
|
|
1623
|
-
try {
|
|
1624
|
-
if (!options.fast) {
|
|
1625
|
-
try {
|
|
1626
|
-
const shutdownAck = await withTimeout(safeSendRequest(state.connection, "shutdown", {}), SHUTDOWN_REQUEST_TIMEOUT_MS);
|
|
1627
|
-
if (shutdownAck === undefined)
|
|
1628
|
-
shutdownRequestUndelivered = true;
|
|
1629
|
-
} catch (err) {
|
|
1630
|
-
if (isShutdownTimeoutError(err, SHUTDOWN_REQUEST_TIMEOUT_MS)) {
|
|
1631
|
-
shutdownRequestTimedOut = true;
|
|
1632
|
-
} else {
|
|
1633
|
-
shutdownRequestUndelivered = true;
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
try {
|
|
1637
|
-
const exitSent = await withTimeout(safeSendNotification(state.connection, "exit", {}), EXIT_NOTIFY_TIMEOUT_MS);
|
|
1638
|
-
if (exitSent === false)
|
|
1639
|
-
exitNotifyUndelivered = true;
|
|
1640
|
-
} catch (err) {
|
|
1641
|
-
if (isShutdownTimeoutError(err, EXIT_NOTIFY_TIMEOUT_MS)) {
|
|
1642
|
-
exitNotifyTimedOut = true;
|
|
1643
|
-
} else {
|
|
1644
|
-
exitNotifyUndelivered = true;
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
} finally {
|
|
1649
|
-
disposeClientConnection(state);
|
|
1650
|
-
const pid = state.lspProcess.pid;
|
|
1651
|
-
logLatency({
|
|
1652
|
-
type: "phase",
|
|
1653
|
-
phase: "lsp_client_shutdown",
|
|
1654
|
-
filePath: state.root,
|
|
1655
|
-
durationMs: Date.now() - shutdownStart,
|
|
1656
|
-
metadata: {
|
|
1657
|
-
serverId: state.serverId,
|
|
1658
|
-
pid,
|
|
1659
|
-
fast: !!options.fast,
|
|
1660
|
-
processExiting: !!options.processExiting,
|
|
1661
|
-
shutdownRequestTimedOut,
|
|
1662
|
-
exitNotifyTimedOut,
|
|
1663
|
-
shutdownRequestUndelivered,
|
|
1664
|
-
exitNotifyUndelivered,
|
|
1665
|
-
shutdownOutcome: options.fast ? "fast" : shutdownRequestTimedOut || exitNotifyTimedOut || shutdownRequestUndelivered || exitNotifyUndelivered ? "forced" : "graceful"
|
|
1666
|
-
}
|
|
1667
|
-
});
|
|
1668
|
-
removeLspChild(pid, extractSpawnMarker(state.lspProcess.args)).catch((err) => {
|
|
1669
|
-
logLatency({
|
|
1670
|
-
type: "phase",
|
|
1671
|
-
phase: "lsp_registry_write_failed",
|
|
1672
|
-
filePath: "",
|
|
1673
|
-
durationMs: 0,
|
|
1674
|
-
metadata: { op: "remove", pid, error: String(err) }
|
|
1675
|
-
});
|
|
1676
|
-
});
|
|
1677
|
-
await killProcessTree(state.lspProcess.process, pid, options);
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
async function toWirePosition(state, filePath, line, character) {
|
|
1681
|
-
if (state.positionEncoding === "utf-16")
|
|
1682
|
-
return { line, character };
|
|
1683
|
-
try {
|
|
1684
|
-
const content = await readFile(filePath, "utf8");
|
|
1685
|
-
return {
|
|
1686
|
-
line,
|
|
1687
|
-
character: convertCharacterOffset(state.positionEncoding, lineTextAt(content, line), character)
|
|
1688
|
-
};
|
|
1689
|
-
} catch {
|
|
1690
|
-
return { line, character };
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
function navStaleDropEnabled() {
|
|
1694
|
-
return process.env.DM_LENS_LSP_NAV_STALE_DROP !== "0";
|
|
1695
|
-
}
|
|
1696
|
-
export async function navRequest(state, method, params, staleCheckPath, timeoutMs = NAV_REQUEST_TIMEOUT_MS, signal = getAmbientAbortSignal()) {
|
|
1697
|
-
if (!isClientAlive(state))
|
|
1698
|
-
return null;
|
|
1699
|
-
const normalizedPath = staleCheckPath !== undefined ? normalizeMapKey(staleCheckPath) : undefined;
|
|
1700
|
-
const requestVersion = normalizedPath !== undefined ? state.documentVersions.get(normalizedPath) : undefined;
|
|
1701
|
-
const requestStartedAt = Date.now();
|
|
1702
|
-
const requestPromise = safeSendRequest(state.connection, method, params, signal);
|
|
1703
|
-
const result = await withTimeout(requestPromise, timeoutMs).catch((err) => {
|
|
1704
|
-
if (isNavTimeoutError(err, timeoutMs)) {
|
|
1705
|
-
recordNavTimeoutTelemetry({
|
|
1706
|
-
method,
|
|
1707
|
-
scope: normalizedPath,
|
|
1708
|
-
budgetMs: timeoutMs,
|
|
1709
|
-
elapsedMs: Date.now() - requestStartedAt
|
|
1710
|
-
});
|
|
1711
|
-
armNavLateAnswerTelemetry({
|
|
1712
|
-
requestPromise,
|
|
1713
|
-
method,
|
|
1714
|
-
scope: normalizedPath,
|
|
1715
|
-
requestStartedAt
|
|
1716
|
-
});
|
|
1717
|
-
return;
|
|
1718
|
-
}
|
|
1719
|
-
if (err instanceof Error && err.message.startsWith("Timeout after")) {
|
|
1720
|
-
return;
|
|
1721
|
-
}
|
|
1722
|
-
throw err;
|
|
1723
|
-
});
|
|
1724
|
-
if (normalizedPath !== undefined && requestVersion !== undefined && navStaleDropEnabled()) {
|
|
1725
|
-
const currentVersion = state.documentVersions.get(normalizedPath);
|
|
1726
|
-
if (currentVersion !== undefined && currentVersion > requestVersion) {
|
|
1727
|
-
return;
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
return result;
|
|
1731
|
-
}
|
|
1732
|
-
function isNavTimeoutError(err, timeoutMs) {
|
|
1733
|
-
return err instanceof Error && err.message === `Timeout after ${timeoutMs}ms`;
|
|
1734
|
-
}
|
|
1735
|
-
function isShutdownTimeoutError(err, timeoutMs) {
|
|
1736
|
-
return err instanceof Error && err.message === `Timeout after ${timeoutMs}ms`;
|
|
1737
|
-
}
|
|
1738
|
-
const NAV_REQUEST_NO_PATH_SCOPE = "*no-path*";
|
|
1739
|
-
function recordNavTimeoutTelemetry(args) {
|
|
1740
|
-
emitBounded("lsp_nav_request_timeout", `${args.method}:${args.scope ?? NAV_REQUEST_NO_PATH_SCOPE}`, {
|
|
1741
|
-
type: "phase",
|
|
1742
|
-
filePath: args.scope ?? NAV_REQUEST_NO_PATH_SCOPE,
|
|
1743
|
-
durationMs: args.elapsedMs,
|
|
1744
|
-
metadata: { method: args.method, effectiveBudgetMs: args.budgetMs }
|
|
1745
|
-
}, {
|
|
1746
|
-
ledgerKind: "lsp-nav-request-timeout",
|
|
1747
|
-
risingEdgePer: "identity",
|
|
1748
|
-
reason: `timeout budget=${args.budgetMs}ms elapsed=${args.elapsedMs}ms`
|
|
1749
|
-
});
|
|
1750
|
-
}
|
|
1751
|
-
function armNavLateAnswerTelemetry(args) {
|
|
1752
|
-
args.requestPromise.then(() => {
|
|
1753
|
-
try {
|
|
1754
|
-
const elapsedMs = Date.now() - args.requestStartedAt;
|
|
1755
|
-
emitBounded("lsp_nav_late_answer_discarded", `${args.method}:${args.scope ?? NAV_REQUEST_NO_PATH_SCOPE}`, {
|
|
1756
|
-
type: "phase",
|
|
1757
|
-
filePath: args.scope ?? NAV_REQUEST_NO_PATH_SCOPE,
|
|
1758
|
-
durationMs: elapsedMs,
|
|
1759
|
-
metadata: { method: args.method }
|
|
1760
|
-
}, {
|
|
1761
|
-
ledgerKind: "lsp-nav-late-answer",
|
|
1762
|
-
risingEdgePer: "identity",
|
|
1763
|
-
reason: `late nav answer discarded after ${elapsedMs}ms`
|
|
1764
|
-
});
|
|
1765
|
-
} catch {}
|
|
1766
|
-
}, () => {});
|
|
1767
|
-
}
|
|
1768
|
-
function chooseLivenessProbe(state) {
|
|
1769
|
-
if (state.operationSupport?.workspaceSymbol === true) {
|
|
1770
|
-
return {
|
|
1771
|
-
method: "workspace/symbol",
|
|
1772
|
-
params: { query: LIVENESS_PING_QUERY }
|
|
1773
|
-
};
|
|
1774
|
-
}
|
|
1775
|
-
const openUri = firstOpenDocumentUri(state);
|
|
1776
|
-
if (openUri === undefined)
|
|
1777
|
-
return;
|
|
1778
|
-
if (state.operationSupport?.documentSymbol === true) {
|
|
1779
|
-
return {
|
|
1780
|
-
method: "textDocument/documentSymbol",
|
|
1781
|
-
params: { textDocument: { uri: openUri } }
|
|
1782
|
-
};
|
|
1783
|
-
}
|
|
1784
|
-
if (state.operationSupport?.hover === true) {
|
|
1785
|
-
return {
|
|
1786
|
-
method: "textDocument/hover",
|
|
1787
|
-
params: {
|
|
1788
|
-
textDocument: { uri: openUri },
|
|
1789
|
-
position: { line: 0, character: 0 }
|
|
1790
|
-
}
|
|
1791
|
-
};
|
|
1792
|
-
}
|
|
1793
|
-
return;
|
|
1794
|
-
}
|
|
1795
|
-
function firstOpenDocumentUri(state) {
|
|
1796
|
-
for (const normalizedPath of state.openDocuments) {
|
|
1797
|
-
const uri = state.openDocumentUris?.get(normalizedPath);
|
|
1798
|
-
if (uri !== undefined)
|
|
1799
|
-
return uri;
|
|
1800
|
-
}
|
|
1801
|
-
return;
|
|
1802
|
-
}
|
|
1803
|
-
async function clientPingLiveness(state, timeoutMs = LIVENESS_PING_TIMEOUT_MS) {
|
|
1804
|
-
if (!isClientAlive(state))
|
|
1805
|
-
return false;
|
|
1806
|
-
const probe = chooseLivenessProbe(state);
|
|
1807
|
-
if (probe === undefined) {
|
|
1808
|
-
try {
|
|
1809
|
-
incrementDegradationCount({
|
|
1810
|
-
kind: "lsp-liveness-probe-unsupported",
|
|
1811
|
-
subject: state.serverId,
|
|
1812
|
-
reason: "no advertised request method to probe with (no " + "workspaceSymbolProvider, and no open document with " + "documentSymbolProvider or hoverProvider); liveness from " + "process and connection state only"
|
|
1813
|
-
});
|
|
1814
|
-
} catch {}
|
|
1815
|
-
return isClientAlive(state);
|
|
1816
|
-
}
|
|
1817
|
-
try {
|
|
1818
|
-
await withTimeout(safeSendRequest(state.connection, probe.method, probe.params), timeoutMs);
|
|
1819
|
-
} catch (err) {
|
|
1820
|
-
if (err instanceof Error && err.message.startsWith("Timeout after")) {
|
|
1821
|
-
return false;
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
return isClientAlive(state);
|
|
1825
|
-
}
|
|
1826
|
-
function syncMutationContextSlot(state) {
|
|
1827
|
-
state.activeMutationContext = state.activeMutationDepth === 1 ? state.mutationContextOwner : undefined;
|
|
1828
|
-
}
|
|
1829
|
-
export async function runServerCommand(state, command, args, timeoutMs = EXECUTE_COMMAND_TIMEOUT_MS, mutationContext) {
|
|
1830
|
-
if (!isClientAlive(state)) {
|
|
1831
|
-
return { executed: false, reason: "lsp client not alive" };
|
|
1832
|
-
}
|
|
1833
|
-
if (!state.advertisedCommands.has(command)) {
|
|
1834
|
-
return {
|
|
1835
|
-
executed: false,
|
|
1836
|
-
reason: `command "${command}" is not advertised by the ${state.serverId} server`
|
|
1837
|
-
};
|
|
1838
|
-
}
|
|
1839
|
-
state.serverEditsAllowed += 1;
|
|
1840
|
-
state.activeMutationDepth = (state.activeMutationDepth ?? 0) + 1;
|
|
1841
|
-
const isMutationContextOwner = state.activeMutationDepth === 1;
|
|
1842
|
-
if (isMutationContextOwner) {
|
|
1843
|
-
state.mutationContextOwner = mutationContext;
|
|
1844
|
-
}
|
|
1845
|
-
syncMutationContextSlot(state);
|
|
1846
|
-
try {
|
|
1847
|
-
let result;
|
|
1848
|
-
try {
|
|
1849
|
-
result = await withTimeout(safeSendRequest(state.connection, "workspace/executeCommand", {
|
|
1850
|
-
command,
|
|
1851
|
-
arguments: args ?? []
|
|
1852
|
-
}), timeoutMs);
|
|
1853
|
-
} catch (err) {
|
|
1854
|
-
if (err instanceof Error && err.message.startsWith("Timeout after")) {
|
|
1855
|
-
return {
|
|
1856
|
-
executed: false,
|
|
1857
|
-
reason: `workspace/executeCommand timed out after ${timeoutMs}ms — the command may still be applying server-side`
|
|
1858
|
-
};
|
|
1859
|
-
}
|
|
1860
|
-
throw err;
|
|
1861
|
-
}
|
|
1862
|
-
return { executed: true, result };
|
|
1863
|
-
} finally {
|
|
1864
|
-
state.serverEditsAllowed -= 1;
|
|
1865
|
-
state.activeMutationDepth = Math.max(0, (state.activeMutationDepth ?? 0) - 1);
|
|
1866
|
-
if (isMutationContextOwner) {
|
|
1867
|
-
state.mutationContextOwner = undefined;
|
|
1868
|
-
}
|
|
1869
|
-
syncMutationContextSlot(state);
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
export async function runReadOnlyServerCommand(state, command, args, timeoutMs = PROBE_COMMAND_TIMEOUT_MS) {
|
|
1873
|
-
if (!isClientAlive(state)) {
|
|
1874
|
-
return { executed: false, reason: "lsp client not alive" };
|
|
1875
|
-
}
|
|
1876
|
-
if (!state.advertisedCommands.has(command)) {
|
|
1877
|
-
return {
|
|
1878
|
-
executed: false,
|
|
1879
|
-
reason: `command "${command}" is not advertised by the ${state.serverId} server`
|
|
1880
|
-
};
|
|
1881
|
-
}
|
|
1882
|
-
try {
|
|
1883
|
-
const result = await withTimeout(safeSendRequest(state.connection, "workspace/executeCommand", {
|
|
1884
|
-
command,
|
|
1885
|
-
arguments: args ?? []
|
|
1886
|
-
}), timeoutMs);
|
|
1887
|
-
return { executed: true, result };
|
|
1888
|
-
} catch (err) {
|
|
1889
|
-
if (err instanceof Error && err.message.startsWith("Timeout after")) {
|
|
1890
|
-
return {
|
|
1891
|
-
executed: false,
|
|
1892
|
-
reason: `workspace/executeCommand timed out after ${timeoutMs}ms`
|
|
1893
|
-
};
|
|
1894
|
-
}
|
|
1895
|
-
throw err;
|
|
1896
|
-
}
|
|
1897
|
-
}
|
|
1898
|
-
function validateWorkspaceEditVersions(state, edit) {
|
|
1899
|
-
for (const change of edit.documentChanges ?? []) {
|
|
1900
|
-
if (typeof change !== "object" || change === null || !("textDocument" in change))
|
|
1901
|
-
continue;
|
|
1902
|
-
const textDocument = change.textDocument;
|
|
1903
|
-
if (!textDocument || typeof textDocument.uri !== "string" || textDocument.version == null)
|
|
1904
|
-
continue;
|
|
1905
|
-
const current = state.documentVersions.get(normalizeMapKey(uriToPath(textDocument.uri)));
|
|
1906
|
-
if (current === undefined || current !== textDocument.version) {
|
|
1907
|
-
throw new Error(`stale workspace edit document version for ${textDocument.uri}`);
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
}
|
|
1911
|
-
function stripDocumentVersions(edit) {
|
|
1912
|
-
if (!Array.isArray(edit.documentChanges))
|
|
1913
|
-
return edit;
|
|
1914
|
-
const documentChanges = edit.documentChanges.map((change) => {
|
|
1915
|
-
if (typeof change === "object" && change !== null && "textDocument" in change && "edits" in change) {
|
|
1916
|
-
const textDocument = change.textDocument;
|
|
1917
|
-
if (textDocument && typeof textDocument.version === "number") {
|
|
1918
|
-
return {
|
|
1919
|
-
...change,
|
|
1920
|
-
textDocument: { ...textDocument, version: null }
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
return change;
|
|
1925
|
-
});
|
|
1926
|
-
return { ...edit, documentChanges };
|
|
1927
|
-
}
|
|
1928
|
-
export async function normalizeClientWorkspaceEdit(state, edit) {
|
|
1929
|
-
validateWorkspaceEditVersions(state, edit);
|
|
1930
|
-
const normalized = await normalizeWorkspaceEditToUtf16(edit, state.positionEncoding, state.root);
|
|
1931
|
-
return stripDocumentVersions(normalized);
|
|
1932
|
-
}
|
|
1933
|
-
async function resolveCodeActionBestEffort(state, action) {
|
|
1934
|
-
if (!isClientAlive(state))
|
|
1935
|
-
return action;
|
|
1936
|
-
if (action.edit) {
|
|
1937
|
-
return {
|
|
1938
|
-
...action,
|
|
1939
|
-
edit: await normalizeClientWorkspaceEdit(state, action.edit)
|
|
1940
|
-
};
|
|
1941
|
-
}
|
|
1942
|
-
if (!state.operationSupport.codeActionResolve) {
|
|
1943
|
-
recordDegradationOnce({
|
|
1944
|
-
kind: "lsp-capability-skip",
|
|
1945
|
-
subject: `${state.serverId}:codeAction/resolve`,
|
|
1946
|
-
reason: "server did not advertise codeActionProvider.resolveProvider"
|
|
1947
|
-
});
|
|
1948
|
-
return action;
|
|
1949
|
-
}
|
|
1950
|
-
let resolved;
|
|
1951
|
-
try {
|
|
1952
|
-
resolved = await withTimeout(safeSendRequest(state.connection, "codeAction/resolve", action), NAV_REQUEST_TIMEOUT_MS);
|
|
1953
|
-
} catch {
|
|
1954
|
-
return action;
|
|
1955
|
-
}
|
|
1956
|
-
if (!resolved || typeof resolved !== "object")
|
|
1957
|
-
return action;
|
|
1958
|
-
const merged = { ...action, ...resolved };
|
|
1959
|
-
return merged.edit ? {
|
|
1960
|
-
...merged,
|
|
1961
|
-
edit: await normalizeClientWorkspaceEdit(state, merged.edit)
|
|
1962
|
-
} : merged;
|
|
1963
|
-
}
|
|
1964
|
-
export async function createLSPClient(options) {
|
|
1965
|
-
installCrashGuard();
|
|
1966
|
-
const { serverId, process: lspProcess, root, sessionCwd, initialization, initializeTimeoutMs = INITIALIZE_TIMEOUT_MS, launchVariant, onDiagnosticsPublished } = options;
|
|
1967
|
-
recordLspChild({
|
|
1968
|
-
pid: lspProcess.pid,
|
|
1969
|
-
serverId,
|
|
1970
|
-
command: lspProcess.command,
|
|
1971
|
-
marker: extractSpawnMarker(lspProcess.args),
|
|
1972
|
-
sessionIdentity: {
|
|
1973
|
-
projectRoot: sessionCwd ?? process.cwd(),
|
|
1974
|
-
rootSource: sessionCwd ? "service-cwd" : "lsp-fallback",
|
|
1975
|
-
startedAt: new Date(workspaceDiagnosticsCacheSessionStart()).toISOString()
|
|
1976
|
-
}
|
|
1977
|
-
}).catch((err) => {
|
|
1978
|
-
logLatency({
|
|
1979
|
-
type: "phase",
|
|
1980
|
-
phase: "lsp_registry_write_failed",
|
|
1981
|
-
filePath: "",
|
|
1982
|
-
durationMs: 0,
|
|
1983
|
-
metadata: { op: "record", pid: lspProcess.pid, error: String(err) }
|
|
1984
|
-
});
|
|
1985
|
-
});
|
|
1986
|
-
const startupState = {
|
|
1987
|
-
exitCode: null,
|
|
1988
|
-
exitSignal: null,
|
|
1989
|
-
closeCode: null,
|
|
1990
|
-
closeSignal: null,
|
|
1991
|
-
stderr: ""
|
|
1992
|
-
};
|
|
1993
|
-
const stderrRing = [];
|
|
1994
|
-
const MAX_STDERR_LINES = 100;
|
|
1995
|
-
const onStderr = (chunk) => {
|
|
1996
|
-
stderrRing.push(chunk.toString());
|
|
1997
|
-
if (stderrRing.length > MAX_STDERR_LINES)
|
|
1998
|
-
stderrRing.shift();
|
|
1999
|
-
if (startupState.stderr.length < 4096) {
|
|
2000
|
-
startupState.stderr += chunk.toString();
|
|
2001
|
-
}
|
|
2002
|
-
};
|
|
2003
|
-
const recentStderr = (lines = 10) => stderrRing.slice(-lines).join("").trim();
|
|
2004
|
-
const checkProcessAlive = () => {
|
|
2005
|
-
const exited = lspProcess.process.exitCode;
|
|
2006
|
-
if (exited !== null) {
|
|
2007
|
-
const tail = recentStderr(20);
|
|
2008
|
-
return `LSP server ${serverId} exited with code ${exited}${tail ? `. stderr: ${tail}` : ""}`;
|
|
2009
|
-
}
|
|
2010
|
-
if (lspProcess.process.killed) {
|
|
2011
|
-
return `LSP server ${serverId} was killed`;
|
|
2012
|
-
}
|
|
2013
|
-
return;
|
|
2014
|
-
};
|
|
2015
|
-
const onProcessExit = (code, signal) => {
|
|
2016
|
-
startupState.exitCode = code;
|
|
2017
|
-
startupState.exitSignal = signal;
|
|
2018
|
-
};
|
|
2019
|
-
const onProcessClose = (code, signal) => {
|
|
2020
|
-
startupState.closeCode = code;
|
|
2021
|
-
startupState.closeSignal = signal;
|
|
2022
|
-
};
|
|
2023
|
-
lspProcess.stderr.on("data", onStderr);
|
|
2024
|
-
lspProcess.process.on("exit", onProcessExit);
|
|
2025
|
-
lspProcess.process.on("close", onProcessClose);
|
|
2026
|
-
const streamErrorHandler = (_label) => (err) => {
|
|
2027
|
-
if (err.code === "ERR_STREAM_DESTROYED" || err.code === "ERR_STREAM_WRITE_AFTER_END" || err.code === "EPIPE" || err.code === "ECONNRESET")
|
|
2028
|
-
return;
|
|
2029
|
-
};
|
|
2030
|
-
lspProcess.stdin.on("error", streamErrorHandler("stdin"));
|
|
2031
|
-
lspProcess.stdout.on("error", streamErrorHandler("stdout"));
|
|
2032
|
-
lspProcess.stderr.on("error", streamErrorHandler("stderr"));
|
|
2033
|
-
const connection = createMessageConnection(new StreamMessageReader(lspProcess.stdout), new StreamMessageWriter(lspProcess.stdin));
|
|
2034
|
-
const diagnosticEmitter = new EventEmitter;
|
|
2035
|
-
diagnosticEmitter.setMaxListeners(50);
|
|
2036
|
-
const state = {
|
|
2037
|
-
isConnected: true,
|
|
2038
|
-
isDestroyed: false,
|
|
2039
|
-
shutdownRequested: false,
|
|
2040
|
-
shutdownPromise: undefined,
|
|
2041
|
-
shutdownOptions: undefined,
|
|
2042
|
-
exitedAt: undefined,
|
|
2043
|
-
connectionDisposed: false,
|
|
2044
|
-
lastError: undefined,
|
|
2045
|
-
connection,
|
|
2046
|
-
pushDiagnostics: new Map,
|
|
2047
|
-
pushDiagnosticTimestamps: new Map,
|
|
2048
|
-
documentPullDiagnostics: new Map,
|
|
2049
|
-
documentPullDiagnosticTimestamps: new Map,
|
|
2050
|
-
pullFailureHistory: [],
|
|
2051
|
-
pendingDiagnostics: new Map,
|
|
2052
|
-
diagnosticPublicationCounts: new Map,
|
|
2053
|
-
documentOpenedAt: new Map,
|
|
2054
|
-
diagnosticEmitter,
|
|
2055
|
-
diagnosticsVersion: 0,
|
|
2056
|
-
diagnosticsVersionsByPath: new Map,
|
|
2057
|
-
documentVersions: new Map,
|
|
2058
|
-
notifyChangeQueues: new Map,
|
|
2059
|
-
diagnosticDocVersions: new Map,
|
|
2060
|
-
documentContentHashes: new Map,
|
|
2061
|
-
incrementalTextRetainedEntries: 0,
|
|
2062
|
-
incrementalTextBearingPaths: new Set,
|
|
2063
|
-
incrementalTextRetainedBytes: 0,
|
|
2064
|
-
diagnosticBindings: new Map,
|
|
2065
|
-
pullResultIds: new Map,
|
|
2066
|
-
documentPullDiagnosticsBySource: new Map,
|
|
2067
|
-
pullGenerations: new Map,
|
|
2068
|
-
pullRequestSequences: new Map,
|
|
2069
|
-
workspacePullResultCache: new Map,
|
|
2070
|
-
openDocuments: new Set,
|
|
2071
|
-
emptyFirstPublishHoldSpent: false,
|
|
2072
|
-
closedDocuments: new Set,
|
|
2073
|
-
openDocumentUris: new Map,
|
|
2074
|
-
pendingOpens: new Set,
|
|
2075
|
-
projectIdentityProbedFiles: new Set,
|
|
2076
|
-
workspaceDiagnosticsSupport: undefined,
|
|
2077
|
-
operationSupport: undefined,
|
|
2078
|
-
staticDiagnosticsMode: "push-only",
|
|
2079
|
-
positionEncoding: "utf-16",
|
|
2080
|
-
syncKind: TEXT_DOCUMENT_SYNC_KIND_FULL,
|
|
2081
|
-
dynamicRegistrations: new Map,
|
|
2082
|
-
advertisedCommands: new Set,
|
|
2083
|
-
serverEditsAllowed: 0,
|
|
2084
|
-
activeMutationDepth: 0,
|
|
2085
|
-
serverId,
|
|
2086
|
-
launchVariant,
|
|
2087
|
-
root,
|
|
2088
|
-
lspProcess,
|
|
2089
|
-
watchQueue: undefined
|
|
2090
|
-
};
|
|
2091
|
-
activeLspClientSet().add(state);
|
|
2092
|
-
state.watchQueue = new WatchedFilesQueue((changes) => {
|
|
2093
|
-
if (!isClientAlive(state))
|
|
2094
|
-
return;
|
|
2095
|
-
safeSendNotification(state.connection, "workspace/didChangeWatchedFiles", { changes });
|
|
2096
|
-
});
|
|
2097
|
-
setupIncomingHandlers(state, initialization, onDiagnosticsPublished);
|
|
2098
|
-
connection.listen();
|
|
2099
|
-
setupConnectionLifecycle(state, recentStderr);
|
|
2100
|
-
let initResult;
|
|
2101
|
-
try {
|
|
2102
|
-
initResult = await withTimeout(safeSendRequest(connection, "initialize", {
|
|
2103
|
-
processId: process.pid,
|
|
2104
|
-
rootUri: pathToFileURL(root).href,
|
|
2105
|
-
workspaceFolders: [
|
|
2106
|
-
{ name: "workspace", uri: pathToFileURL(root).href }
|
|
2107
|
-
],
|
|
2108
|
-
capabilities: CLIENT_CAPABILITIES,
|
|
2109
|
-
initializationOptions: initialization
|
|
2110
|
-
}), initializeTimeoutMs);
|
|
2111
|
-
} catch (err) {
|
|
2112
|
-
state.shutdownRequested = true;
|
|
2113
|
-
const pid = lspProcess.pid;
|
|
2114
|
-
killProcessTree(lspProcess.process, pid);
|
|
2115
|
-
removeLspChild(pid, extractSpawnMarker(lspProcess.args)).catch((err) => {
|
|
2116
|
-
logLatency({
|
|
2117
|
-
type: "phase",
|
|
2118
|
-
phase: "lsp_registry_write_failed",
|
|
2119
|
-
filePath: "",
|
|
2120
|
-
durationMs: 0,
|
|
2121
|
-
metadata: { op: "remove", pid, error: String(err) }
|
|
2122
|
-
});
|
|
2123
|
-
});
|
|
2124
|
-
setTimeout(() => {
|
|
2125
|
-
if (lspProcess.process.exitCode === null && lspProcess.process.signalCode === null && process.platform !== "win32") {
|
|
2126
|
-
lspProcess.process.kill("SIGKILL");
|
|
2127
|
-
}
|
|
2128
|
-
}, 2000);
|
|
2129
|
-
throw err;
|
|
2130
|
-
} finally {
|
|
2131
|
-
lspProcess.stderr.off("data", onStderr);
|
|
2132
|
-
}
|
|
2133
|
-
if (initResult === undefined) {
|
|
2134
|
-
const compactStderr = startupState.stderr.replace(/\s+/g, " ").trim().slice(0, 320);
|
|
2135
|
-
const reinstallHint = serverId === "cpp" ? "Install clangd (LLVM/clang-tools) and ensure clangd.exe is on PATH." : `Try reinstalling: npm install -g ${serverId}-language-server.`;
|
|
2136
|
-
const telemetry = [
|
|
2137
|
-
`pid=${lspProcess.pid}`,
|
|
2138
|
-
`exitCode=${startupState.exitCode ?? "none"}`,
|
|
2139
|
-
`exitSignal=${startupState.exitSignal ?? "none"}`,
|
|
2140
|
-
`closeCode=${startupState.closeCode ?? "none"}`,
|
|
2141
|
-
`closeSignal=${startupState.closeSignal ?? "none"}`,
|
|
2142
|
-
`root=${root}`,
|
|
2143
|
-
compactStderr ? `stderr=${compactStderr}` : "stderr=<empty>"
|
|
2144
|
-
].join(" ");
|
|
2145
|
-
throw new Error(`[lsp] ${serverId} failed to initialize - stream may have been destroyed. ` + `The server binary may be missing or crashed immediately. ${reinstallHint} ` + `telemetry: ${telemetry}`);
|
|
2146
|
-
}
|
|
2147
|
-
state.workspaceDiagnosticsSupport = detectWorkspaceDiagnosticsSupport(initResult);
|
|
2148
|
-
state.operationSupport = detectOperationSupport(initResult);
|
|
2149
|
-
const capabilities = initResult?.capabilities ?? {};
|
|
2150
|
-
const workspace = isCapabilityRecord(capabilities.workspace) ? capabilities.workspace : undefined;
|
|
2151
|
-
const fileOperations = isCapabilityRecord(workspace) ? workspace.fileOperations : undefined;
|
|
2152
|
-
const malformedFileOperationRegistrations = new Set;
|
|
2153
|
-
if (isCapabilityRecord(fileOperations)) {
|
|
2154
|
-
for (const operation of ["willRename", "didRename"]) {
|
|
2155
|
-
if (fileOperations[operation] !== undefined && parseFileOperationFilters(fileOperations[operation]) === undefined) {
|
|
2156
|
-
malformedFileOperationRegistrations.add(operation);
|
|
2157
|
-
}
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
state.fileOperationMalformedRegistrations = malformedFileOperationRegistrations;
|
|
2161
|
-
if (isCapabilityRecord(fileOperations)) {
|
|
2162
|
-
state.fileOperationFilters = {
|
|
2163
|
-
willRename: parseFileOperationFilters(fileOperations.willRename),
|
|
2164
|
-
didRename: parseFileOperationFilters(fileOperations.didRename)
|
|
2165
|
-
};
|
|
2166
|
-
}
|
|
2167
|
-
state.positionEncoding = negotiatePositionEncoding(initResult?.capabilities);
|
|
2168
|
-
state.syncKind = negotiateSyncKind(initResult?.capabilities);
|
|
2169
|
-
state.saveOptions = negotiateSaveOptions(initResult?.capabilities);
|
|
2170
|
-
state.rawCapabilityKeys = Object.keys(initResult?.capabilities ?? {}).sort((a, b) => a.localeCompare(b));
|
|
2171
|
-
for (const cmd of detectExecuteCommands(initResult)) {
|
|
2172
|
-
state.advertisedCommands.add(cmd);
|
|
2173
|
-
}
|
|
2174
|
-
state.staticDiagnosticsMode = state.workspaceDiagnosticsSupport.mode;
|
|
2175
|
-
await safeSendNotification(connection, "initialized", {});
|
|
2176
|
-
if (initialization) {
|
|
2177
|
-
await safeSendNotification(connection, "workspace/didChangeConfiguration", {
|
|
2178
|
-
settings: initialization
|
|
2179
|
-
});
|
|
2180
|
-
}
|
|
2181
|
-
return {
|
|
2182
|
-
serverId,
|
|
2183
|
-
root,
|
|
2184
|
-
connection,
|
|
2185
|
-
isAlive: () => isClientAlive(state),
|
|
2186
|
-
processExited: () => lspProcess.process.exitCode !== null || lspProcess.process.killed === true,
|
|
2187
|
-
wasShutdownIntentional: () => state.shutdownRequested,
|
|
2188
|
-
getExitedAt: () => state.exitedAt,
|
|
2189
|
-
recentStderr: (lines) => recentStderr(lines),
|
|
2190
|
-
getPullFailureHistory: () => state.pullFailureHistory.map((entry) => ({
|
|
2191
|
-
...entry,
|
|
2192
|
-
message: entry.message.slice(0, 200)
|
|
2193
|
-
})),
|
|
2194
|
-
checkAlive: () => checkProcessAlive(),
|
|
2195
|
-
pingLiveness: (timeoutMs) => clientPingLiveness(state, timeoutMs),
|
|
2196
|
-
getProcessPid: () => lspProcess.pid,
|
|
2197
|
-
notify: {
|
|
2198
|
-
async open(filePath, content, languageId, preserveDiagnostics, silent, saved) {
|
|
2199
|
-
return handleNotifyOpen(state, filePath, content, languageId, preserveDiagnostics, silent, saved);
|
|
2200
|
-
},
|
|
2201
|
-
async change(filePath, content) {
|
|
2202
|
-
return handleNotifyChange(state, filePath, content);
|
|
2203
|
-
},
|
|
2204
|
-
watchedFileChange(filePath, type) {
|
|
2205
|
-
handleNotifyExternalChange(state, filePath, type);
|
|
2206
|
-
}
|
|
2207
|
-
},
|
|
2208
|
-
getDiagnostics(filePath) {
|
|
2209
|
-
return getMergedDiagnosticsForPath(state, normalizeMapKey(filePath));
|
|
2210
|
-
},
|
|
2211
|
-
getDiagnosticBinding(filePath) {
|
|
2212
|
-
return state.diagnosticBindings.get(normalizeMapKey(filePath));
|
|
2213
|
-
},
|
|
2214
|
-
getDiagnosticsVersionForPath(filePath) {
|
|
2215
|
-
return diagnosticsVersionForPath(state, normalizeMapKey(filePath));
|
|
2216
|
-
},
|
|
2217
|
-
getAllDiagnostics() {
|
|
2218
|
-
const result = new Map;
|
|
2219
|
-
const keys = new Set([
|
|
2220
|
-
...state.pushDiagnostics.keys(),
|
|
2221
|
-
...state.documentPullDiagnostics.keys()
|
|
2222
|
-
]);
|
|
2223
|
-
for (const key of keys) {
|
|
2224
|
-
result.set(key, {
|
|
2225
|
-
diags: getMergedDiagnosticsForPath(state, key),
|
|
2226
|
-
ts: Math.max(state.pushDiagnosticTimestamps.get(key) ?? 0, state.documentPullDiagnosticTimestamps.get(key) ?? 0),
|
|
2227
|
-
binding: state.diagnosticBindings.get(key)
|
|
2228
|
-
});
|
|
2229
|
-
}
|
|
2230
|
-
return result;
|
|
2231
|
-
},
|
|
2232
|
-
getTrackedDiagnosticPaths() {
|
|
2233
|
-
return [
|
|
2234
|
-
...new Set([
|
|
2235
|
-
...state.pushDiagnostics.keys(),
|
|
2236
|
-
...state.documentPullDiagnostics.keys()
|
|
2237
|
-
])
|
|
2238
|
-
].map((filePath) => process.platform === "win32" ? filePath.replace(/\//g, "\\") : filePath);
|
|
2239
|
-
},
|
|
2240
|
-
pruneDiagnostics(predicate) {
|
|
2241
|
-
let removed = 0;
|
|
2242
|
-
const keys = new Set([
|
|
2243
|
-
...state.pushDiagnostics.keys(),
|
|
2244
|
-
...state.documentPullDiagnostics.keys()
|
|
2245
|
-
]);
|
|
2246
|
-
for (const key of keys) {
|
|
2247
|
-
const diags = getMergedDiagnosticsForPath(state, key);
|
|
2248
|
-
const ts = Math.max(state.pushDiagnosticTimestamps.get(key) ?? 0, state.documentPullDiagnosticTimestamps.get(key) ?? 0);
|
|
2249
|
-
if (!predicate(key, ts, diags))
|
|
2250
|
-
continue;
|
|
2251
|
-
clearDiagnosticsForPath(state, key);
|
|
2252
|
-
removed++;
|
|
2253
|
-
}
|
|
2254
|
-
return removed;
|
|
2255
|
-
},
|
|
2256
|
-
getWorkspaceDiagnosticsSupport() {
|
|
2257
|
-
return state.workspaceDiagnosticsSupport;
|
|
2258
|
-
},
|
|
2259
|
-
requestWorkspaceDiagnostics(budgetMs) {
|
|
2260
|
-
return clientRequestWorkspaceDiagnostics(state, budgetMs);
|
|
2261
|
-
},
|
|
2262
|
-
getOperationSupport() {
|
|
2263
|
-
return state.operationSupport;
|
|
2264
|
-
},
|
|
2265
|
-
getMalformedFileOperationRegistrations() {
|
|
2266
|
-
return state.fileOperationMalformedRegistrations ?? new Set;
|
|
2267
|
-
},
|
|
2268
|
-
getAdvertisedCommands() {
|
|
2269
|
-
return [...state.advertisedCommands];
|
|
2270
|
-
},
|
|
2271
|
-
getRawCapabilityKeys() {
|
|
2272
|
-
return state.rawCapabilityKeys ?? [];
|
|
2273
|
-
},
|
|
2274
|
-
getLaunchVariant() {
|
|
2275
|
-
return state.launchVariant;
|
|
2276
|
-
},
|
|
2277
|
-
async executeCommand(command, args, mutationContext) {
|
|
2278
|
-
return runServerCommand(state, command, args, EXECUTE_COMMAND_TIMEOUT_MS, mutationContext);
|
|
2279
|
-
},
|
|
2280
|
-
async executeReadOnlyCommand(command, args) {
|
|
2281
|
-
return runReadOnlyServerCommand(state, command, args);
|
|
2282
|
-
},
|
|
2283
|
-
get diagnosticsVersion() {
|
|
2284
|
-
return state.diagnosticsVersion;
|
|
2285
|
-
},
|
|
2286
|
-
async waitForDiagnostics(filePath, timeoutMs = DIAGNOSTICS_WAIT_TIMEOUT_MS, options) {
|
|
2287
|
-
return clientWaitForDiagnostics(state, filePath, timeoutMs, options);
|
|
2288
|
-
},
|
|
2289
|
-
async definition(filePath, line, character) {
|
|
2290
|
-
const result = await navRequest(state, "textDocument/definition", {
|
|
2291
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2292
|
-
position: await toWirePosition(state, filePath, line, character)
|
|
2293
|
-
}, filePath);
|
|
2294
|
-
if (!result)
|
|
2295
|
-
return [];
|
|
2296
|
-
return Array.isArray(result) ? result : [result];
|
|
2297
|
-
},
|
|
2298
|
-
async typeDefinition(filePath, line, character) {
|
|
2299
|
-
const result = await navRequest(state, "textDocument/typeDefinition", {
|
|
2300
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2301
|
-
position: await toWirePosition(state, filePath, line, character)
|
|
2302
|
-
}, filePath);
|
|
2303
|
-
if (!result)
|
|
2304
|
-
return [];
|
|
2305
|
-
return Array.isArray(result) ? result : [result];
|
|
2306
|
-
},
|
|
2307
|
-
async declaration(filePath, line, character) {
|
|
2308
|
-
const result = await navRequest(state, "textDocument/declaration", {
|
|
2309
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2310
|
-
position: await toWirePosition(state, filePath, line, character)
|
|
2311
|
-
}, filePath);
|
|
2312
|
-
if (!result)
|
|
2313
|
-
return [];
|
|
2314
|
-
return Array.isArray(result) ? result : [result];
|
|
2315
|
-
},
|
|
2316
|
-
async references(filePath, line, character, includeDeclaration = true) {
|
|
2317
|
-
const result = await navRequest(state, "textDocument/references", {
|
|
2318
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2319
|
-
position: await toWirePosition(state, filePath, line, character),
|
|
2320
|
-
context: { includeDeclaration }
|
|
2321
|
-
}, filePath);
|
|
2322
|
-
return result ?? [];
|
|
2323
|
-
},
|
|
2324
|
-
async hover(filePath, line, character) {
|
|
2325
|
-
const result = await navRequest(state, "textDocument/hover", {
|
|
2326
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2327
|
-
position: await toWirePosition(state, filePath, line, character)
|
|
2328
|
-
}, filePath);
|
|
2329
|
-
return result ?? null;
|
|
2330
|
-
},
|
|
2331
|
-
async signatureHelp(filePath, line, character) {
|
|
2332
|
-
const result = await navRequest(state, "textDocument/signatureHelp", {
|
|
2333
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2334
|
-
position: await toWirePosition(state, filePath, line, character)
|
|
2335
|
-
}, filePath);
|
|
2336
|
-
return result ?? null;
|
|
2337
|
-
},
|
|
2338
|
-
async documentSymbol(filePath) {
|
|
2339
|
-
const result = await navRequest(state, "textDocument/documentSymbol", { textDocument: { uri: pathToFileURL(filePath).href } }, filePath);
|
|
2340
|
-
return result ?? [];
|
|
2341
|
-
},
|
|
2342
|
-
isDocumentOpen(filePath) {
|
|
2343
|
-
return state.openDocuments.has(normalizeMapKey(filePath));
|
|
2344
|
-
},
|
|
2345
|
-
openDocumentPaths() {
|
|
2346
|
-
return [...state.openDocuments];
|
|
2347
|
-
},
|
|
2348
|
-
isBusy() {
|
|
2349
|
-
return (activeRequestsByConnection.get(connection) ?? 0) > 0;
|
|
2350
|
-
},
|
|
2351
|
-
getDocumentUri(filePath) {
|
|
2352
|
-
return state.openDocumentUris?.get(normalizeMapKey(filePath));
|
|
2353
|
-
},
|
|
2354
|
-
async workspaceSymbol(query) {
|
|
2355
|
-
if (!isClientAlive(state))
|
|
2356
|
-
return [];
|
|
2357
|
-
const result = await navRequest(state, "workspace/symbol", {
|
|
2358
|
-
query
|
|
2359
|
-
});
|
|
2360
|
-
return result ?? [];
|
|
2361
|
-
},
|
|
2362
|
-
async codeAction(filePath, line, character, endLine, endCharacter) {
|
|
2363
|
-
if (!isClientAlive(state))
|
|
2364
|
-
return [];
|
|
2365
|
-
const uri = pathToFileURL(filePath).href;
|
|
2366
|
-
const result = await navRequest(state, "textDocument/codeAction", {
|
|
2367
|
-
textDocument: { uri },
|
|
2368
|
-
range: {
|
|
2369
|
-
start: await toWirePosition(state, filePath, line, character),
|
|
2370
|
-
end: await toWirePosition(state, filePath, endLine, endCharacter)
|
|
2371
|
-
},
|
|
2372
|
-
context: {
|
|
2373
|
-
diagnostics: getMergedDiagnosticsForPath(state, normalizeMapKey(filePath))
|
|
2374
|
-
}
|
|
2375
|
-
}, filePath);
|
|
2376
|
-
if (!result || !Array.isArray(result))
|
|
2377
|
-
return [];
|
|
2378
|
-
const actions = result.filter((item) => typeof item === "object" && item !== null && ("title" in item));
|
|
2379
|
-
return Promise.all(actions.map((action) => resolveCodeActionBestEffort(state, action)));
|
|
2380
|
-
},
|
|
2381
|
-
async rename(filePath, line, character, newName) {
|
|
2382
|
-
const result = await navRequest(state, "textDocument/rename", {
|
|
2383
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2384
|
-
position: await toWirePosition(state, filePath, line, character),
|
|
2385
|
-
newName
|
|
2386
|
-
}, filePath);
|
|
2387
|
-
return result ? await normalizeClientWorkspaceEdit(state, result) : null;
|
|
2388
|
-
},
|
|
2389
|
-
closeDocument: (filePath) => closeDocument(state, filePath),
|
|
2390
|
-
async willRenameFiles(oldFilePath, newFilePath) {
|
|
2391
|
-
if (!isClientAlive(state))
|
|
2392
|
-
return null;
|
|
2393
|
-
const oldUri = pathToFileURL(oldFilePath).href;
|
|
2394
|
-
const newUri = pathToFileURL(newFilePath).href;
|
|
2395
|
-
if (!await fileOperationFiltersMatch(state.fileOperationFilters?.willRename, oldUri, newUri, oldFilePath, newFilePath)) {
|
|
2396
|
-
recordDegradationOnce({
|
|
2397
|
-
kind: "lsp-capability-skip",
|
|
2398
|
-
subject: `${state.serverId}:workspace/willRenameFiles:filter`,
|
|
2399
|
-
reason: "filter-mismatch"
|
|
2400
|
-
});
|
|
2401
|
-
return null;
|
|
2402
|
-
}
|
|
2403
|
-
const result = await navRequest(state, "workspace/willRenameFiles", {
|
|
2404
|
-
files: [
|
|
2405
|
-
{
|
|
2406
|
-
oldUri,
|
|
2407
|
-
newUri
|
|
2408
|
-
}
|
|
2409
|
-
]
|
|
2410
|
-
});
|
|
2411
|
-
return result ? await normalizeClientWorkspaceEdit(state, result) : null;
|
|
2412
|
-
},
|
|
2413
|
-
async didRenameFiles(oldFilePath, newFilePath, oldUri, newUri) {
|
|
2414
|
-
if (!isClientAlive(state))
|
|
2415
|
-
return;
|
|
2416
|
-
const wireOldUri = oldUri ?? pathToFileURL(oldFilePath).href;
|
|
2417
|
-
const wireNewUri = newUri ?? pathToFileURL(newFilePath).href;
|
|
2418
|
-
if (!await fileOperationFiltersMatch(state.fileOperationFilters?.didRename, wireOldUri, wireNewUri, oldFilePath, newFilePath)) {
|
|
2419
|
-
recordDegradationOnce({
|
|
2420
|
-
kind: "lsp-capability-skip",
|
|
2421
|
-
subject: `${state.serverId}:workspace/didRenameFiles:filter`,
|
|
2422
|
-
reason: "filter-mismatch"
|
|
2423
|
-
});
|
|
2424
|
-
return;
|
|
2425
|
-
}
|
|
2426
|
-
await safeSendNotification(state.connection, "workspace/didRenameFiles", {
|
|
2427
|
-
files: [
|
|
2428
|
-
{
|
|
2429
|
-
oldUri: wireOldUri,
|
|
2430
|
-
newUri: wireNewUri
|
|
2431
|
-
}
|
|
2432
|
-
]
|
|
2433
|
-
});
|
|
2434
|
-
},
|
|
2435
|
-
async implementation(filePath, line, character) {
|
|
2436
|
-
const result = await navRequest(state, "textDocument/implementation", {
|
|
2437
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2438
|
-
position: await toWirePosition(state, filePath, line, character)
|
|
2439
|
-
}, filePath);
|
|
2440
|
-
if (!result)
|
|
2441
|
-
return [];
|
|
2442
|
-
return Array.isArray(result) ? result : [result];
|
|
2443
|
-
},
|
|
2444
|
-
async prepareCallHierarchy(filePath, line, character) {
|
|
2445
|
-
const result = await navRequest(state, "textDocument/prepareCallHierarchy", {
|
|
2446
|
-
textDocument: { uri: pathToFileURL(filePath).href },
|
|
2447
|
-
position: await toWirePosition(state, filePath, line, character)
|
|
2448
|
-
}, filePath);
|
|
2449
|
-
if (!result)
|
|
2450
|
-
return [];
|
|
2451
|
-
return Array.isArray(result) ? result : [result];
|
|
2452
|
-
},
|
|
2453
|
-
async incomingCalls(item) {
|
|
2454
|
-
const result = await navRequest(state, "callHierarchy/incomingCalls", { item });
|
|
2455
|
-
return result ?? [];
|
|
2456
|
-
},
|
|
2457
|
-
async outgoingCalls(item) {
|
|
2458
|
-
const result = await navRequest(state, "callHierarchy/outgoingCalls", { item });
|
|
2459
|
-
return result ?? [];
|
|
2460
|
-
},
|
|
2461
|
-
async shutdown(options) {
|
|
2462
|
-
return clientShutdown(state, options);
|
|
2463
|
-
}
|
|
2464
|
-
};
|
|
2465
|
-
}
|
|
2466
|
-
async function safeSendNotification(connection, method, params) {
|
|
2467
|
-
try {
|
|
2468
|
-
await connection.sendNotification(method, params);
|
|
2469
|
-
return true;
|
|
2470
|
-
} catch (err) {
|
|
2471
|
-
if (isStreamError(err)) {
|
|
2472
|
-
return false;
|
|
2473
|
-
}
|
|
2474
|
-
throw err;
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
const activeRequestsByConnection = new WeakMap;
|
|
2478
|
-
async function safeSendRequest(connection, method, params, signal, settlement) {
|
|
2479
|
-
if (signal?.aborted)
|
|
2480
|
-
return;
|
|
2481
|
-
let tokenSource;
|
|
2482
|
-
let onAbort;
|
|
2483
|
-
if (signal) {
|
|
2484
|
-
tokenSource = new CancellationTokenSource;
|
|
2485
|
-
onAbort = () => tokenSource?.cancel();
|
|
2486
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
2487
|
-
}
|
|
2488
|
-
const send = () => tokenSource ? connection.sendRequest(method, params, tokenSource.token) : connection.sendRequest(method, params);
|
|
2489
|
-
activeRequestsByConnection.set(connection, (activeRequestsByConnection.get(connection) ?? 0) + 1);
|
|
2490
|
-
try {
|
|
2491
|
-
const MAX_ATTEMPTS = 2;
|
|
2492
|
-
for (let attempt = 1;; attempt++) {
|
|
2493
|
-
try {
|
|
2494
|
-
return await send();
|
|
2495
|
-
} catch (err) {
|
|
2496
|
-
if (isCancellationError(err)) {
|
|
2497
|
-
if (settlement)
|
|
2498
|
-
settlement.cancelled = true;
|
|
2499
|
-
return;
|
|
2500
|
-
}
|
|
2501
|
-
if (isStreamError(err)) {
|
|
2502
|
-
return;
|
|
2503
|
-
}
|
|
2504
|
-
if (isContentModifiedError(err)) {
|
|
2505
|
-
if (attempt < MAX_ATTEMPTS && !signal?.aborted)
|
|
2506
|
-
continue;
|
|
2507
|
-
return;
|
|
2508
|
-
}
|
|
2509
|
-
throw err;
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
} finally {
|
|
2513
|
-
const remaining = (activeRequestsByConnection.get(connection) ?? 1) - 1;
|
|
2514
|
-
if (remaining > 0)
|
|
2515
|
-
activeRequestsByConnection.set(connection, remaining);
|
|
2516
|
-
else
|
|
2517
|
-
activeRequestsByConnection.delete(connection);
|
|
2518
|
-
if (signal && onAbort)
|
|
2519
|
-
signal.removeEventListener("abort", onAbort);
|
|
2520
|
-
tokenSource?.dispose();
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
function isCancellationError(err) {
|
|
2524
|
-
const code = err?.code;
|
|
2525
|
-
return code === -32800 || code === -32802;
|
|
2526
|
-
}
|
|
2527
|
-
function isContentModifiedError(err) {
|
|
2528
|
-
return err?.code === -32801;
|
|
2529
|
-
}
|
|
2530
|
-
function isStreamError(err) {
|
|
2531
|
-
if (!(err instanceof Error))
|
|
2532
|
-
return false;
|
|
2533
|
-
const msg = err.message.toLowerCase();
|
|
2534
|
-
return msg.includes("stream") || msg.includes("destroyed") || msg.includes("closed") || msg.includes("disposed") || msg.includes("cancelled") || err.code === "ERR_STREAM_DESTROYED" || err.code === "ERR_STREAM_WRITE_AFTER_END" || err.code === "EPIPE";
|
|
2535
|
-
}
|
|
2536
|
-
function positiveIntFromEnv(name, fallback) {
|
|
2537
|
-
const raw = process.env[name];
|
|
2538
|
-
if (!raw)
|
|
2539
|
-
return fallback;
|
|
2540
|
-
const parsed = Number.parseInt(raw, 10);
|
|
2541
|
-
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
2542
|
-
return fallback;
|
|
2543
|
-
return parsed;
|
|
2544
|
-
}
|
|
2545
|
-
function detectWorkspaceDiagnosticsSupport(initResult) {
|
|
2546
|
-
const capabilities = typeof initResult === "object" && initResult !== null ? initResult.capabilities : undefined;
|
|
2547
|
-
const diagnosticProvider = capabilities?.diagnosticProvider;
|
|
2548
|
-
if (!diagnosticProvider) {
|
|
2549
|
-
return {
|
|
2550
|
-
advertised: false,
|
|
2551
|
-
mode: "push-only",
|
|
2552
|
-
workspaceDiagnostics: false,
|
|
2553
|
-
diagnosticProviderKind: "none"
|
|
2554
|
-
};
|
|
2555
|
-
}
|
|
2556
|
-
if (typeof diagnosticProvider === "boolean") {
|
|
2557
|
-
return {
|
|
2558
|
-
advertised: diagnosticProvider,
|
|
2559
|
-
mode: diagnosticProvider ? "pull" : "push-only",
|
|
2560
|
-
workspaceDiagnostics: false,
|
|
2561
|
-
diagnosticProviderKind: "boolean"
|
|
2562
|
-
};
|
|
2563
|
-
}
|
|
2564
|
-
if (typeof diagnosticProvider === "object") {
|
|
2565
|
-
return {
|
|
2566
|
-
advertised: true,
|
|
2567
|
-
mode: "pull",
|
|
2568
|
-
workspaceDiagnostics: diagnosticProvider.workspaceDiagnostics === true,
|
|
2569
|
-
diagnosticProviderKind: "object"
|
|
2570
|
-
};
|
|
2571
|
-
}
|
|
2572
|
-
return {
|
|
2573
|
-
advertised: false,
|
|
2574
|
-
mode: "push-only",
|
|
2575
|
-
workspaceDiagnostics: false,
|
|
2576
|
-
diagnosticProviderKind: typeof diagnosticProvider
|
|
2577
|
-
};
|
|
2578
|
-
}
|
|
2579
|
-
function detectExecuteCommands(initResult) {
|
|
2580
|
-
const capabilities = typeof initResult === "object" && initResult !== null ? initResult.capabilities : undefined;
|
|
2581
|
-
const provider = capabilities?.executeCommandProvider;
|
|
2582
|
-
if (typeof provider !== "object" || provider === null)
|
|
2583
|
-
return [];
|
|
2584
|
-
const commands = provider.commands;
|
|
2585
|
-
if (!Array.isArray(commands))
|
|
2586
|
-
return [];
|
|
2587
|
-
return commands.filter((cmd) => typeof cmd === "string");
|
|
2588
|
-
}
|
|
2589
|
-
function detectOperationSupport(initResult) {
|
|
2590
|
-
const capabilities = typeof initResult === "object" && initResult !== null ? initResult.capabilities : undefined;
|
|
2591
|
-
const hasProvider = (key) => {
|
|
2592
|
-
const value = capabilities?.[key];
|
|
2593
|
-
if (value === undefined || value === null)
|
|
2594
|
-
return false;
|
|
2595
|
-
if (typeof value === "boolean")
|
|
2596
|
-
return value;
|
|
2597
|
-
return isCapabilityRecord(value);
|
|
2598
|
-
};
|
|
2599
|
-
const codeActionProvider = capabilities?.codeActionProvider;
|
|
2600
|
-
const workspace = capabilities?.workspace;
|
|
2601
|
-
const fileOperations = isCapabilityRecord(workspace) ? workspace.fileOperations : undefined;
|
|
2602
|
-
return {
|
|
2603
|
-
definition: hasProvider("definitionProvider"),
|
|
2604
|
-
typeDefinition: hasProvider("typeDefinitionProvider"),
|
|
2605
|
-
declaration: hasProvider("declarationProvider"),
|
|
2606
|
-
references: hasProvider("referencesProvider"),
|
|
2607
|
-
hover: hasProvider("hoverProvider"),
|
|
2608
|
-
signatureHelp: hasProvider("signatureHelpProvider"),
|
|
2609
|
-
documentSymbol: hasProvider("documentSymbolProvider"),
|
|
2610
|
-
workspaceSymbol: hasProvider("workspaceSymbolProvider"),
|
|
2611
|
-
codeAction: hasProvider("codeActionProvider"),
|
|
2612
|
-
codeActionResolve: isCapabilityRecord(codeActionProvider) && codeActionProvider.resolveProvider === true,
|
|
2613
|
-
rename: hasProvider("renameProvider"),
|
|
2614
|
-
willRenameFiles: isCapabilityRecord(fileOperations) && isFileOperationRegistrationOptions(fileOperations.willRename),
|
|
2615
|
-
didRenameFiles: isCapabilityRecord(fileOperations) && isFileOperationRegistrationOptions(fileOperations.didRename),
|
|
2616
|
-
implementation: hasProvider("implementationProvider"),
|
|
2617
|
-
callHierarchy: hasProvider("callHierarchyProvider")
|
|
2618
|
-
};
|
|
2619
|
-
}
|
|
2620
|
-
function isCapabilityRecord(value) {
|
|
2621
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2622
|
-
}
|
|
2623
|
-
function isFileOperationRegistrationOptions(value) {
|
|
2624
|
-
return parseFileOperationFilters(value) !== undefined;
|
|
2625
|
-
}
|
|
2626
|
-
function parseFileOperationFilters(value) {
|
|
2627
|
-
if (!isCapabilityRecord(value) || !Array.isArray(value.filters)) {
|
|
2628
|
-
return;
|
|
2629
|
-
}
|
|
2630
|
-
const filters = [];
|
|
2631
|
-
for (const entry of value.filters) {
|
|
2632
|
-
if (!isCapabilityRecord(entry))
|
|
2633
|
-
return;
|
|
2634
|
-
const pattern = entry.pattern;
|
|
2635
|
-
if (!isCapabilityRecord(pattern) || typeof pattern.glob !== "string" || pattern.glob.trim() === "") {
|
|
2636
|
-
return;
|
|
2637
|
-
}
|
|
2638
|
-
if (entry.scheme !== undefined && (typeof entry.scheme !== "string" || entry.scheme.trim() === "" || !/^[A-Za-z][A-Za-z0-9+.-]*$/.test(entry.scheme))) {
|
|
2639
|
-
return;
|
|
2640
|
-
}
|
|
2641
|
-
const matches = pattern.matches;
|
|
2642
|
-
if (matches !== undefined && matches !== "file" && matches !== "folder") {
|
|
2643
|
-
return;
|
|
2644
|
-
}
|
|
2645
|
-
if (pattern.options !== undefined && !isCapabilityRecord(pattern.options)) {
|
|
2646
|
-
return;
|
|
2647
|
-
}
|
|
2648
|
-
const options = isCapabilityRecord(pattern.options) ? pattern.options : undefined;
|
|
2649
|
-
if (options?.ignoreCase !== undefined && typeof options.ignoreCase !== "boolean") {
|
|
2650
|
-
return;
|
|
2651
|
-
}
|
|
2652
|
-
filters.push({
|
|
2653
|
-
scheme: entry.scheme?.toLowerCase(),
|
|
2654
|
-
glob: pattern.glob,
|
|
2655
|
-
matches: matches === "file" || matches === "folder" ? matches : undefined,
|
|
2656
|
-
ignoreCase: options?.ignoreCase === true
|
|
2657
|
-
});
|
|
2658
|
-
}
|
|
2659
|
-
return filters;
|
|
2660
|
-
}
|
|
2661
|
-
async function fileOperationFiltersMatch(filters, oldUri, newUri, oldFilePath, newFilePath) {
|
|
2662
|
-
if (!filters || filters.length === 0)
|
|
2663
|
-
return false;
|
|
2664
|
-
const candidates = [oldUri, newUri].flatMap(fileOperationUriCandidates);
|
|
2665
|
-
const entityKind = filters.some((filter) => filter.matches !== undefined) ? await probeRenameEntityKind(oldFilePath, newFilePath) : undefined;
|
|
2666
|
-
for (const filter of filters) {
|
|
2667
|
-
if (filter.scheme !== undefined && filter.scheme !== "file")
|
|
2668
|
-
continue;
|
|
2669
|
-
if (filter.matches !== undefined && filter.matches !== entityKind)
|
|
2670
|
-
continue;
|
|
2671
|
-
for (const candidate of candidates) {
|
|
2672
|
-
try {
|
|
2673
|
-
if (minimatch(candidate, filter.glob, {
|
|
2674
|
-
nocase: filter.ignoreCase,
|
|
2675
|
-
dot: true
|
|
2676
|
-
})) {
|
|
2677
|
-
return true;
|
|
2678
|
-
}
|
|
2679
|
-
} catch {}
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
return false;
|
|
2683
|
-
}
|
|
2684
|
-
function fileOperationUriCandidates(uri) {
|
|
2685
|
-
let parsed;
|
|
2686
|
-
try {
|
|
2687
|
-
parsed = new URL(uri);
|
|
2688
|
-
} catch {
|
|
2689
|
-
return [];
|
|
2690
|
-
}
|
|
2691
|
-
if (parsed.protocol.toLowerCase() !== "file:")
|
|
2692
|
-
return [];
|
|
2693
|
-
let uriPath;
|
|
2694
|
-
try {
|
|
2695
|
-
uriPath = decodeURIComponent(parsed.pathname).replace(/\\/g, "/");
|
|
2696
|
-
} catch {
|
|
2697
|
-
return [];
|
|
2698
|
-
}
|
|
2699
|
-
return [uriPath];
|
|
2700
|
-
}
|
|
2701
|
-
async function probeRenameEntityKind(oldFilePath, newFilePath) {
|
|
2702
|
-
for (const filePath of [oldFilePath, newFilePath]) {
|
|
2703
|
-
try {
|
|
2704
|
-
const info = await lstat(filePath);
|
|
2705
|
-
return info.isDirectory() ? "folder" : "file";
|
|
2706
|
-
} catch {}
|
|
2707
|
-
}
|
|
2708
|
-
return;
|
|
2709
|
-
}
|