@hydranium/core 1.0.0-next.19 → 1.0.0-next.193
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/lib/documents/ast-document-manager.d.ts +222 -63
- package/lib/documents/ast-document-manager.d.ts.map +1 -1
- package/lib/documents/ast-document-manager.js +250 -83
- 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/client-session-errors.d.ts +15 -0
- package/lib/documents/client-session-errors.d.ts.map +1 -0
- package/lib/documents/client-session-errors.js +15 -0
- package/lib/documents/client-session-errors.js.map +1 -0
- package/lib/documents/client-session-registry.d.ts +138 -0
- package/lib/documents/client-session-registry.d.ts.map +1 -0
- package/lib/documents/client-session-registry.js +187 -0
- package/lib/documents/client-session-registry.js.map +1 -0
- package/lib/documents/file-system-task-queue.d.ts +49 -0
- package/lib/documents/file-system-task-queue.d.ts.map +1 -0
- package/lib/documents/file-system-task-queue.js +34 -0
- package/lib/documents/file-system-task-queue.js.map +1 -0
- package/lib/documents/hydranium-text-documents.d.ts +523 -101
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
- package/lib/documents/hydranium-text-documents.js +961 -177
- package/lib/documents/hydranium-text-documents.js.map +1 -1
- package/lib/documents/index.d.ts +5 -0
- package/lib/documents/index.d.ts.map +1 -1
- package/lib/documents/index.js +5 -0
- package/lib/documents/index.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/model-ledger.d.ts +47 -0
- package/lib/documents/model-ledger.d.ts.map +1 -0
- package/lib/documents/model-ledger.js +30 -0
- package/lib/documents/model-ledger.js.map +1 -0
- 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/documents/version-sync-service.d.ts +134 -0
- package/lib/documents/version-sync-service.d.ts.map +1 -0
- package/lib/documents/version-sync-service.js +171 -0
- package/lib/documents/version-sync-service.js.map +1 -0
- 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-extension-service.d.ts.map +1 -1
- package/lib/langium/ast-extension/ast-extension-service.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 +32 -9
- package/lib/langium/bootstrap.js.map +1 -1
- package/lib/langium/build-phase-pass/build-phase-pass-service.d.ts.map +1 -1
- package/lib/langium/build-phase-pass/build-phase-pass-service.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/config/settings.js.map +1 -1
- package/lib/langium/diagnostics/hydranium-langium-profiler.d.ts.map +1 -1
- package/lib/langium/diagnostics/hydranium-langium-profiler.js.map +1 -1
- package/lib/langium/diagnostics/logger.d.ts +10 -3
- package/lib/langium/diagnostics/logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/logger.js +12 -5
- package/lib/langium/diagnostics/logger.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 +56 -13
- package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
- package/lib/langium/diagnostics/server-tracer.d.ts.map +1 -1
- package/lib/langium/diagnostics/server-tracer.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 +382 -29
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +659 -61
- 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/documentation/comment-provider.d.ts.map +1 -1
- package/lib/langium/documentation/comment-provider.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-rule.d.ts +13 -5
- package/lib/langium/integrity/integrity-rule.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-rule.js.map +1 -1
- package/lib/langium/integrity/integrity-service.d.ts +58 -13
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-service.js +204 -29
- 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/keys/positional-key-provider.d.ts.map +1 -1
- package/lib/langium/keys/positional-key-provider.js.map +1 -1
- package/lib/langium/labeling/label-provider.d.ts.map +1 -1
- package/lib/langium/labeling/label-provider.js.map +1 -1
- package/lib/langium/language-module.d.ts +27 -2
- package/lib/langium/language-module.d.ts.map +1 -1
- package/lib/langium/language-module.js +16 -2
- package/lib/langium/language-module.js.map +1 -1
- package/lib/langium/model-service/client-session.d.ts +330 -0
- package/lib/langium/model-service/client-session.d.ts.map +1 -0
- package/lib/langium/model-service/client-session.js +418 -0
- package/lib/langium/model-service/client-session.js.map +1 -0
- package/lib/langium/model-service/index.d.ts +1 -0
- package/lib/langium/model-service/index.d.ts.map +1 -1
- package/lib/langium/model-service/index.js +1 -0
- package/lib/langium/model-service/index.js.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +353 -253
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +361 -408
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/module.d.ts +150 -35
- package/lib/langium/module.d.ts.map +1 -1
- package/lib/langium/module.js +50 -35
- 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/project/abstract-project-manager.d.ts.map +1 -1
- package/lib/langium/project/abstract-project-manager.js +9 -2
- package/lib/langium/project/abstract-project-manager.js.map +1 -1
- package/lib/langium/project/project-change-event.d.ts +2 -2
- package/lib/langium/project/single-project-manager.d.ts.map +1 -1
- package/lib/langium/project/single-project-manager.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 +23 -57
- package/lib/langium/residency/cst-residency-service.js.map +1 -1
- package/lib/langium/scope/ast-node-description-provider.d.ts.map +1 -1
- package/lib/langium/scope/ast-node-description-provider.js.map +1 -1
- package/lib/langium/scope/hydranium-scope-computation.d.ts.map +1 -1
- package/lib/langium/scope/hydranium-scope-computation.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/scope/reference-builder.d.ts.map +1 -1
- package/lib/langium/scope/reference-builder.js.map +1 -1
- package/lib/langium/scope/reference-candidate-provider.d.ts.map +1 -1
- package/lib/langium/scope/reference-candidate-provider.js.map +1 -1
- package/lib/langium/scope/scope-extension-service.d.ts.map +1 -1
- package/lib/langium/scope/scope-extension-service.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/serialization/json-serializer.d.ts +1 -1
- package/lib/langium/serialization/json-serializer.d.ts.map +1 -1
- package/lib/langium/serialization/json-serializer.js.map +1 -1
- package/lib/langium/serialization/yaml-serializer.d.ts.map +1 -1
- package/lib/langium/serialization/yaml-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 +21 -4
- 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 +75 -23
- package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
- package/lib/langium/transfer/transfer-encoder.js +44 -21
- 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/update-rewrite/normalize-empty-strings.d.ts.map +1 -1
- package/lib/langium/update-rewrite/update-rewrite-service.d.ts.map +1 -1
- package/lib/langium/update-rewrite/update-rewrite-service.js.map +1 -1
- package/lib/langium/update-rewrite/update-rewrite.d.ts +1 -1
- 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/validation/validation-contribution-collector.d.ts.map +1 -1
- package/lib/langium/validation/validation-contribution-collector.js.map +1 -1
- package/lib/langium/workspace/document-uri-policy.d.ts +17 -12
- package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
- package/lib/langium/workspace/document-uri-policy.js +10 -9
- package/lib/langium/workspace/document-uri-policy.js.map +1 -1
- package/lib/langium/workspace/file-not-found.d.ts +17 -0
- package/lib/langium/workspace/file-not-found.d.ts.map +1 -0
- package/lib/langium/workspace/file-not-found.js +20 -0
- package/lib/langium/workspace/file-not-found.js.map +1 -0
- package/lib/langium/workspace/file-system-provider.d.ts +84 -8
- package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/file-system-provider.js +93 -14
- package/lib/langium/workspace/file-system-provider.js.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +56 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.js +79 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-lock.d.ts +43 -1
- package/lib/langium/workspace/hydranium-workspace-lock.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-lock.js +58 -1
- package/lib/langium/workspace/hydranium-workspace-lock.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts +81 -8
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.js +128 -10
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts +25 -2
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.js +41 -15
- package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
- package/lib/langium/workspace/index-manager.d.ts.map +1 -1
- package/lib/langium/workspace/index-manager.js.map +1 -1
- package/lib/langium/workspace/initialize-workspace.d.ts +31 -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 +86 -17
- package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
- package/lib/langium/workspace/langium-documents.js +101 -29
- package/lib/langium/workspace/langium-documents.js.map +1 -1
- package/lib/langium/workspace/persistent-file-system-provider.d.ts +0 -7
- package/lib/langium/workspace/persistent-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/persistent-file-system-provider.js +0 -7
- package/lib/langium/workspace/persistent-file-system-provider.js.map +1 -1
- package/lib/langium/workspace/virtual-document.d.ts +48 -15
- package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
- package/lib/langium/workspace/virtual-document.js +78 -18
- package/lib/langium/workspace/virtual-document.js.map +1 -1
- package/lib/langium/workspace/write-lock-scope.d.ts +20 -10
- package/lib/langium/workspace/write-lock-scope.d.ts.map +1 -1
- package/lib/langium/workspace/write-lock-scope.js +12 -4
- package/lib/langium/workspace/write-lock-scope.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 +90 -79
- package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.js +153 -99
- 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/shared-module.d.ts +4 -8
- package/lib/lsp/shared-module.d.ts.map +1 -1
- package/lib/lsp/shared-module.js.map +1 -1
- package/lib/lsp/start-language-server.d.ts +8 -7
- package/lib/lsp/start-language-server.d.ts.map +1 -1
- package/lib/lsp/start-language-server.js +13 -7
- 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 +142 -0
- package/lib/messages/renderer.d.ts.map +1 -0
- package/lib/messages/renderer.js +165 -0
- package/lib/messages/renderer.js.map +1 -0
- package/lib/node/event-loop-monitor.js.map +1 -1
- 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/lint-grammar.js.map +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/memory-monitor.js.map +1 -1
- package/lib/node/node-file-system-provider.d.ts +39 -4
- package/lib/node/node-file-system-provider.d.ts.map +1 -1
- package/lib/node/node-file-system-provider.js +109 -49
- package/lib/node/node-file-system-provider.js.map +1 -1
- package/lib/node/process-memory.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/profile-digest.js.map +1 -1
- package/lib/node/profiling-run.d.ts +13 -3
- package/lib/node/profiling-run.d.ts.map +1 -1
- package/lib/node/profiling-run.js +17 -8
- 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 +16 -1
- package/lib/node/socket-launcher.d.ts.map +1 -1
- package/lib/node/socket-launcher.js +4 -3
- package/lib/node/socket-launcher.js.map +1 -1
- package/lib/node/stdio-launcher.d.ts +1 -1
- package/lib/node/stdio-launcher.d.ts.map +1 -1
- package/lib/node/stdio-launcher.js +1 -1
- package/lib/node/stdio-launcher.js.map +1 -1
- package/lib/testing/document-uri-policy-conformance.js.map +1 -1
- package/lib/testing/fake-description.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/index.d.ts +1 -1
- package/lib/testing/index.d.ts.map +1 -1
- package/lib/testing/index.js +2 -1
- package/lib/testing/index.js.map +1 -1
- package/lib/testing/langium-test-helpers.d.ts.map +1 -1
- package/lib/testing/langium-test-helpers.js +1 -0
- package/lib/testing/langium-test-helpers.js.map +1 -1
- package/lib/testing/make-noop-language-services.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 +69 -14
- package/lib/testing/make-test-services.d.ts.map +1 -1
- package/lib/testing/make-test-services.js +39 -10
- package/lib/testing/make-test-services.js.map +1 -1
- package/lib/testing/make-test-tracer.js.map +1 -1
- package/lib/testing/node/golden-corpus.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 +11 -3
- 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 +10 -5
- 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/browser-capture-bridge.js.map +1 -1
- package/lib/testing/playwright/e2e-profiling.js.map +1 -1
- 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 +24 -8
- package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-capture.js +27 -6
- 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/run-update-pipeline.d.ts +4 -4
- package/lib/testing/run-update-pipeline.d.ts.map +1 -1
- package/lib/testing/stub-ast-document-manager.d.ts +11 -9
- package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
- package/lib/testing/stub-ast-document-manager.js +25 -9
- package/lib/testing/stub-ast-document-manager.js.map +1 -1
- package/lib/testing/stub-document-builder.d.ts +26 -11
- package/lib/testing/stub-document-builder.d.ts.map +1 -1
- package/lib/testing/stub-document-builder.js +57 -4
- package/lib/testing/stub-document-builder.js.map +1 -1
- package/lib/testing/stub-hydranium-text-documents.d.ts +22 -7
- package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
- package/lib/testing/stub-hydranium-text-documents.js +97 -15
- package/lib/testing/stub-hydranium-text-documents.js.map +1 -1
- package/lib/testing/stub-index-manager.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 +7 -6
- package/lib/testing/stub-model-service.d.ts.map +1 -1
- package/lib/testing/stub-model-service.js +4 -4
- package/lib/testing/stub-model-service.js.map +1 -1
- package/lib/testing/stub-project-manager.js.map +1 -1
- package/lib/testing/stub-service-registry.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/lib/util/environment.d.ts.map +1 -1
- package/lib/util/environment.js.map +1 -1
- package/lib/util/registry.d.ts.map +1 -1
- package/package.json +35 -26
- package/src/documents/ast-document-manager.ts +395 -126
- package/src/documents/client-ids.ts +5 -22
- package/src/documents/client-session-errors.ts +24 -0
- package/src/documents/client-session-registry.ts +240 -0
- package/src/documents/file-system-task-queue.ts +68 -0
- package/src/documents/hydranium-text-documents.ts +1175 -225
- package/src/documents/index.ts +5 -0
- package/src/documents/language-client-text-shadow.ts +59 -11
- package/src/documents/model-ledger.ts +66 -0
- package/src/documents/self-save-registry.ts +52 -13
- package/src/documents/version-sync-service.ts +267 -0
- package/src/index.ts +6 -0
- package/src/langium/ast-extension/ast-node-builder.ts +66 -22
- package/src/langium/bootstrap.ts +32 -9
- package/src/langium/config/configuration-provider.ts +119 -0
- package/src/langium/config/index.ts +1 -0
- package/src/langium/diagnostics/logger.ts +12 -5
- package/src/langium/diagnostics/lsp-logger.ts +58 -12
- 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 +776 -59
- package/src/langium/document-builder/index.ts +1 -0
- package/src/langium/integration-services.ts +67 -6
- package/src/langium/integrity/integrity-rule.ts +13 -5
- package/src/langium/integrity/integrity-service.ts +220 -30
- 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 +42 -4
- package/src/langium/model-service/client-session.ts +649 -0
- package/src/langium/model-service/index.ts +1 -0
- package/src/langium/model-service/model-service.ts +570 -433
- package/src/langium/module.ts +201 -64
- package/src/langium/naming/name-provider.ts +27 -5
- package/src/langium/naming/name-separator-validation.ts +34 -5
- package/src/langium/project/abstract-project-manager.ts +9 -1
- package/src/langium/project/project-change-event.ts +2 -2
- package/src/langium/residency/cst-residency-service.ts +46 -15
- 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 +25 -4
- package/src/langium/transfer/transfer-encoder.ts +102 -34
- 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/update-rewrite/update-rewrite.ts +1 -1
- package/src/langium/validation/document-validator.ts +537 -11
- package/src/langium/workspace/document-uri-policy.ts +17 -12
- package/src/langium/workspace/file-not-found.ts +23 -0
- package/src/langium/workspace/file-system-provider.ts +153 -16
- package/src/langium/workspace/hydranium-langium-document-factory.ts +117 -0
- package/src/langium/workspace/hydranium-workspace-lock.ts +86 -2
- package/src/langium/workspace/hydranium-workspace-manager.ts +153 -13
- package/src/langium/workspace/in-memory-file-system-provider.ts +47 -16
- package/src/langium/workspace/initialize-workspace.ts +43 -5
- package/src/langium/workspace/langium-documents.ts +124 -33
- package/src/langium/workspace/persistent-file-system-provider.ts +0 -7
- package/src/langium/workspace/virtual-document.ts +96 -19
- package/src/langium/workspace/write-lock-scope.ts +24 -11
- 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 +185 -106
- 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/shared-module.ts +4 -8
- package/src/lsp/start-language-server.ts +13 -7
- package/src/messages/carriers.ts +78 -0
- package/src/messages/index.ts +36 -0
- package/src/messages/renderer.ts +212 -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 +122 -51
- package/src/node/profile-capture.ts +8 -2
- package/src/node/profiling-run.ts +30 -8
- package/src/node/rename-over-open-readers.ts +78 -0
- package/src/node/socket-launcher.ts +19 -4
- package/src/node/stdio-launcher.ts +1 -2
- package/src/testing/fake-document.ts +24 -5
- package/src/testing/index.ts +2 -1
- package/src/testing/langium-test-helpers.ts +1 -0
- package/src/testing/make-noop-shared-services.ts +50 -2
- package/src/testing/make-test-services.ts +120 -31
- 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 +61 -11
- package/src/testing/node/scratch-workspace.ts +10 -5
- 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 +45 -11
- package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
- package/src/testing/run-update-pipeline.ts +4 -4
- package/src/testing/stub-ast-document-manager.ts +40 -23
- package/src/testing/stub-document-builder.ts +83 -17
- package/src/testing/stub-hydranium-text-documents.ts +133 -23
- package/src/testing/stub-langium-documents.ts +3 -2
- package/src/testing/stub-model-service.ts +9 -8
- package/src/util/connection-liveness.ts +37 -14
- package/src/util/environment.ts +9 -1
- 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
package/src/documents/index.ts
CHANGED
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
10
|
export * from './client-ids.js';
|
|
11
|
+
export * from './client-session-errors.js';
|
|
12
|
+
export * from './client-session-registry.js';
|
|
11
13
|
export * from './language-client-text-shadow.js';
|
|
12
14
|
export * from './hydranium-text-documents.js';
|
|
15
|
+
export * from './model-ledger.js';
|
|
16
|
+
export * from './version-sync-service.js';
|
|
13
17
|
export * from './ast-document-manager.js';
|
|
14
18
|
export * from './self-save-registry.js';
|
|
19
|
+
export * from './file-system-task-queue.js';
|
|
@@ -57,12 +57,23 @@ export function isFullReplace(edits: readonly TextEdit[]): boolean {
|
|
|
57
57
|
* - {@link computeEdits} after each outgoing server→client sync (updates the shadow optimistically).
|
|
58
58
|
* - {@link set} when the server receives a `didChange` from the language client (the client now
|
|
59
59
|
* holds that text; we shouldn't resend it).
|
|
60
|
+
* - {@link setOpenedText} when the client opens a document the server did not baseline for it —
|
|
61
|
+
* an equality-only baseline, since that text is a snapshot rather than a tracked one.
|
|
60
62
|
* - {@link invalidate} when the client rejects an edit (`applied=false`) or a doc is closed —
|
|
61
63
|
* the shadow is now stale and the next sync must fall back to a full replace.
|
|
62
64
|
*/
|
|
63
65
|
export class LanguageClientTextShadow {
|
|
64
66
|
protected shadow = new Map<string, string>();
|
|
65
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Per URI, the text the client declared it opened the document with, held
|
|
70
|
+
* only until a real baseline exists. Consulted by {@link computeEdits} when
|
|
71
|
+
* there is no shadow, and dropped on {@link invalidate}: a snapshot that
|
|
72
|
+
* outlived what the client holds would let the equality check skip an edit
|
|
73
|
+
* the client still needs.
|
|
74
|
+
*/
|
|
75
|
+
protected opened = new Map<string, string>();
|
|
76
|
+
|
|
66
77
|
/**
|
|
67
78
|
* @param onFallback invoked when {@link computeEdits} falls back to a full-range replace due to
|
|
68
79
|
* apply-verify mismatch. Useful for logging regressions.
|
|
@@ -85,23 +96,54 @@ export class LanguageClientTextShadow {
|
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
99
|
+
* Record the buffer the language client declared at `didOpen`, for a URI
|
|
100
|
+
* with no baseline yet.
|
|
101
|
+
*
|
|
102
|
+
* Deliberately NOT a {@link set}: this text is only ever compared for
|
|
103
|
+
* EQUALITY, never diffed against. A line-keyed diff is position-dependent,
|
|
104
|
+
* so keying one to a `didOpen` snapshot the client may have moved past
|
|
105
|
+
* splices its buffer — the failure {@link computeEdits}'s full-replace
|
|
106
|
+
* fallback exists to avoid.
|
|
107
|
+
*/
|
|
108
|
+
setOpenedText(uri: string, text: string): void {
|
|
109
|
+
this.opened.set(uri, text);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The text the language client is believed to hold: the tracked text, else
|
|
114
|
+
* the buffer it declared at `didOpen`.
|
|
90
115
|
*
|
|
91
116
|
* Read at the `workspace/applyEdit` egress BEFORE {@link computeEdits},
|
|
92
|
-
* which overwrites
|
|
93
|
-
*
|
|
94
|
-
* it
|
|
95
|
-
*
|
|
96
|
-
*
|
|
117
|
+
* which overwrites the tracked text and drops the opened snapshot. A change
|
|
118
|
+
* the client sends carries ranges addressing this text and nothing else, so
|
|
119
|
+
* losing it leaves that change unreconstructable — and applying its ranges
|
|
120
|
+
* to the already-advanced synced text splices lines they never addressed.
|
|
121
|
+
*
|
|
122
|
+
* Deliberately wider than what {@link computeEdits} may diff from: an
|
|
123
|
+
* opened snapshot is a position-dependent key to a buffer the client may
|
|
124
|
+
* have moved past, so keying a diff to it splices that buffer.
|
|
97
125
|
*/
|
|
98
|
-
|
|
99
|
-
return this.shadow.get(uri);
|
|
126
|
+
clientText(uri: string): string | undefined {
|
|
127
|
+
return this.shadow.get(uri) ?? this.opened.get(uri);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether a tracked text exists for `uri`, which is what decides whether a
|
|
132
|
+
* push can be diffed at all. Distinct from {@link clientText}, which also
|
|
133
|
+
* answers from the opened snapshot and so cannot settle this.
|
|
134
|
+
*/
|
|
135
|
+
isTracked(uri: string): boolean {
|
|
136
|
+
return this.shadow.has(uri);
|
|
100
137
|
}
|
|
101
138
|
|
|
102
139
|
/** Forget the shadow for a URI; next {@link computeEdits} falls back to a full-document replace. */
|
|
103
140
|
invalidate(uri: string): void {
|
|
104
141
|
this.shadow.delete(uri);
|
|
142
|
+
// Drops the opened snapshot too, and this is the only place that has to:
|
|
143
|
+
// `computeEdits` clears it whenever it consults it, so the sole way it can
|
|
144
|
+
// outlive what the client holds is surviving under a live shadow until that
|
|
145
|
+
// shadow is dropped here.
|
|
146
|
+
this.opened.delete(uri);
|
|
105
147
|
}
|
|
106
148
|
|
|
107
149
|
/**
|
|
@@ -109,7 +151,9 @@ export class LanguageClientTextShadow {
|
|
|
109
151
|
* the tracked shadow to `newText`. Updates the shadow to `newText` so subsequent calls diff
|
|
110
152
|
* against it.
|
|
111
153
|
*
|
|
112
|
-
* - Empty array when the shadow already equals `newText` (caller should skip the RPC)
|
|
154
|
+
* - Empty array when the shadow already equals `newText` (caller should skip the RPC),
|
|
155
|
+
* or when there is no shadow and the client opened the document with exactly `newText`
|
|
156
|
+
* (see {@link setOpenedText}) — pushing it anyway dirties the client's buffer on open.
|
|
113
157
|
* - Full-range replace when there is no shadow (first sync or post-{@link invalidate}).
|
|
114
158
|
* - Line-level diff otherwise. Before returning, we locally reconstruct `newText` by applying
|
|
115
159
|
* the diff to `old`; if that doesn't match, we fall back to a full-range replace and notify
|
|
@@ -124,7 +168,11 @@ export class LanguageClientTextShadow {
|
|
|
124
168
|
this.shadow.set(uri, newText);
|
|
125
169
|
const fullReplace: TextEdit = { range: FULL_RANGE, newText };
|
|
126
170
|
if (old === undefined) {
|
|
127
|
-
|
|
171
|
+
// The opened snapshot has served its purpose either way: the shadow above
|
|
172
|
+
// is now the baseline, so leaving it would only risk a later stale match.
|
|
173
|
+
const openedText = this.opened.get(uri);
|
|
174
|
+
this.opened.delete(uri);
|
|
175
|
+
return openedText === newText ? [] : [fullReplace];
|
|
128
176
|
}
|
|
129
177
|
const edits = diffToEdits(old, newText);
|
|
130
178
|
const verifyDoc = this.documents.create(uri, 'plaintext', 0, old);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { type AstNode } from '@hydranium/langium';
|
|
11
|
+
import { asModelVersion, type ModelVersion, UNRECORDED_VERSION } from '@hydranium/protocol';
|
|
12
|
+
|
|
13
|
+
/** What {@link ModelLedger} holds for one root. */
|
|
14
|
+
export interface ModelRecord {
|
|
15
|
+
readonly version: ModelVersion;
|
|
16
|
+
/** For a root whose CST no longer is the text it describes: changed in place, or shed. */
|
|
17
|
+
readonly text?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The version of the text each root was parsed from, and that text where the
|
|
22
|
+
* root's CST does not hold it. Stores what it is told; the rest of the
|
|
23
|
+
* framework relies on these:
|
|
24
|
+
*
|
|
25
|
+
* - every workspace root is recorded through `VersionSyncService.modelProduced` when produced;
|
|
26
|
+
* - a version is recorded only for text the store held at that version, else `STALE_VERSION`;
|
|
27
|
+
* - the builder's placeholder for a file it has not parsed carries no version;
|
|
28
|
+
* - re-recording a root without text keeps the text recorded before.
|
|
29
|
+
*/
|
|
30
|
+
export interface ModelLedger {
|
|
31
|
+
record(root: AstNode, version: number, text?: string): void;
|
|
32
|
+
/** `UNRECORDED_VERSION`, which every write conflicts with, for a root nothing recorded. */
|
|
33
|
+
versionOf(root: AstNode): ModelVersion;
|
|
34
|
+
/** The recorded text, else the root's CST text; `undefined` for a root with neither. */
|
|
35
|
+
textOf(root: AstNode): string | undefined;
|
|
36
|
+
/** Mark `root`, registered before any parse, as no parse of any text, dropping its record. */
|
|
37
|
+
markPlaceholder(root: AstNode): void;
|
|
38
|
+
/** Keyed on the root, not its state: a rebuilt document is reset below `Parsed` but keeps its root. */
|
|
39
|
+
isPlaceholder(root: AstNode): boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class DefaultModelLedger implements ModelLedger {
|
|
43
|
+
protected readonly records = new WeakMap<AstNode, ModelRecord>();
|
|
44
|
+
protected readonly placeholders = new WeakSet<AstNode>();
|
|
45
|
+
|
|
46
|
+
record(root: AstNode, version: number, text?: string): void {
|
|
47
|
+
this.records.set(root, { version: asModelVersion(version), text: text ?? this.records.get(root)?.text });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
versionOf(root: AstNode): ModelVersion {
|
|
51
|
+
return this.records.get(root)?.version ?? UNRECORDED_VERSION;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
textOf(root: AstNode): string | undefined {
|
|
55
|
+
return this.records.get(root)?.text ?? root.$cstNode?.root.fullText;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
markPlaceholder(root: AstNode): void {
|
|
59
|
+
this.placeholders.add(root);
|
|
60
|
+
this.records.delete(root);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
isPlaceholder(root: AstNode): boolean {
|
|
64
|
+
return this.placeholders.has(root);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -22,10 +22,32 @@ export interface SelfSaveRegistryOptions {
|
|
|
22
22
|
/**
|
|
23
23
|
* Tracks `mtime` of files written by the server itself so the echo
|
|
24
24
|
* `didChangeWatchedFiles` event from the OS file watcher can be suppressed,
|
|
25
|
-
* avoiding redundant rebuilds after each save.
|
|
26
|
-
* (not by a time window) and expire after a TTL.
|
|
25
|
+
* avoiding redundant rebuilds after each save.
|
|
27
26
|
*/
|
|
28
|
-
export
|
|
27
|
+
export interface SelfSaveRegistry {
|
|
28
|
+
/**
|
|
29
|
+
* Record a self-write, and drop any registration already past its TTL.
|
|
30
|
+
*
|
|
31
|
+
* Keyed by `fsPath` rather than URI string, because the two sides of a
|
|
32
|
+
* suppression see different URI forms for one file.
|
|
33
|
+
*/
|
|
34
|
+
register(fsPath: string, mtimeMs: number): void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether `fsPath` carries a live registration at exactly `mtimeMs`.
|
|
38
|
+
*
|
|
39
|
+
* A registration expires after the TTL, so a write recorded longer ago than
|
|
40
|
+
* that answers `false` — the caller's decision holds only while the echo it
|
|
41
|
+
* suppresses could still be in flight. A query never mutates the registry.
|
|
42
|
+
*/
|
|
43
|
+
isRegistered(fsPath: string, mtimeMs: number): boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Compares entries by mtime rather than by a time window, so the echo of a
|
|
48
|
+
* save whose content is unchanged is still recognised.
|
|
49
|
+
*/
|
|
50
|
+
export class DefaultSelfSaveRegistry implements SelfSaveRegistry {
|
|
29
51
|
protected readonly entries = new Map<string, { mtimeMs: number; registeredAt: number }>();
|
|
30
52
|
/**
|
|
31
53
|
* Read time through the shared {@link Clock} rather than `Date.now()`: the
|
|
@@ -59,23 +81,40 @@ export class SelfSaveRegistry {
|
|
|
59
81
|
return isCaseInsensitiveFileSystem() ? fsPath.toLowerCase() : fsPath;
|
|
60
82
|
}
|
|
61
83
|
|
|
62
|
-
/** Record a self-write. Key by `fsPath` (not URI string) to avoid URI-form mismatches. */
|
|
63
84
|
register(fsPath: string, mtimeMs: number): void {
|
|
85
|
+
this.evictExpired();
|
|
64
86
|
this.entries.set(this.key(fsPath), { mtimeMs, registeredAt: this.clock.now() });
|
|
65
87
|
}
|
|
66
88
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const entry = this.entries.get(key);
|
|
71
|
-
if (!entry) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
if (this.clock.now() - entry.registeredAt > this.ttlMs) {
|
|
75
|
-
this.entries.delete(key);
|
|
89
|
+
isRegistered(fsPath: string, mtimeMs: number): boolean {
|
|
90
|
+
const entry = this.entries.get(this.key(fsPath));
|
|
91
|
+
if (!entry || this.isExpired(entry.registeredAt)) {
|
|
76
92
|
return false;
|
|
77
93
|
}
|
|
78
94
|
// Accept mtime equality (some filesystems have 1-second precision).
|
|
79
95
|
return entry.mtimeMs === mtimeMs;
|
|
80
96
|
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Drop every expired registration.
|
|
100
|
+
*
|
|
101
|
+
* On the WRITE path, so the query stays free of side effects. Evicting from
|
|
102
|
+
* the query instead reaches only the keys something happens to ask about,
|
|
103
|
+
* and a path written once and never queried again is then retained for the
|
|
104
|
+
* life of the process. Bounded by the paths self-written inside one TTL
|
|
105
|
+
* window, so the sweep is over a handful of entries and sits behind a file
|
|
106
|
+
* write either way.
|
|
107
|
+
*/
|
|
108
|
+
protected evictExpired(): void {
|
|
109
|
+
for (const [key, entry] of this.entries) {
|
|
110
|
+
if (this.isExpired(entry.registeredAt)) {
|
|
111
|
+
this.entries.delete(key);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Strictly greater, so an entry aged exactly to the TTL is still eligible. */
|
|
117
|
+
protected isExpired(registeredAt: number): boolean {
|
|
118
|
+
return this.clock.now() - registeredAt > this.ttlMs;
|
|
119
|
+
}
|
|
81
120
|
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { DocumentState, type LangiumDocument, type URI } from '@hydranium/langium';
|
|
11
|
+
import { type Tracer, UNRECORDED_VERSION } from '@hydranium/protocol';
|
|
12
|
+
import { Disposable, Emitter, type Event } from 'vscode-languageserver-protocol';
|
|
13
|
+
import { type LogNameOptions } from '../langium/diagnostics/logger.js';
|
|
14
|
+
import { type ServerSharedServices } from '../langium/module.js';
|
|
15
|
+
import { HydraniumWorkspaceLock } from '../langium/workspace/hydranium-workspace-lock.js';
|
|
16
|
+
import { HydraniumTextDocuments } from './hydranium-text-documents.js';
|
|
17
|
+
|
|
18
|
+
/** A build {@link VersionSyncService.requestRecoveryBuild} schedules. */
|
|
19
|
+
export interface RecoveryBuildRequest {
|
|
20
|
+
/** Remove the document instead of building it. */
|
|
21
|
+
readonly deleted?: boolean;
|
|
22
|
+
/** Staged for the build, as `HydraniumDocumentBuilder.scheduleUpdate` takes it. */
|
|
23
|
+
readonly reason?: string;
|
|
24
|
+
/** Asked again inside the lock, where a build that ran meanwhile may have made the request moot. */
|
|
25
|
+
readonly stillNeeded: () => boolean;
|
|
26
|
+
/** Build even while a {@link VersionSyncService.registerDeferredBuilds} check holds the URI back. */
|
|
27
|
+
readonly ignoreDeferred?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Keeps each workspace root's `ModelLedger` record in step with the
|
|
32
|
+
* text store, and owns every build that syncs a root to its text or recovers
|
|
33
|
+
* one a build left behind.
|
|
34
|
+
*/
|
|
35
|
+
export interface VersionSyncService {
|
|
36
|
+
/**
|
|
37
|
+
* Report a root a factory, registry, integrity repair or other producer
|
|
38
|
+
* made. Below `Parsed` it is marked a placeholder. Otherwise `origin.version`,
|
|
39
|
+
* the store's version read before the parse, is recorded; a registered
|
|
40
|
+
* document is then reconciled with the store, re-versioned and recorded at
|
|
41
|
+
* the store's version of its text, built if behind, and announced on
|
|
42
|
+
* {@link onDidRecordModel}. `origin.text` stands for a root whose CST is not
|
|
43
|
+
* the text it describes.
|
|
44
|
+
*/
|
|
45
|
+
modelProduced(document: LangiumDocument, origin?: { version?: number; text?: string }): void;
|
|
46
|
+
/**
|
|
47
|
+
* Build `uri` from a `WorkspaceLock` read, so the build queues behind a
|
|
48
|
+
* running one rather than cancelling it. Requests made while one for `uri`
|
|
49
|
+
* is pending share its next build. Resolves `true` once a build that carried
|
|
50
|
+
* this request completed, or once a check before a build found it no longer
|
|
51
|
+
* needed or deferred. A build a later write cancelled does not count: the
|
|
52
|
+
* request rides the next one. Resolves `false` once builds carrying this
|
|
53
|
+
* request have failed more often than it may retry, so a caller waiting on
|
|
54
|
+
* the build can stop. Never rejects.
|
|
55
|
+
*/
|
|
56
|
+
requestRecoveryBuild(uri: URI, request: RecoveryBuildRequest): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Build `uri` unless it is {@link isSyncedTo synced to} `textVersion`, asked
|
|
59
|
+
* again inside the lock. `undefined` when no build is needed, else the
|
|
60
|
+
* {@link requestRecoveryBuild} answer.
|
|
61
|
+
*/
|
|
62
|
+
syncTo(uri: URI, textVersion: number): Promise<boolean> | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Whether `uri`'s root was parsed from text at `textVersion` or later; requests
|
|
65
|
+
* nothing. A root with no recorded version or a placeholder counts as synced,
|
|
66
|
+
* since no build records a version for it, and so does a URI with no document.
|
|
67
|
+
*/
|
|
68
|
+
isSyncedTo(uri: URI, textVersion: number): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Register `isDeferred`, which answers whether its caller holds back a build
|
|
71
|
+
* of a URI it will request later, as the update handler does for a debounced
|
|
72
|
+
* editor change. A caller that drops a held change has to leave the URI to
|
|
73
|
+
* another build.
|
|
74
|
+
*/
|
|
75
|
+
registerDeferredBuilds(isDeferred: (uri: URI) => boolean): Disposable;
|
|
76
|
+
/**
|
|
77
|
+
* Fires for every reconciled root. A cancel cannot skip it, unlike a
|
|
78
|
+
* `Parsed` phase listener. It can fire inside a build's write lock, so a
|
|
79
|
+
* listener that requests a build must not await it.
|
|
80
|
+
*/
|
|
81
|
+
readonly onDidRecordModel: Event<LangiumDocument>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A request waiting in {@link PendingRecoveryBuilds}, with the failed builds that carried it. */
|
|
85
|
+
export interface QueuedRecoveryBuild {
|
|
86
|
+
readonly request: RecoveryBuildRequest;
|
|
87
|
+
failures: number;
|
|
88
|
+
readonly answer: (built: boolean) => void;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** The requests waiting on one URI's builds in {@link DefaultVersionSyncService}. */
|
|
92
|
+
export interface PendingRecoveryBuilds {
|
|
93
|
+
readonly queued: QueuedRecoveryBuild[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Collaborators are resolved per call: the registry and its document factory,
|
|
98
|
+
* which construct each other, report roots to this service.
|
|
99
|
+
*/
|
|
100
|
+
export class DefaultVersionSyncService implements VersionSyncService {
|
|
101
|
+
protected readonly tracer: Tracer;
|
|
102
|
+
protected readonly recordedEmitter = new Emitter<LangiumDocument>();
|
|
103
|
+
protected readonly deferredBuildChecks = new Set<(uri: URI) => boolean>();
|
|
104
|
+
protected readonly pendingBuilds = new Map<string, PendingRecoveryBuilds>();
|
|
105
|
+
/** Retries of each request after a failed build before it is answered `false`. */
|
|
106
|
+
protected readonly maxBuildRetries: number = 1;
|
|
107
|
+
|
|
108
|
+
constructor(
|
|
109
|
+
protected readonly services: ServerSharedServices,
|
|
110
|
+
options: LogNameOptions = {}
|
|
111
|
+
) {
|
|
112
|
+
this.tracer = services.Tracer.for(options.logName ?? 'VersionSyncService').trace('instantiated');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get onDidRecordModel(): Event<LangiumDocument> {
|
|
116
|
+
return this.recordedEmitter.event;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
modelProduced(document: LangiumDocument, origin: { version?: number; text?: string } = {}): void {
|
|
120
|
+
const workspace = this.services.workspace;
|
|
121
|
+
const ledger = workspace.ModelLedger;
|
|
122
|
+
const root = document.parseResult.value;
|
|
123
|
+
if (document.state < DocumentState.Parsed) {
|
|
124
|
+
ledger.markPlaceholder(root);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (origin.version !== undefined) {
|
|
128
|
+
ledger.record(root, origin.version, origin.text);
|
|
129
|
+
}
|
|
130
|
+
const store = workspace.TextDocuments;
|
|
131
|
+
// Unregistered, it is a probe or a load whose registration reports it again.
|
|
132
|
+
if (!(store instanceof HydraniumTextDocuments) || workspace.LangiumDocuments.getDocument(document.uri) !== document) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const uri = document.uri.toString();
|
|
136
|
+
// Against the parsed text: the text document may already hold a newer edit.
|
|
137
|
+
const version = store.reconcileExternalContent(uri, origin.text ?? this.parsedText(document));
|
|
138
|
+
if (version !== undefined) {
|
|
139
|
+
if (document.textDocument.version !== version) {
|
|
140
|
+
// An empty-changes update only re-stamps the version.
|
|
141
|
+
store.update(document.textDocument, [], version);
|
|
142
|
+
}
|
|
143
|
+
ledger.record(root, version, origin.text);
|
|
144
|
+
}
|
|
145
|
+
void this.syncTo(document.uri, store.version(uri));
|
|
146
|
+
this.recordedEmitter.fire(document);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
syncTo(uri: URI, textVersion: number): Promise<boolean> | undefined {
|
|
150
|
+
const behind = (): boolean => !this.isSyncedTo(uri, textVersion);
|
|
151
|
+
return behind() ? this.requestRecoveryBuild(uri, { stillNeeded: behind }) : undefined;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
requestRecoveryBuild(uri: URI, request: RecoveryBuildRequest): Promise<boolean> {
|
|
155
|
+
return new Promise<boolean>(answer => {
|
|
156
|
+
const key = uri.toString();
|
|
157
|
+
const queued: QueuedRecoveryBuild = { request, failures: 0, answer };
|
|
158
|
+
const pending = this.pendingBuilds.get(key);
|
|
159
|
+
if (pending) {
|
|
160
|
+
// Not dropped: the pending build may have read the text before this request's change.
|
|
161
|
+
pending.queued.push(queued);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const entry: PendingRecoveryBuilds = { queued: [queued] };
|
|
165
|
+
this.pendingBuilds.set(key, entry);
|
|
166
|
+
void this.runBuilds(key, uri, entry);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
registerDeferredBuilds(isDeferred: (uri: URI) => boolean): Disposable {
|
|
171
|
+
this.deferredBuildChecks.add(isDeferred);
|
|
172
|
+
return Disposable.create(() => this.deferredBuildChecks.delete(isDeferred));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
isSyncedTo(uri: URI, textVersion: number): boolean {
|
|
176
|
+
const root = this.services.workspace.LangiumDocuments.getDocument(uri)?.parseResult.value;
|
|
177
|
+
if (root === undefined) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
const ledger = this.services.workspace.ModelLedger;
|
|
181
|
+
const recorded = ledger.versionOf(root);
|
|
182
|
+
return recorded === UNRECORDED_VERSION || ledger.isPlaceholder(root) || recorded >= textVersion;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** The text `document`'s root was parsed from, or for a root built without either record or CST, the document's own. */
|
|
186
|
+
protected parsedText(document: LangiumDocument): string {
|
|
187
|
+
return this.services.workspace.ModelLedger.textOf(document.parseResult.value) ?? document.textDocument.getText();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Run `entry`'s requests, one build per batch, until none is queued, and
|
|
192
|
+
* answer each. A request a failed build carried is queued again
|
|
193
|
+
* {@link maxBuildRetries} times, then answered `false`; one a cancelled
|
|
194
|
+
* build carried is queued again without counting. The entry is dropped
|
|
195
|
+
* in the same tick as the last check of the queue, so no request lands in
|
|
196
|
+
* an entry nothing runs any more.
|
|
197
|
+
*/
|
|
198
|
+
protected async runBuilds(key: string, uri: URI, entry: PendingRecoveryBuilds): Promise<void> {
|
|
199
|
+
const tracer = this.tracer.withUri(key);
|
|
200
|
+
const lock = this.services.workspace.WorkspaceLock;
|
|
201
|
+
// Any other lock resolves a cancelled write as if it had completed, unnoticed.
|
|
202
|
+
const counting = lock instanceof HydraniumWorkspaceLock ? lock : undefined;
|
|
203
|
+
while (entry.queued.length > 0) {
|
|
204
|
+
let carried: QueuedRecoveryBuild[] = [];
|
|
205
|
+
let build: Promise<void> | undefined;
|
|
206
|
+
let cancellations: number | undefined;
|
|
207
|
+
try {
|
|
208
|
+
// The read waits until no write runs or is queued. It must not await
|
|
209
|
+
// the build, a write that queues behind it.
|
|
210
|
+
await lock.read(() => {
|
|
211
|
+
carried = entry.queued.splice(0);
|
|
212
|
+
const needed = carried.filter(queued => this.isStillNeeded(uri, queued.request));
|
|
213
|
+
carried.filter(queued => !needed.includes(queued)).forEach(queued => queued.answer(true));
|
|
214
|
+
carried = needed;
|
|
215
|
+
if (needed.length > 0) {
|
|
216
|
+
build = this.scheduleBatch(
|
|
217
|
+
uri,
|
|
218
|
+
needed.map(queued => queued.request)
|
|
219
|
+
);
|
|
220
|
+
// Read after the build is queued, which cancels the write before it.
|
|
221
|
+
cancellations = counting?.writeCancellations;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
await build;
|
|
225
|
+
// A write cancelled the build, before it ran or while it did.
|
|
226
|
+
if (cancellations !== undefined && counting?.writeCancellations !== cancellations) {
|
|
227
|
+
entry.queued.unshift(...carried);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
carried.forEach(queued => queued.answer(true));
|
|
231
|
+
} catch (err: unknown) {
|
|
232
|
+
const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
|
|
233
|
+
const retried = carried.filter(queued => queued.failures++ < this.maxBuildRetries);
|
|
234
|
+
const givenUp = carried.filter(queued => !retried.includes(queued));
|
|
235
|
+
if (retried.length > 0) {
|
|
236
|
+
tracer.warn(`Requested build failed; retrying. ${detail}`);
|
|
237
|
+
entry.queued.unshift(...retried);
|
|
238
|
+
}
|
|
239
|
+
if (givenUp.length > 0) {
|
|
240
|
+
tracer.error(`Requested build failed again; giving up. ${detail}`);
|
|
241
|
+
givenUp.forEach(queued => queued.answer(false));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
this.pendingBuilds.delete(key);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Whether `request` still needs its build, and no deferral it heeds holds it back. Runs inside the lock read. */
|
|
249
|
+
protected isStillNeeded(uri: URI, request: RecoveryBuildRequest): boolean {
|
|
250
|
+
return request.stillNeeded() && (request.ignoreDeferred === true || !this.isDeferred(uri));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* One build for every request of `batch`, removing the document when the
|
|
255
|
+
* latest of them asks to, as `HydraniumDocumentBuilder.scheduleUpdate`
|
|
256
|
+
* merges a change and a deletion. Runs inside the lock read.
|
|
257
|
+
*/
|
|
258
|
+
protected scheduleBatch(uri: URI, batch: readonly RecoveryBuildRequest[]): Promise<void> {
|
|
259
|
+
const deleted = batch.at(-1)?.deleted === true;
|
|
260
|
+
const reason = batch.find(request => request.reason !== undefined)?.reason;
|
|
261
|
+
return this.services.workspace.DocumentBuilder.scheduleUpdate(deleted ? [] : [uri], deleted ? [uri] : [], reason);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
protected isDeferred(uri: URI): boolean {
|
|
265
|
+
return [...this.deferredBuildChecks].some(isDeferred => isDeferred(uri));
|
|
266
|
+
}
|
|
267
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export * from './langium/project/index.js';
|
|
|
28
28
|
export * from './langium/residency/index.js';
|
|
29
29
|
export * from './langium/scope/index.js';
|
|
30
30
|
export * from './langium/serialization/index.js';
|
|
31
|
+
export * from './langium/trivia/index.js';
|
|
31
32
|
export * from './langium/transfer/index.js';
|
|
32
33
|
export * from './langium/update-rewrite/index.js';
|
|
33
34
|
export * from './langium/validation/index.js';
|
|
@@ -35,5 +36,10 @@ export * from './langium/workspace/index.js';
|
|
|
35
36
|
export * from './langium/service-registry.js';
|
|
36
37
|
export * from './langium/language-types.js';
|
|
37
38
|
export * from './launcher/index.js';
|
|
39
|
+
export * from './locale/index.js';
|
|
40
|
+
// The renderer only. The `./messages` subpath additionally enumerates this
|
|
41
|
+
// package's own declarations, which the root barrel already re-exports through
|
|
42
|
+
// the modules that raise them.
|
|
43
|
+
export * from './messages/renderer.js';
|
|
38
44
|
export * from './documents/index.js';
|
|
39
45
|
export * from './util/index.js';
|
|
@@ -78,15 +78,13 @@ export type AstNodeBuilder<TMap> = <TType extends string & keyof TMap>(
|
|
|
78
78
|
* AST-interface mapping, so call sites get full inference from the type
|
|
79
79
|
* constant alone — no explicit generic parameter at every call site.
|
|
80
80
|
*
|
|
81
|
-
* Runtime behaviour:
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* TS-augmented fields (declared via `declare module './generated/ast.js'`)
|
|
89
|
-
* are unaffected by the runtime defaulting but remain typed at call sites.
|
|
81
|
+
* Runtime behaviour: pre-fills every grammar-declared default (see
|
|
82
|
+
* {@link withTypeDefaults}) before merging the caller's `init`, so containment
|
|
83
|
+
* lists and `?=`-style boolean flags default automatically. TS-augmented fields
|
|
84
|
+
* (declared via `declare module './generated/ast.js'`) are unaffected by the
|
|
85
|
+
* runtime defaulting but remain typed at call sites.
|
|
86
|
+
*
|
|
87
|
+
* Use {@link buildAstNode} instead when the type name is only known at runtime.
|
|
90
88
|
*
|
|
91
89
|
* The init shape only relaxes array-typed fields to optional (via
|
|
92
90
|
* {@link ArrayKeys}) — there is no equivalent structural signal for "this
|
|
@@ -101,19 +99,7 @@ export function makeAstNodeBuilder<TMap>(reflection: AstReflection): AstNodeBuil
|
|
|
101
99
|
init: AstNodeInit<Extract<TMap[TType], AstNode>>,
|
|
102
100
|
extras?: Record<string, unknown>
|
|
103
101
|
): Extract<TMap[TType], AstNode> {
|
|
104
|
-
const
|
|
105
|
-
const result: Record<string, unknown> = { $type: typeConstant.$type };
|
|
106
|
-
if (meta?.properties) {
|
|
107
|
-
for (const [propName, propInfo] of Object.entries(meta.properties)) {
|
|
108
|
-
const defaultValue = propInfo.defaultValue;
|
|
109
|
-
if (defaultValue === undefined) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
// Arrays are duplicated so each node carries its own mutable instance;
|
|
113
|
-
// scalars are immutable and safe to copy by reference.
|
|
114
|
-
result[propName] = Array.isArray(defaultValue) ? [...defaultValue] : defaultValue;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
102
|
+
const result = withTypeDefaults(reflection, typeConstant.$type);
|
|
117
103
|
Object.assign(result, init);
|
|
118
104
|
if (extras) {
|
|
119
105
|
// Extras win over init: this is the explicit escape hatch, so when a
|
|
@@ -123,3 +109,61 @@ export function makeAstNodeBuilder<TMap>(reflection: AstReflection): AstNodeBuil
|
|
|
123
109
|
return result as unknown as Extract<TMap[TType], AstNode>;
|
|
124
110
|
};
|
|
125
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Build a node whose type is only known as a RUNTIME string.
|
|
115
|
+
*
|
|
116
|
+
* The typed {@link makeAstNodeBuilder} cannot express this: its `TMap` generic
|
|
117
|
+
* resolves the AST interface from a `$type` literal, and a type name that
|
|
118
|
+
* arrives over the wire — a protocol-layer reference request naming a node that
|
|
119
|
+
* does not exist yet — has no literal to resolve from. Callers that DO know the
|
|
120
|
+
* type at compile time want the typed builder, which checks their mandatory
|
|
121
|
+
* fields; this one checks nothing beyond the reflection lookup.
|
|
122
|
+
*
|
|
123
|
+
* Applies the same reflection-driven defaulting, so a node built here carries
|
|
124
|
+
* grammar-declared containment arrays rather than leaving them `undefined`. An
|
|
125
|
+
* unknown type name yields a node with `$type` and whatever `init` supplied,
|
|
126
|
+
* because reflection answers no metadata for it and refusing would turn a
|
|
127
|
+
* permissive lookup into a throw at a call site that can only guess.
|
|
128
|
+
*
|
|
129
|
+
* **The `type` argument is authoritative: `init` cannot rebind `$type`.** The
|
|
130
|
+
* typed builder gets this for free — `AstNodeInit` omits `$type`, so a call site
|
|
131
|
+
* cannot reach it — but here `init` is loosely typed, and the case that needs
|
|
132
|
+
* the guarantee is RETYPING: a caller spreading an existing node to produce one
|
|
133
|
+
* of a different type (`{ ...source }`) carries the SOURCE's `$type` in, which
|
|
134
|
+
* would silently win over the type actually requested and hand back a node of
|
|
135
|
+
* the wrong type with no error anywhere.
|
|
136
|
+
*/
|
|
137
|
+
export function buildAstNode<TAst extends AstNode = AstNode>(
|
|
138
|
+
reflection: AstReflection,
|
|
139
|
+
type: string,
|
|
140
|
+
init?: Partial<AstNode> & Record<string, unknown>
|
|
141
|
+
): TAst {
|
|
142
|
+
const result = withTypeDefaults(reflection, type);
|
|
143
|
+
if (init) {
|
|
144
|
+
Object.assign(result, init);
|
|
145
|
+
result.$type = type;
|
|
146
|
+
}
|
|
147
|
+
return result as unknown as TAst;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Seed a node with `$type` plus every property the grammar declares a default
|
|
152
|
+
* for. Array defaults are materialised as a fresh `[]` per call — never shared
|
|
153
|
+
* with the metadata's own array instance, which downstream callers mutate —
|
|
154
|
+
* while scalar defaults are copied as-is since they are immutable.
|
|
155
|
+
*/
|
|
156
|
+
function withTypeDefaults(reflection: AstReflection, type: string): Record<string, unknown> {
|
|
157
|
+
const result: Record<string, unknown> = { $type: type };
|
|
158
|
+
const meta = reflection.getTypeMetaData(type);
|
|
159
|
+
if (meta?.properties) {
|
|
160
|
+
for (const [propName, propInfo] of Object.entries(meta.properties)) {
|
|
161
|
+
const defaultValue = propInfo.defaultValue;
|
|
162
|
+
if (defaultValue === undefined) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
result[propName] = Array.isArray(defaultValue) ? [...defaultValue] : defaultValue;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}
|