@hydranium/core 1.0.0-next.11 → 1.0.0-next.112
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/lib/documents/ast-document-manager.d.ts +75 -16
- package/lib/documents/ast-document-manager.d.ts.map +1 -1
- package/lib/documents/ast-document-manager.js +57 -19
- package/lib/documents/ast-document-manager.js.map +1 -1
- package/lib/documents/client-ids.d.ts +5 -19
- package/lib/documents/client-ids.d.ts.map +1 -1
- package/lib/documents/client-ids.js +5 -19
- package/lib/documents/client-ids.js.map +1 -1
- package/lib/documents/hydranium-text-documents.d.ts +132 -42
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
- package/lib/documents/hydranium-text-documents.js +199 -57
- package/lib/documents/hydranium-text-documents.js.map +1 -1
- package/lib/documents/language-client-text-shadow.d.ts +41 -9
- package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
- package/lib/documents/language-client-text-shadow.js +56 -11
- package/lib/documents/language-client-text-shadow.js.map +1 -1
- package/lib/documents/self-save-registry.d.ts +37 -6
- package/lib/documents/self-save-registry.d.ts.map +1 -1
- package/lib/documents/self-save-registry.js +28 -15
- package/lib/documents/self-save-registry.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +6 -0
- package/lib/index.js.map +1 -1
- package/lib/langium/ast-extension/ast-node-builder.d.ts +32 -9
- package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
- package/lib/langium/ast-extension/ast-node-builder.js +60 -22
- package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
- package/lib/langium/bootstrap.d.ts.map +1 -1
- package/lib/langium/bootstrap.js +16 -9
- package/lib/langium/bootstrap.js.map +1 -1
- package/lib/langium/config/configuration-provider.d.ts +81 -0
- package/lib/langium/config/configuration-provider.d.ts.map +1 -0
- package/lib/langium/config/configuration-provider.js +112 -0
- package/lib/langium/config/configuration-provider.js.map +1 -0
- package/lib/langium/config/index.d.ts +1 -0
- package/lib/langium/config/index.d.ts.map +1 -1
- package/lib/langium/config/index.js +1 -0
- package/lib/langium/config/index.js.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.d.ts +17 -0
- package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.js +40 -6
- package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
- package/lib/langium/document-builder/build-pipeline-integration.d.ts +21 -1
- package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
- package/lib/langium/document-builder/build-pipeline-integration.js +32 -5
- package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
- package/lib/langium/document-builder/build-session.d.ts +93 -0
- package/lib/langium/document-builder/build-session.d.ts.map +1 -0
- package/lib/langium/document-builder/build-session.js +72 -0
- package/lib/langium/document-builder/build-session.js.map +1 -0
- package/lib/langium/document-builder/document-builder.d.ts +150 -7
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +298 -11
- package/lib/langium/document-builder/document-builder.js.map +1 -1
- package/lib/langium/document-builder/index.d.ts +1 -0
- package/lib/langium/document-builder/index.d.ts.map +1 -1
- package/lib/langium/document-builder/index.js +1 -0
- package/lib/langium/document-builder/index.js.map +1 -1
- package/lib/langium/integration-services.d.ts +45 -1
- package/lib/langium/integration-services.d.ts.map +1 -1
- package/lib/langium/integration-services.js +10 -5
- package/lib/langium/integration-services.js.map +1 -1
- package/lib/langium/integrity/integrity-service.d.ts +16 -0
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-service.js +110 -2
- package/lib/langium/integrity/integrity-service.js.map +1 -1
- package/lib/langium/keys/containment.d.ts +64 -0
- package/lib/langium/keys/containment.d.ts.map +1 -0
- package/lib/langium/keys/containment.js +53 -0
- package/lib/langium/keys/containment.js.map +1 -0
- package/lib/langium/keys/index.d.ts +1 -0
- package/lib/langium/keys/index.d.ts.map +1 -1
- package/lib/langium/keys/index.js +1 -0
- package/lib/langium/keys/index.js.map +1 -1
- package/lib/langium/keys/name-based-key-provider.d.ts +4 -0
- package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
- package/lib/langium/keys/name-based-key-provider.js +4 -0
- package/lib/langium/keys/name-based-key-provider.js.map +1 -1
- package/lib/langium/language-module.d.ts +25 -0
- package/lib/langium/language-module.d.ts.map +1 -1
- package/lib/langium/language-module.js +14 -0
- package/lib/langium/language-module.js.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +161 -10
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +176 -97
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/module.d.ts +84 -15
- package/lib/langium/module.d.ts.map +1 -1
- package/lib/langium/module.js +26 -12
- package/lib/langium/module.js.map +1 -1
- package/lib/langium/naming/name-provider.d.ts +25 -4
- package/lib/langium/naming/name-provider.d.ts.map +1 -1
- package/lib/langium/naming/name-provider.js +2 -1
- package/lib/langium/naming/name-provider.js.map +1 -1
- package/lib/langium/naming/name-separator-validation.d.ts +23 -0
- package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
- package/lib/langium/naming/name-separator-validation.js +30 -1
- package/lib/langium/naming/name-separator-validation.js.map +1 -1
- package/lib/langium/residency/cst-residency-service.d.ts +31 -11
- package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
- package/lib/langium/residency/cst-residency-service.js +13 -56
- package/lib/langium/residency/cst-residency-service.js.map +1 -1
- package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
- package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
- package/lib/langium/scope/hydranium-scope-provider.js +56 -20
- package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
- package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
- package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
- package/lib/langium/serialization/abstract-serializer.js +12 -21
- package/lib/langium/serialization/abstract-serializer.js.map +1 -1
- package/lib/langium/service-registry.d.ts +15 -1
- package/lib/langium/service-registry.d.ts.map +1 -1
- package/lib/langium/service-registry.js +16 -0
- package/lib/langium/service-registry.js.map +1 -1
- package/lib/langium/shared-services.d.ts +15 -2
- package/lib/langium/shared-services.d.ts.map +1 -1
- package/lib/langium/shared-services.js.map +1 -1
- package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
- package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
- package/lib/langium/transfer/transfer-encoder.js +38 -16
- package/lib/langium/transfer/transfer-encoder.js.map +1 -1
- package/lib/langium/trivia/comment-preserver.d.ts +423 -0
- package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/comment-preserver.js +906 -0
- package/lib/langium/trivia/comment-preserver.js.map +1 -0
- package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
- package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/document-ending-preserver.js +48 -0
- package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
- package/lib/langium/trivia/index.d.ts +14 -0
- package/lib/langium/trivia/index.d.ts.map +1 -0
- package/lib/langium/trivia/index.js +14 -0
- package/lib/langium/trivia/index.js.map +1 -0
- package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
- package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-contribution.js +10 -0
- package/lib/langium/trivia/trivia-contribution.js.map +1 -0
- package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
- package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-preserver.js +10 -0
- package/lib/langium/trivia/trivia-preserver.js.map +1 -0
- package/lib/langium/trivia/trivia-service.d.ts +70 -0
- package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-service.js +56 -0
- package/lib/langium/trivia/trivia-service.js.map +1 -0
- package/lib/langium/validation/document-validator.d.ts +294 -10
- package/lib/langium/validation/document-validator.d.ts.map +1 -1
- package/lib/langium/validation/document-validator.js +448 -5
- package/lib/langium/validation/document-validator.js.map +1 -1
- package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
- package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
- package/lib/langium/workspace/document-uri-policy.js +3 -4
- package/lib/langium/workspace/document-uri-policy.js.map +1 -1
- package/lib/langium/workspace/file-system-provider.d.ts +9 -1
- package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/file-system-provider.js +40 -7
- package/lib/langium/workspace/file-system-provider.js.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.js +47 -9
- package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
- package/lib/langium/workspace/initialize-workspace.d.ts +23 -2
- package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
- package/lib/langium/workspace/initialize-workspace.js +13 -5
- package/lib/langium/workspace/initialize-workspace.js.map +1 -1
- package/lib/langium/workspace/langium-documents.d.ts +79 -17
- package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
- package/lib/langium/workspace/langium-documents.js +83 -28
- package/lib/langium/workspace/langium-documents.js.map +1 -1
- package/lib/langium/workspace/virtual-document.d.ts +18 -3
- package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
- package/lib/langium/workspace/virtual-document.js +19 -2
- package/lib/langium/workspace/virtual-document.js.map +1 -1
- package/lib/locale/index.d.ts +10 -0
- package/lib/locale/index.d.ts.map +1 -0
- package/lib/locale/index.js +10 -0
- package/lib/locale/index.js.map +1 -0
- package/lib/locale/server-locale.d.ts +73 -0
- package/lib/locale/server-locale.d.ts.map +1 -0
- package/lib/locale/server-locale.js +61 -0
- package/lib/locale/server-locale.js.map +1 -0
- package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
- package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
- package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
- package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
- package/lib/lsp/connection-features.d.ts +23 -0
- package/lib/lsp/connection-features.d.ts.map +1 -0
- package/lib/lsp/connection-features.js +60 -0
- package/lib/lsp/connection-features.js.map +1 -0
- package/lib/lsp/diagnostics-connection.d.ts +27 -0
- package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
- package/lib/lsp/diagnostics-connection.js +53 -0
- package/lib/lsp/diagnostics-connection.js.map +1 -0
- package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.js +10 -1
- package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
- package/lib/lsp/index.d.ts +3 -1
- package/lib/lsp/index.d.ts.map +1 -1
- package/lib/lsp/index.js +3 -1
- package/lib/lsp/index.js.map +1 -1
- package/lib/lsp/lsp-latency.d.ts +39 -0
- package/lib/lsp/lsp-latency.d.ts.map +1 -0
- package/lib/lsp/lsp-latency.js +58 -0
- package/lib/lsp/lsp-latency.js.map +1 -0
- package/lib/lsp/semantic-token-provider.d.ts +34 -1
- package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
- package/lib/lsp/semantic-token-provider.js +55 -8
- package/lib/lsp/semantic-token-provider.js.map +1 -1
- package/lib/lsp/start-language-server.d.ts +5 -3
- package/lib/lsp/start-language-server.d.ts.map +1 -1
- package/lib/lsp/start-language-server.js +10 -3
- package/lib/lsp/start-language-server.js.map +1 -1
- package/lib/messages/carriers.d.ts +54 -0
- package/lib/messages/carriers.d.ts.map +1 -0
- package/lib/messages/carriers.js +62 -0
- package/lib/messages/carriers.js.map +1 -0
- package/lib/messages/index.d.ts +25 -0
- package/lib/messages/index.d.ts.map +1 -0
- package/lib/messages/index.js +25 -0
- package/lib/messages/index.js.map +1 -0
- package/lib/messages/renderer.d.ts +136 -0
- package/lib/messages/renderer.d.ts.map +1 -0
- package/lib/messages/renderer.js +159 -0
- package/lib/messages/renderer.js.map +1 -0
- package/lib/node/heap-ceiling.d.ts +61 -0
- package/lib/node/heap-ceiling.d.ts.map +1 -0
- package/lib/node/heap-ceiling.js +72 -0
- package/lib/node/heap-ceiling.js.map +1 -0
- package/lib/node/index.d.ts +1 -0
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/index.js +1 -0
- package/lib/node/index.js.map +1 -1
- package/lib/node/latency-from-env.d.ts +1 -1
- package/lib/node/latency-from-env.js +1 -1
- package/lib/node/measure-memory.d.ts.map +1 -1
- package/lib/node/measure-memory.js +1 -0
- package/lib/node/measure-memory.js.map +1 -1
- package/lib/node/node-file-system-provider.d.ts +35 -1
- package/lib/node/node-file-system-provider.d.ts.map +1 -1
- package/lib/node/node-file-system-provider.js +140 -45
- package/lib/node/node-file-system-provider.js.map +1 -1
- package/lib/node/profile-capture.d.ts +8 -2
- package/lib/node/profile-capture.d.ts.map +1 -1
- package/lib/node/profile-capture.js.map +1 -1
- package/lib/node/profiling-run.d.ts +2 -2
- package/lib/node/profiling-run.js +1 -1
- package/lib/node/profiling-run.js.map +1 -1
- package/lib/node/rename-over-open-readers.d.ts +52 -0
- package/lib/node/rename-over-open-readers.d.ts.map +1 -0
- package/lib/node/rename-over-open-readers.js +66 -0
- package/lib/node/rename-over-open-readers.js.map +1 -0
- package/lib/node/socket-launcher.d.ts +15 -0
- package/lib/node/socket-launcher.d.ts.map +1 -1
- package/lib/node/socket-launcher.js +3 -2
- package/lib/node/socket-launcher.js.map +1 -1
- package/lib/testing/fake-document.d.ts +16 -4
- package/lib/testing/fake-document.d.ts.map +1 -1
- package/lib/testing/fake-document.js +18 -3
- package/lib/testing/fake-document.js.map +1 -1
- package/lib/testing/make-noop-shared-services.d.ts +14 -0
- package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
- package/lib/testing/make-noop-shared-services.js +28 -2
- package/lib/testing/make-noop-shared-services.js.map +1 -1
- package/lib/testing/make-test-services.d.ts +45 -9
- package/lib/testing/make-test-services.d.ts.map +1 -1
- package/lib/testing/make-test-services.js +19 -3
- package/lib/testing/make-test-services.js.map +1 -1
- package/lib/testing/node/index.d.ts +1 -0
- package/lib/testing/node/index.d.ts.map +1 -1
- package/lib/testing/node/index.js +1 -0
- package/lib/testing/node/index.js.map +1 -1
- package/lib/testing/node/lsp-harness.d.ts.map +1 -1
- package/lib/testing/node/lsp-harness.js +2 -0
- package/lib/testing/node/lsp-harness.js.map +1 -1
- package/lib/testing/node/lsp-server-connection.d.ts +10 -2
- package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
- package/lib/testing/node/lsp-server-connection.js +42 -9
- package/lib/testing/node/lsp-server-connection.js.map +1 -1
- package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
- package/lib/testing/node/scratch-workspace.js +9 -4
- package/lib/testing/node/scratch-workspace.js.map +1 -1
- package/lib/testing/node/spawned-server.d.ts +14 -0
- package/lib/testing/node/spawned-server.d.ts.map +1 -1
- package/lib/testing/node/spawned-server.js +33 -1
- package/lib/testing/node/spawned-server.js.map +1 -1
- package/lib/testing/node/unhandled-rejections.d.ts +20 -0
- package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
- package/lib/testing/node/unhandled-rejections.js +32 -0
- package/lib/testing/node/unhandled-rejections.js.map +1 -0
- package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
- package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
- package/lib/testing/playwright/flaky-network-proxy.js +164 -0
- package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
- package/lib/testing/playwright/index.d.ts +1 -0
- package/lib/testing/playwright/index.d.ts.map +1 -1
- package/lib/testing/playwright/index.js +1 -0
- package/lib/testing/playwright/index.js.map +1 -1
- package/lib/testing/playwright/server-log-capture.d.ts +14 -4
- package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-capture.js +21 -4
- package/lib/testing/playwright/server-log-capture.js.map +1 -1
- package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
- package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
- package/lib/testing/stub-ast-document-manager.d.ts +5 -5
- package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
- package/lib/testing/stub-ast-document-manager.js +4 -6
- package/lib/testing/stub-ast-document-manager.js.map +1 -1
- package/lib/testing/stub-document-builder.d.ts +8 -0
- package/lib/testing/stub-document-builder.d.ts.map +1 -1
- package/lib/testing/stub-document-builder.js +22 -3
- package/lib/testing/stub-document-builder.js.map +1 -1
- package/lib/testing/stub-langium-documents.d.ts +3 -2
- package/lib/testing/stub-langium-documents.d.ts.map +1 -1
- package/lib/testing/stub-langium-documents.js.map +1 -1
- package/lib/testing/stub-model-service.d.ts +5 -4
- package/lib/testing/stub-model-service.d.ts.map +1 -1
- package/lib/testing/stub-model-service.js +2 -2
- package/lib/testing/stub-model-service.js.map +1 -1
- package/lib/util/connection-liveness.d.ts +7 -6
- package/lib/util/connection-liveness.d.ts.map +1 -1
- package/lib/util/connection-liveness.js +35 -14
- package/lib/util/connection-liveness.js.map +1 -1
- package/package.json +17 -8
- package/src/documents/ast-document-manager.ts +129 -36
- package/src/documents/client-ids.ts +5 -22
- package/src/documents/hydranium-text-documents.ts +256 -81
- package/src/documents/language-client-text-shadow.ts +59 -11
- package/src/documents/self-save-registry.ts +52 -13
- package/src/index.ts +6 -0
- package/src/langium/ast-extension/ast-node-builder.ts +66 -22
- package/src/langium/bootstrap.ts +16 -9
- package/src/langium/config/configuration-provider.ts +119 -0
- package/src/langium/config/index.ts +1 -0
- package/src/langium/diagnostics/lsp-logger.ts +41 -6
- package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
- package/src/langium/document-builder/build-session.ts +87 -0
- package/src/langium/document-builder/document-builder.ts +339 -11
- package/src/langium/document-builder/index.ts +1 -0
- package/src/langium/integration-services.ts +67 -6
- package/src/langium/integrity/integrity-service.ts +116 -2
- package/src/langium/keys/containment.ts +87 -0
- package/src/langium/keys/index.ts +1 -0
- package/src/langium/keys/name-based-key-provider.ts +4 -0
- package/src/langium/language-module.ts +38 -0
- package/src/langium/model-service/model-service.ts +266 -34
- package/src/langium/module.ts +106 -21
- package/src/langium/naming/name-provider.ts +27 -5
- package/src/langium/naming/name-separator-validation.ts +34 -5
- package/src/langium/residency/cst-residency-service.ts +36 -14
- package/src/langium/scope/hydranium-scope-provider.ts +60 -22
- package/src/langium/serialization/abstract-serializer.ts +13 -32
- package/src/langium/service-registry.ts +19 -2
- package/src/langium/shared-services.ts +19 -2
- package/src/langium/transfer/transfer-encoder.ts +85 -24
- package/src/langium/trivia/comment-preserver.ts +1100 -0
- package/src/langium/trivia/document-ending-preserver.ts +55 -0
- package/src/langium/trivia/index.ts +14 -0
- package/src/langium/trivia/trivia-contribution.ts +39 -0
- package/src/langium/trivia/trivia-preserver.ts +54 -0
- package/src/langium/trivia/trivia-service.ts +99 -0
- package/src/langium/validation/document-validator.ts +537 -11
- package/src/langium/workspace/document-uri-policy.ts +3 -4
- package/src/langium/workspace/file-system-provider.ts +48 -8
- package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
- package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
- package/src/langium/workspace/in-memory-file-system-provider.ts +53 -10
- package/src/langium/workspace/initialize-workspace.ts +35 -5
- package/src/langium/workspace/langium-documents.ts +106 -33
- package/src/langium/workspace/virtual-document.ts +21 -3
- package/src/locale/index.ts +10 -0
- package/src/locale/server-locale.ts +86 -0
- package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
- package/src/lsp/connection-features.ts +67 -0
- package/src/lsp/diagnostics-connection.ts +56 -0
- package/src/lsp/hydranium-document-update-handler.ts +10 -1
- package/src/lsp/index.ts +3 -1
- package/src/lsp/lsp-latency.ts +62 -0
- package/src/lsp/semantic-token-provider.ts +74 -7
- package/src/lsp/start-language-server.ts +10 -3
- package/src/messages/carriers.ts +78 -0
- package/src/messages/index.ts +36 -0
- package/src/messages/renderer.ts +206 -0
- package/src/node/heap-ceiling.ts +108 -0
- package/src/node/index.ts +1 -0
- package/src/node/latency-from-env.ts +1 -1
- package/src/node/measure-memory.ts +1 -0
- package/src/node/node-file-system-provider.ts +154 -47
- package/src/node/profile-capture.ts +8 -2
- package/src/node/profiling-run.ts +3 -3
- package/src/node/rename-over-open-readers.ts +78 -0
- package/src/node/socket-launcher.ts +18 -2
- package/src/testing/fake-document.ts +24 -5
- package/src/testing/make-noop-shared-services.ts +50 -2
- package/src/testing/make-test-services.ts +74 -18
- package/src/testing/node/index.ts +1 -0
- package/src/testing/node/lsp-harness.ts +2 -0
- package/src/testing/node/lsp-server-connection.ts +60 -10
- package/src/testing/node/scratch-workspace.ts +9 -4
- package/src/testing/node/spawned-server.ts +35 -1
- package/src/testing/node/unhandled-rejections.ts +31 -0
- package/src/testing/playwright/flaky-network-proxy.ts +240 -0
- package/src/testing/playwright/index.ts +1 -0
- package/src/testing/playwright/server-log-capture.ts +24 -5
- package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
- package/src/testing/stub-ast-document-manager.ts +10 -14
- package/src/testing/stub-document-builder.ts +32 -3
- package/src/testing/stub-langium-documents.ts +3 -2
- package/src/testing/stub-model-service.ts +7 -6
- package/src/util/connection-liveness.ts +37 -14
- package/lib/lsp/instrument-connection.d.ts +0 -36
- package/lib/lsp/instrument-connection.d.ts.map +0 -1
- package/lib/lsp/instrument-connection.js +0 -62
- package/lib/lsp/instrument-connection.js.map +0 -1
- package/src/lsp/instrument-connection.ts +0 -66
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
OptionalVersionedTextDocumentIdentifier,
|
|
38
38
|
type RequestHandler,
|
|
39
39
|
type TextDocumentChangeEvent,
|
|
40
|
+
TextDocumentContentChangeEvent as ContentChange,
|
|
40
41
|
TextDocumentEdit,
|
|
41
42
|
TextDocumentSyncKind,
|
|
42
43
|
type TextDocumentWillSaveEvent,
|
|
@@ -89,8 +90,13 @@ export interface HydraniumTextDocumentsOptions<T extends TextDocument = TextDocu
|
|
|
89
90
|
* separately-tested diff/apply-verify abstraction that owns its own baseline
|
|
90
91
|
* text; folding its storage here would couple a clean utility to this record
|
|
91
92
|
* for no real gain.
|
|
93
|
+
*
|
|
94
|
+
* Returned by the `protected` {@link HydraniumTextDocuments.trackingFor}, so an
|
|
95
|
+
* override has to name it. Restating the shape structurally instead compiles
|
|
96
|
+
* until a field is added here, and then fails at the adopter rather than at the
|
|
97
|
+
* change that caused it.
|
|
92
98
|
*/
|
|
93
|
-
interface
|
|
99
|
+
export interface DocumentTrackingRecord {
|
|
94
100
|
/** Client ids currently holding this document open (multi-client membership). */
|
|
95
101
|
readonly clients: Set<string>;
|
|
96
102
|
/** Author of each version, sparse-indexed by the SHARED (server-assigned) version number. */
|
|
@@ -128,13 +134,28 @@ export interface OpenDocument {
|
|
|
128
134
|
readonly clients: readonly string[];
|
|
129
135
|
}
|
|
130
136
|
|
|
137
|
+
/**
|
|
138
|
+
* What {@link HydraniumTextDocuments.commitRepair} did with an integrity repair.
|
|
139
|
+
*
|
|
140
|
+
* Three outcomes rather than a document-or-nothing, because the two ways of
|
|
141
|
+
* getting nothing call for opposite responses: `not-open` means there is no
|
|
142
|
+
* store document to correct and the caller carries on with the one it holds,
|
|
143
|
+
* while `stale` means there IS one and it has moved past the text the repair
|
|
144
|
+
* was computed against — so carrying on would persist a correction over an edit
|
|
145
|
+
* that superseded it. Collapsing them lets the second read as permission to
|
|
146
|
+
* proceed, which is the failure this shape exists to make unrepresentable.
|
|
147
|
+
*/
|
|
148
|
+
export type RepairCommit<T extends TextDocument> =
|
|
149
|
+
{ readonly status: 'committed'; readonly document: T } | { readonly status: 'stale' } | { readonly status: 'not-open' };
|
|
150
|
+
|
|
131
151
|
/**
|
|
132
152
|
* Where a URI's shared version sequence left off — written once at last-client
|
|
133
153
|
* close, consulted at the next open so the sequence CONTINUES instead of
|
|
134
154
|
* restarting at whatever version id the reopening client declares. One entry
|
|
135
|
-
* of {@link HydraniumTextDocuments.__versionSequences}
|
|
155
|
+
* of {@link HydraniumTextDocuments.__versionSequences} — a `protected` field, so
|
|
156
|
+
* a subclass reading the map has to name what it holds.
|
|
136
157
|
*/
|
|
137
|
-
interface VersionSequence {
|
|
158
|
+
export interface VersionSequence {
|
|
138
159
|
/** The shared version at last-client close. */
|
|
139
160
|
readonly version: number;
|
|
140
161
|
/** {@link contentHash} of the synced text at last-client close. */
|
|
@@ -161,19 +182,20 @@ interface ConnectionWithTextDocumentSync {
|
|
|
161
182
|
/**
|
|
162
183
|
* One text pushed to the LSP textual language client by
|
|
163
184
|
* {@link HydraniumTextDocuments.applyEditToLanguageClient} whose echo has not
|
|
164
|
-
* come back yet. One entry of {@link HydraniumTextDocuments.__pendingPushes}
|
|
185
|
+
* come back yet. One entry of {@link HydraniumTextDocuments.__pendingPushes} —
|
|
186
|
+
* a `protected` field, so a subclass reading the queue has to name what it holds.
|
|
165
187
|
*/
|
|
166
|
-
interface
|
|
188
|
+
export interface PendingLanguageClientPush {
|
|
167
189
|
/**
|
|
168
|
-
* The text the client held BEFORE this push
|
|
169
|
-
*
|
|
170
|
-
*
|
|
190
|
+
* The text the client held BEFORE this push, and therefore the text its
|
|
191
|
+
* echo addresses with its ranges. Wider than the baseline the push's edits
|
|
192
|
+
* were diffed against: a full replace is sent with no diff baseline and
|
|
193
|
+
* still lands on a buffer the echo is keyed to.
|
|
171
194
|
*
|
|
172
|
-
* `undefined` when
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
* application is position-independent.
|
|
195
|
+
* `undefined` only when that buffer is unknown — the client never declared
|
|
196
|
+
* one, or a rejection invalidated what was tracked. The echo is then
|
|
197
|
+
* reconstructed against the synced text, which is sound only for a
|
|
198
|
+
* position-independent (full-text) change.
|
|
177
199
|
*/
|
|
178
200
|
readonly before: string | undefined;
|
|
179
201
|
/** {@link contentHash} of the text this push moves the client to. */
|
|
@@ -182,18 +204,26 @@ interface PendingPush {
|
|
|
182
204
|
|
|
183
205
|
/**
|
|
184
206
|
* What an incoming language-client change turns out to be once reconstructed
|
|
185
|
-
* against the
|
|
186
|
-
* {@link HydraniumTextDocuments.classifyLanguageClientChange}.
|
|
207
|
+
* against the buffer its ranges address — the return of
|
|
208
|
+
* {@link HydraniumTextDocuments.classifyLanguageClientChange}. That method is
|
|
209
|
+
* `protected`, so an override has to name every arm it can return.
|
|
187
210
|
*/
|
|
188
|
-
type LanguageClientChangeOrigin =
|
|
211
|
+
export type LanguageClientChangeOrigin =
|
|
189
212
|
/** The client is reporting a text we pushed it. The synced document is already there. */
|
|
190
213
|
| { readonly kind: 'echo' }
|
|
191
214
|
/**
|
|
192
|
-
* The client's buffer
|
|
193
|
-
* a
|
|
194
|
-
* reconstructed text is what it now holds, and is authoritative.
|
|
215
|
+
* The client's buffer holds a text we did not push it — a keystroke that
|
|
216
|
+
* raced a push, or an edit to a buffer the store has already been written
|
|
217
|
+
* past. The reconstructed text is what it now holds, and is authoritative.
|
|
218
|
+
*/
|
|
219
|
+
| { readonly kind: 'divergent'; readonly text: string }
|
|
220
|
+
/**
|
|
221
|
+
* The change carries ranges and no known text addresses them, so no
|
|
222
|
+
* reconstruction is offered. Adopting one anyway splices the document and
|
|
223
|
+
* stores an edit nobody made; dropping costs at most the one keystroke the
|
|
224
|
+
* client still holds and the next push contradicts.
|
|
195
225
|
*/
|
|
196
|
-
| { readonly kind: '
|
|
226
|
+
| { readonly kind: 'unreconstructable' };
|
|
197
227
|
|
|
198
228
|
/**
|
|
199
229
|
* Cheap, stable, non-cryptographic content hash (cyrb53) for the version
|
|
@@ -229,7 +259,7 @@ function contentHash(text: string): string {
|
|
|
229
259
|
* per-client staleness guard and never leak into the shared sequence —
|
|
230
260
|
* the two are different things (an editor's edit-operation counter vs the
|
|
231
261
|
* document's content-revision number), and splicing them lets versions drift
|
|
232
|
-
* silently past
|
|
262
|
+
* silently past based-on gate holders.
|
|
233
263
|
* - Version-author history so each edit is attributable to its originating client.
|
|
234
264
|
* - Pending-content staging used by the integrity service to thread corrections
|
|
235
265
|
* through `workspace/applyEdit` cycles for currently-closed documents.
|
|
@@ -240,23 +270,23 @@ function contentHash(text: string): string {
|
|
|
240
270
|
*/
|
|
241
271
|
export class HydraniumTextDocuments<T extends TextDocument = TextDocument> extends NormalizedTextDocuments<T> {
|
|
242
272
|
/**
|
|
243
|
-
* Per-URI client-facing tracking ({@link
|
|
273
|
+
* Per-URI client-facing tracking ({@link DocumentTrackingRecord}): client
|
|
244
274
|
* membership, version-author history, and integrity-staged pending content,
|
|
245
275
|
* keyed by normalized URI. One record per URI so the last-client close clears
|
|
246
276
|
* every axis atomically. The language-client diff baseline stays in
|
|
247
277
|
* {@link __shadow} (its own abstraction); the parsed-document store stays in
|
|
248
278
|
* the inherited `__syncedDocuments`.
|
|
249
279
|
*/
|
|
250
|
-
protected __documents = new Map<CanonicalUri,
|
|
280
|
+
protected __documents = new Map<CanonicalUri, DocumentTrackingRecord>();
|
|
251
281
|
|
|
252
282
|
/**
|
|
253
283
|
* Per-URI shared-version continuity across close/reopen cycles
|
|
254
284
|
* ({@link VersionSequence}), written at last-client close and consulted by
|
|
255
|
-
* the next first-client open. DELIBERATELY outside {@link
|
|
285
|
+
* the next first-client open. DELIBERATELY outside {@link DocumentTrackingRecord}:
|
|
256
286
|
* that record is deleted on last close, while the version sequence must
|
|
257
287
|
* survive it — the shared version is a server-owned, monotonic,
|
|
258
288
|
* advances-iff-content-changes counter that never resets while the server
|
|
259
|
-
* lives. That invariant is what makes an optimistic
|
|
289
|
+
* lives. That invariant is what makes an optimistic based-on gate
|
|
260
290
|
* sound: "version unchanged ⇔ content unchanged", with no false conflicts
|
|
261
291
|
* from close/reopen version resets and no false passes from a reopened
|
|
262
292
|
* sequence coincidentally landing on a stale writer's number.
|
|
@@ -269,7 +299,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
269
299
|
/**
|
|
270
300
|
* Texts pushed to the LSP textual language client via
|
|
271
301
|
* {@link applyEditToLanguageClient} whose echoes have not come back yet
|
|
272
|
-
* ({@link
|
|
302
|
+
* ({@link PendingLanguageClientPush}), keyed like the shadow by language-client URI.
|
|
273
303
|
* Outbound pushes and inbound echoes are uncorrelated on the wire; this
|
|
274
304
|
* FIFO is the explicit correlation, and it is what
|
|
275
305
|
* {@link classifyLanguageClientChange} reconstructs against.
|
|
@@ -291,7 +321,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
291
321
|
* a pathological echo that never arrives; the memory cost until then is
|
|
292
322
|
* one pre-push text per in-flight push, for milliseconds.
|
|
293
323
|
*/
|
|
294
|
-
protected readonly __pendingPushes = new Map<LanguageClientUri,
|
|
324
|
+
protected readonly __pendingPushes = new Map<LanguageClientUri, PendingLanguageClientPush[]>();
|
|
295
325
|
|
|
296
326
|
/**
|
|
297
327
|
* Tracked text content per URI for the LSP textual language client (Monaco / VS Code).
|
|
@@ -471,15 +501,16 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
471
501
|
}
|
|
472
502
|
record.clientVersions.set(clientId, td.version);
|
|
473
503
|
|
|
474
|
-
// A language-client change
|
|
475
|
-
//
|
|
476
|
-
//
|
|
504
|
+
// A language-client change is keyed to the buffer that client holds,
|
|
505
|
+
// which is the synced text only while the two agree — an authored
|
|
506
|
+
// write advances the synced text without the client knowing, and a
|
|
507
|
+
// push in flight moves the client without the store knowing.
|
|
477
508
|
// Resolve which it is, and against what, before touching anything.
|
|
478
|
-
const
|
|
509
|
+
const origin =
|
|
479
510
|
clientId === LANGUAGE_CLIENT_ID
|
|
480
511
|
? this.classifyLanguageClientChange(this.toLanguageClientUri(td.uri), document, changes)
|
|
481
512
|
: undefined;
|
|
482
|
-
if (
|
|
513
|
+
if (origin?.kind === 'echo') {
|
|
483
514
|
// The client is reporting a text we pushed it, so the synced
|
|
484
515
|
// document is ALREADY there and the changes must not be applied a
|
|
485
516
|
// second time. Nothing is minted and no rebuild fires. The
|
|
@@ -491,19 +522,23 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
491
522
|
this.logUri(uri, `Skip rebuild: echo of a server-authored push (client version ${td.version})`, 'debug');
|
|
492
523
|
return;
|
|
493
524
|
}
|
|
525
|
+
if (origin?.kind === 'unreconstructable') {
|
|
526
|
+
this.tracer.with(uri).warn(`Drop change: no known client buffer for its ranges (client version ${td.version})`);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
494
529
|
|
|
495
530
|
// The SHARED version advances iff the content actually changes — the
|
|
496
|
-
// invariant optimistic
|
|
531
|
+
// invariant optimistic based-on gates rely on. The new text is
|
|
497
532
|
// only known after applying the (possibly incremental) changes, so
|
|
498
533
|
// apply at a tentative +1 and roll the version back on an identical
|
|
499
534
|
// result (an empty-changes update only re-stamps the version).
|
|
500
535
|
//
|
|
501
|
-
// A divergent
|
|
502
|
-
//
|
|
503
|
-
//
|
|
536
|
+
// A divergent change is applied as its RECONSTRUCTED text rather than
|
|
537
|
+
// as its own ranges: those ranges address the client's own buffer, so
|
|
538
|
+
// applying them here would splice the wrong lines.
|
|
504
539
|
const previousText = document.getText();
|
|
505
540
|
const sharedVersion = document.version;
|
|
506
|
-
document = this.configuration.update(document,
|
|
541
|
+
document = this.configuration.update(document, origin === undefined ? changes : [{ text: origin.text }], sharedVersion + 1);
|
|
507
542
|
const changed = document.getText() !== previousText;
|
|
508
543
|
if (!changed) {
|
|
509
544
|
document = this.configuration.update(document, [], sharedVersion);
|
|
@@ -544,7 +579,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
544
579
|
* A content-identical write still fires the change event (rebuild): the
|
|
545
580
|
* authored write's server-side rebuild is correctness-bearing, it just
|
|
546
581
|
* mints no new version — nothing observable changed, so watchers'
|
|
547
|
-
*
|
|
582
|
+
* based-on versions stay valid.
|
|
548
583
|
*
|
|
549
584
|
* Returns the resulting shared version. Throws when the document is not
|
|
550
585
|
* open — callers (`AstDocumentManager.update`) open first.
|
|
@@ -644,18 +679,20 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
644
679
|
if (this.isOpenInClient(uri, clientId)) {
|
|
645
680
|
// Already open for this client under this canonical identity. If this is a
|
|
646
681
|
// NEW client-facing URI for the same file (a second tab reached via a
|
|
647
|
-
// divergent path, e.g. a symlink and its real path), record it
|
|
648
|
-
//
|
|
649
|
-
//
|
|
682
|
+
// divergent path, e.g. a symlink and its real path), record it so outbound
|
|
683
|
+
// edits reach this tab too — but do NOT re-fire open/rebuild; the document
|
|
684
|
+
// is already live.
|
|
650
685
|
if (clientId === LANGUAGE_CLIENT_ID) {
|
|
651
686
|
const clientFacing = this.toLanguageClientUri(td.uri);
|
|
652
687
|
const record = this.__documents.get(uri);
|
|
653
688
|
if (record && !record.languageClientUris?.has(clientFacing)) {
|
|
654
689
|
(record.languageClientUris ??= new Set<LanguageClientUri>()).add(clientFacing);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
690
|
+
// This tab's own buffer, NOT the synced text: a second tab is a second
|
|
691
|
+
// client model, read from disk, so a server-authored write already
|
|
692
|
+
// applied to the first tab leaves it BEHIND the synced document. Keying
|
|
693
|
+
// a diff to the synced text here addresses lines this tab does not have
|
|
694
|
+
// and drops content it does.
|
|
695
|
+
this.__shadow.setOpenedText(clientFacing, td.text);
|
|
659
696
|
}
|
|
660
697
|
}
|
|
661
698
|
return;
|
|
@@ -679,7 +716,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
679
716
|
const source = pendingText ? ', source=pending' : '';
|
|
680
717
|
// The SHARED version is server-assigned: continue the persisted
|
|
681
718
|
// sequence — same version when the content is unchanged since the
|
|
682
|
-
// last close (so watchers'
|
|
719
|
+
// last close (so watchers' based-on versions stay valid), one
|
|
683
720
|
// step when it changed (so no stale pointer can coincidentally pass
|
|
684
721
|
// the optimistic gate). Only a first-ever open adopts the client's
|
|
685
722
|
// declared id as the sequence seed.
|
|
@@ -694,22 +731,77 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
694
731
|
// Baseline the shadow to what Monaco just opened so the next outbound
|
|
695
732
|
// applyEditToLanguageClient diffs against the right starting point. Keyed by the
|
|
696
733
|
// language-client URI (what Monaco holds), not the canonical document key.
|
|
697
|
-
|
|
734
|
+
//
|
|
735
|
+
// The CLIENT's declared text, never the synced document: staged content
|
|
736
|
+
// consumed above leaves the two different, and a baseline asserting the
|
|
737
|
+
// client already holds the staged text suppresses the one sync that would
|
|
738
|
+
// deliver it. Skipped entirely when a push is already outstanding for this
|
|
739
|
+
// URI — an `applyEdit` to a closed file has the client open from disk and
|
|
740
|
+
// apply afterwards, so that shadow records what it is about to hold and
|
|
741
|
+
// disk text would key the next diff to a buffer nobody has.
|
|
742
|
+
const clientFacing = this.toLanguageClientUri(td.uri);
|
|
743
|
+
if (!this.__shadow.isTracked(clientFacing)) {
|
|
744
|
+
this.__shadow.set(clientFacing, td.text);
|
|
745
|
+
}
|
|
698
746
|
}
|
|
699
747
|
const toFire = Object.freeze({ document, clientId });
|
|
700
748
|
this.__onDidOpen.fire(toFire);
|
|
701
749
|
this.__onDidChangeContent.fire(toFire);
|
|
702
750
|
} else {
|
|
703
751
|
// An additional client attaches to a document already open by another client.
|
|
704
|
-
this.
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
752
|
+
this.logClientJoined(uri, clientId, document.version, existingClients);
|
|
753
|
+
if (clientId === LANGUAGE_CLIENT_ID) {
|
|
754
|
+
// Monaco's own buffer, which on this path is NOT the synced text — another
|
|
755
|
+
// client opened the document and may already have changed it. Recorded as
|
|
756
|
+
// an equality-only baseline (never a diff one, see `setOpenedText`) so the
|
|
757
|
+
// refresh below does not push a full replace of content Monaco already
|
|
758
|
+
// holds, which would dirty the file on open.
|
|
759
|
+
this.__shadow.setOpenedText(this.toLanguageClientUri(td.uri), td.text);
|
|
760
|
+
}
|
|
709
761
|
this.refreshContent(uri, clientId);
|
|
710
762
|
}
|
|
711
763
|
}
|
|
712
764
|
|
|
765
|
+
/**
|
|
766
|
+
* Record `clientId` as an additional holder of a document another client
|
|
767
|
+
* already has open.
|
|
768
|
+
*
|
|
769
|
+
* Unlike the attach branch of {@link notifyDidOpenTextDocument} this does
|
|
770
|
+
* not refresh. That branch re-renders an attaching TEXTUAL view; a client
|
|
771
|
+
* reaching a document through a non-textual route has nothing to re-render,
|
|
772
|
+
* and refreshing per attach turns each into a Langium rebuild and dependent
|
|
773
|
+
* relink cascade.
|
|
774
|
+
*
|
|
775
|
+
* The staleness guard baselines at the SYNCED version, not a declared buffer
|
|
776
|
+
* version, because a client arriving this way holds no buffer of its own.
|
|
777
|
+
* Unifying the two routes therefore rebaselines a textual client's guard to a
|
|
778
|
+
* version it never declared.
|
|
779
|
+
*
|
|
780
|
+
* Returns whether a hold was added — `false` when `uri` is not open, or
|
|
781
|
+
* `clientId` already holds it.
|
|
782
|
+
*/
|
|
783
|
+
attachClient(uri: DocumentUri, clientId: string): boolean {
|
|
784
|
+
const key = this.documentKey(uri);
|
|
785
|
+
const document = this.__syncedDocuments.get(key);
|
|
786
|
+
if (!document || this.isOpenInClient(key, clientId)) {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
const record = this.trackingFor(key);
|
|
790
|
+
const existingClients = [...record.clients];
|
|
791
|
+
record.clients.add(clientId);
|
|
792
|
+
record.clientVersions.set(clientId, document.version);
|
|
793
|
+
this.logClientJoined(key, clientId, document.version, existingClients);
|
|
794
|
+
return true;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
protected logClientJoined(uri: DocumentUri, clientId: string, version: number, existingClients: readonly string[]): void {
|
|
798
|
+
this.log(
|
|
799
|
+
uri,
|
|
800
|
+
`Attach client: ${clientId} joined existing document (version ${version}, ` +
|
|
801
|
+
`now open in: ${[...existingClients, clientId].join(', ')})`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
|
|
713
805
|
refreshContent(uri: DocumentUri, clientId: string): void {
|
|
714
806
|
const syncedDocument = this.__syncedDocuments.get(this.documentKey(uri));
|
|
715
807
|
if (syncedDocument) {
|
|
@@ -730,7 +822,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
730
822
|
* two URIs for one physical file (a symlink path and its real path) into a
|
|
731
823
|
* single registration — dedup at the editor layer, not just in
|
|
732
824
|
* `LangiumDocuments`. The URI the client opened under is preserved separately
|
|
733
|
-
* for egress addressing (see {@link
|
|
825
|
+
* for egress addressing (see {@link DocumentTrackingRecord.languageClientUris}). The
|
|
734
826
|
* policy is always bound (the framework defaults it to
|
|
735
827
|
* `DefaultDocumentUriPolicy`, where canonical ≡ syntactic normalize).
|
|
736
828
|
*/
|
|
@@ -751,7 +843,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
751
843
|
}
|
|
752
844
|
|
|
753
845
|
/** Get-or-create the per-URI tracking record. `uri` must already be a {@link documentKey}. */
|
|
754
|
-
protected trackingFor(uri: CanonicalUri):
|
|
846
|
+
protected trackingFor(uri: CanonicalUri): DocumentTrackingRecord {
|
|
755
847
|
let record = this.__documents.get(uri);
|
|
756
848
|
if (!record) {
|
|
757
849
|
record = { clients: new Set(), versionAuthors: [], clientVersions: new Map() };
|
|
@@ -790,7 +882,7 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
790
882
|
* `0` for a URI this store has never seen. The shared sequence is
|
|
791
883
|
* server-owned and monotonic across close/reopen cycles, and advances
|
|
792
884
|
* exactly when the synced content changes — which is what makes it a sound
|
|
793
|
-
* optimistic-concurrency token (
|
|
885
|
+
* optimistic-concurrency token (based-on gates): version unchanged ⇔
|
|
794
886
|
* content unchanged.
|
|
795
887
|
*/
|
|
796
888
|
version(uri: DocumentUri): number {
|
|
@@ -832,6 +924,51 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
832
924
|
return stepped.version;
|
|
833
925
|
}
|
|
834
926
|
|
|
927
|
+
/**
|
|
928
|
+
* Commit an integrity repair into the OPEN document for `uri`, returning the
|
|
929
|
+
* document that now holds it, or `undefined` when there was nothing here to
|
|
930
|
+
* commit into.
|
|
931
|
+
*
|
|
932
|
+
* The one thing this exists to do is address the store BY URI. A repair
|
|
933
|
+
* reaches the resync as a text-document object, and that object is the
|
|
934
|
+
* store's own only on the LSP path; a document built for an already-open URI
|
|
935
|
+
* through `LangiumDocumentFactory.fromString` carries its own, so writing the
|
|
936
|
+
* repair into it corrects a copy the store does not know about and the editor
|
|
937
|
+
* never sees.
|
|
938
|
+
*
|
|
939
|
+
* `parsedFrom` is the text the repaired AST was parsed from, and a mismatch
|
|
940
|
+
* against the current content REFUSES the commit: the store has moved on,
|
|
941
|
+
* which means an editor change landed after that parse, and committing would
|
|
942
|
+
* overwrite a newer edit with a repair computed against text the user has
|
|
943
|
+
* already replaced. The build that change provokes recomputes the repair.
|
|
944
|
+
*
|
|
945
|
+
* Deliberately NOT compared by version. A separately created document seeds
|
|
946
|
+
* its own numbering, so requiring the two to agree would refuse every commit
|
|
947
|
+
* on the path this exists for. Content is the thing both sides can be held to.
|
|
948
|
+
*
|
|
949
|
+
* The version is KEPT rather than stepped, and no change event is fired: the
|
|
950
|
+
* repair rides the build already under way, so minting a version would
|
|
951
|
+
* invalidate the based-on token every watcher just took, and an event would
|
|
952
|
+
* re-enter the build that is mid-flight.
|
|
953
|
+
*/
|
|
954
|
+
commitRepair(uri: DocumentUri, parsedFrom: string, repaired: string): RepairCommit<T> {
|
|
955
|
+
const key = this.documentKey(uri);
|
|
956
|
+
const document = this.__syncedDocuments.get(key);
|
|
957
|
+
if (document === undefined) {
|
|
958
|
+
return { status: 'not-open' };
|
|
959
|
+
}
|
|
960
|
+
if (document.getText() !== parsedFrom) {
|
|
961
|
+
this.logUri(key, 'Refuse repair commit: the open document moved on from the text the AST was parsed from', 'debug');
|
|
962
|
+
return { status: 'stale' };
|
|
963
|
+
}
|
|
964
|
+
// Reassigned rather than mutated in place: the default configuration
|
|
965
|
+
// updates and returns the SAME instance, but an adopter-supplied one may
|
|
966
|
+
// return a new object, and the store must end up holding whichever it is.
|
|
967
|
+
const updated = this.configuration.update(document, [{ text: repaired }], document.version);
|
|
968
|
+
this.__syncedDocuments.set(key, updated);
|
|
969
|
+
return { status: 'committed', document: updated };
|
|
970
|
+
}
|
|
971
|
+
|
|
835
972
|
getAuthor(uri: DocumentUri, version?: number): string | undefined {
|
|
836
973
|
const history = this.__documents.get(this.documentKey(uri))?.versionAuthors;
|
|
837
974
|
// Either the requested version, or the latest. `version !== undefined` so we treat 0 correctly.
|
|
@@ -898,18 +1035,20 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
898
1035
|
}
|
|
899
1036
|
|
|
900
1037
|
/**
|
|
901
|
-
* Stages integrity-updated content for a
|
|
1038
|
+
* Stages integrity-updated content for a document no editor holds.
|
|
902
1039
|
*
|
|
903
1040
|
* When `workspace/applyEdit` targets a closed file, the client opens it from
|
|
904
1041
|
* disk (stale) and sends `didOpen` before applying the edit. This staged
|
|
905
1042
|
* content is consumed by {@link notifyDidOpenTextDocument} to replace the
|
|
906
1043
|
* stale disk text, preventing a brief revert of the integrity update.
|
|
907
1044
|
*
|
|
908
|
-
*
|
|
909
|
-
*
|
|
910
|
-
*
|
|
911
|
-
* entry
|
|
912
|
-
*
|
|
1045
|
+
* Only a FIRST open consumes it. The integrity service stages whenever the
|
|
1046
|
+
* URI is not open in the language client, which includes a URI held only
|
|
1047
|
+
* through another head; an editor attaching to that URI joins the existing
|
|
1048
|
+
* entry and never reads the stage, and the last close discards it with the
|
|
1049
|
+
* tracking record. For a closed URI an entry lingers only if
|
|
1050
|
+
* `workspace/applyEdit` fails and the file is never opened — the memory cost
|
|
1051
|
+
* is one serialised string per URI.
|
|
913
1052
|
*/
|
|
914
1053
|
stagePendingContent(uri: DocumentUri, text: string): void {
|
|
915
1054
|
this.trackingFor(this.documentKey(uri)).pendingContent = text;
|
|
@@ -967,10 +1106,11 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
967
1106
|
const targets: Iterable<LanguageClientUri> = recorded && recorded.size > 0 ? recorded : [this.toLanguageClientUri(uri)];
|
|
968
1107
|
let lastResult: ApplyWorkspaceEditResult | undefined;
|
|
969
1108
|
for (const targetUri of targets) {
|
|
970
|
-
// Read BEFORE computeEdits, which overwrites
|
|
971
|
-
//
|
|
972
|
-
// echo of
|
|
973
|
-
|
|
1109
|
+
// Read BEFORE computeEdits, which overwrites the baseline and drops
|
|
1110
|
+
// the opened snapshot. This is the text the client holds, and
|
|
1111
|
+
// therefore the only text its echo of this push can be reconstructed
|
|
1112
|
+
// against — whether or not the push below is keyed to it.
|
|
1113
|
+
const before = this.__shadow.clientText(targetUri);
|
|
974
1114
|
const edits = this.__shadow.computeEdits(targetUri, newText);
|
|
975
1115
|
if (edits.length === 0) {
|
|
976
1116
|
continue;
|
|
@@ -985,9 +1125,20 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
985
1125
|
// it is exactly what the caller retries with after a rejection, so
|
|
986
1126
|
// gating it there would refuse the recovery too.
|
|
987
1127
|
const version = isFullReplace(edits) ? UNKNOWN_CLIENT_VERSION : this.languageClientVersion(uri);
|
|
1128
|
+
// A full `ApplyWorkspaceEditParams`, `edit` and all — NOT a bare
|
|
1129
|
+
// `WorkspaceEdit` with a `label` beside it. `applyEdit` takes
|
|
1130
|
+
// `ApplyWorkspaceEditParams | WorkspaceEdit` and discriminates on
|
|
1131
|
+
// `!!value.edit`, so `{ label, documentChanges }` is wrapped as
|
|
1132
|
+
// `{ edit: { label, documentChanges } }` — putting the label inside
|
|
1133
|
+
// the edit, where LSP defines no such field and no client reads it.
|
|
1134
|
+
// The union is also what hides it at compile time: excess-property
|
|
1135
|
+
// checking admits a property present in EITHER member, so an object
|
|
1136
|
+
// matching neither type-checks against the union.
|
|
988
1137
|
const result = await connection.workspace.applyEdit({
|
|
989
1138
|
label: options?.label,
|
|
990
|
-
|
|
1139
|
+
edit: {
|
|
1140
|
+
documentChanges: [TextDocumentEdit.create(OptionalVersionedTextDocumentIdentifier.create(targetUri, version), edits)]
|
|
1141
|
+
}
|
|
991
1142
|
});
|
|
992
1143
|
if (result && result.applied === false) {
|
|
993
1144
|
this.__shadow.invalidate(targetUri);
|
|
@@ -1043,12 +1194,17 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
1043
1194
|
|
|
1044
1195
|
/**
|
|
1045
1196
|
* Decide what an incoming language-client change actually is, by
|
|
1046
|
-
* reconstructing the client's resulting buffer against the
|
|
1047
|
-
* in flight for
|
|
1197
|
+
* reconstructing the client's resulting buffer against the text its ranges
|
|
1198
|
+
* address — the pre-push buffer of the OLDEST push still in flight for
|
|
1199
|
+
* `clientFacing`, else whatever that client is believed to hold.
|
|
1048
1200
|
*
|
|
1049
|
-
* `undefined` when
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1201
|
+
* `undefined` when the client's ranges address the synced text itself —
|
|
1202
|
+
* nothing in flight, and no evidence the client holds anything else. That
|
|
1203
|
+
* is the ordinary path, and the caller then applies the ranges directly.
|
|
1204
|
+
* The two texts part company without a push in flight whenever a client
|
|
1205
|
+
* attaches to a document another client has already written: it opened
|
|
1206
|
+
* from disk, so applying its ranges to the synced text splices lines they
|
|
1207
|
+
* never addressed.
|
|
1052
1208
|
*
|
|
1053
1209
|
* **Why the oldest, and why reconstruct at all.** The client applies our
|
|
1054
1210
|
* pushes in order and echoes each against the buffer it held before that
|
|
@@ -1079,22 +1235,41 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
|
|
|
1079
1235
|
document: T,
|
|
1080
1236
|
changes: TextDocumentContentChangeEvent[]
|
|
1081
1237
|
): LanguageClientChangeOrigin | undefined {
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1238
|
+
const queued = this.__pendingPushes.get(clientFacing);
|
|
1239
|
+
const pending = queued?.length ? queued : undefined;
|
|
1240
|
+
// The text the client's ranges address: the buffer it held before the
|
|
1241
|
+
// oldest push still in flight, else the buffer it is believed to hold.
|
|
1242
|
+
const clientText = pending?.[0].before ?? this.__shadow.clientText(clientFacing);
|
|
1243
|
+
if (pending === undefined && (clientText === undefined || clientText === document.getText())) {
|
|
1084
1244
|
return undefined;
|
|
1085
1245
|
}
|
|
1246
|
+
if (pending !== undefined && pending[0].before === undefined && changes.some(change => ContentChange.isIncremental(change))) {
|
|
1247
|
+
// A push sent to a client whose buffer was unknown — a first sync, or
|
|
1248
|
+
// the recovery push after a rejection. The shadow now holds the text
|
|
1249
|
+
// that push MOVES the client to, which is the one text the echo's
|
|
1250
|
+
// ranges provably do not address, so the fallback above is a baseline
|
|
1251
|
+
// known to be wrong rather than merely unverified. Drop the queue and
|
|
1252
|
+
// the shadow: the next sync is then a full replace, which lands on
|
|
1253
|
+
// whatever the client holds. A full-text change is exempt because it
|
|
1254
|
+
// reconstructs identically against any baseline.
|
|
1255
|
+
pending.length = 0;
|
|
1256
|
+
this.__shadow.invalidate(clientFacing);
|
|
1257
|
+
return { kind: 'unreconstructable' };
|
|
1258
|
+
}
|
|
1086
1259
|
// Through the configured factories, not `TextDocument` directly, so an
|
|
1087
1260
|
// adopter's custom text-document type governs how the ranges are applied
|
|
1088
1261
|
// here exactly as it does on the synced document.
|
|
1089
|
-
const probe = this.create(clientFacing, document.languageId, 0,
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1262
|
+
const probe = this.create(clientFacing, document.languageId, 0, clientText ?? document.getText());
|
|
1263
|
+
const reconstructed = this.update(probe, changes, 0).getText();
|
|
1264
|
+
if (pending !== undefined) {
|
|
1265
|
+
const matchIndex = pending.findIndex(push => push.afterHash === contentHash(reconstructed));
|
|
1266
|
+
if (matchIndex >= 0) {
|
|
1267
|
+
pending.splice(0, matchIndex + 1);
|
|
1268
|
+
return { kind: 'echo' };
|
|
1269
|
+
}
|
|
1270
|
+
pending.length = 0;
|
|
1095
1271
|
}
|
|
1096
|
-
|
|
1097
|
-
return { kind: 'divergent', text: clientText };
|
|
1272
|
+
return { kind: 'divergent', text: reconstructed };
|
|
1098
1273
|
}
|
|
1099
1274
|
|
|
1100
1275
|
/**
|