@hydranium/core 1.0.0-next.38 → 1.0.0-next.380
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 +78 -82
- package/lib/browser/host-heads-in-worker.d.ts +77 -0
- package/lib/browser/host-heads-in-worker.d.ts.map +1 -0
- package/lib/browser/host-heads-in-worker.js +169 -0
- package/lib/browser/host-heads-in-worker.js.map +1 -0
- package/lib/browser/index.d.ts +12 -0
- package/lib/browser/index.d.ts.map +1 -0
- package/lib/browser/index.js +17 -0
- package/lib/browser/index.js.map +1 -0
- package/lib/browser/indexeddb-file-system-store.d.ts +82 -0
- package/lib/browser/indexeddb-file-system-store.d.ts.map +1 -0
- package/lib/browser/indexeddb-file-system-store.js +181 -0
- package/lib/browser/indexeddb-file-system-store.js.map +1 -0
- package/lib/browser/start-language-server-in-worker.d.ts +32 -0
- package/lib/browser/start-language-server-in-worker.d.ts.map +1 -0
- package/lib/browser/start-language-server-in-worker.js +40 -0
- package/lib/browser/start-language-server-in-worker.js.map +1 -0
- package/lib/documents/ast-document-manager.d.ts +254 -140
- package/lib/documents/ast-document-manager.d.ts.map +1 -1
- package/lib/documents/ast-document-manager.js +311 -127
- 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 +146 -0
- package/lib/documents/client-session-registry.d.ts.map +1 -0
- package/lib/documents/client-session-registry.js +207 -0
- package/lib/documents/client-session-registry.js.map +1 -0
- package/lib/documents/dirty-state-tracker.d.ts +101 -0
- package/lib/documents/dirty-state-tracker.d.ts.map +1 -0
- package/lib/documents/dirty-state-tracker.js +85 -0
- package/lib/documents/dirty-state-tracker.js.map +1 -0
- package/lib/documents/document-release-handler.d.ts +204 -0
- package/lib/documents/document-release-handler.d.ts.map +1 -0
- package/lib/documents/document-release-handler.js +317 -0
- package/lib/documents/document-release-handler.js.map +1 -0
- package/lib/documents/document-release-scheduler.d.ts +67 -0
- package/lib/documents/document-release-scheduler.d.ts.map +1 -0
- package/lib/documents/document-release-scheduler.js +80 -0
- package/lib/documents/document-release-scheduler.js.map +1 -0
- package/lib/documents/file-system-task-queue.d.ts +52 -0
- package/lib/documents/file-system-task-queue.d.ts.map +1 -0
- package/lib/documents/file-system-task-queue.js +37 -0
- package/lib/documents/file-system-task-queue.js.map +1 -0
- package/lib/documents/hydranium-text-documents.d.ts +445 -282
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
- package/lib/documents/hydranium-text-documents.js +903 -504
- package/lib/documents/hydranium-text-documents.js.map +1 -1
- package/lib/documents/index.d.ts +10 -2
- package/lib/documents/index.d.ts.map +1 -1
- package/lib/documents/index.js +10 -2
- package/lib/documents/index.js.map +1 -1
- package/lib/documents/language-client-shadow.d.ts +234 -0
- package/lib/documents/language-client-shadow.d.ts.map +1 -0
- package/lib/documents/language-client-shadow.js +306 -0
- package/lib/documents/language-client-shadow.js.map +1 -0
- 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/text-ledger.d.ts +64 -0
- package/lib/documents/text-ledger.d.ts.map +1 -0
- package/lib/documents/text-ledger.js +63 -0
- package/lib/documents/text-ledger.js.map +1 -0
- package/lib/documents/version-sync-service.d.ts +145 -0
- package/lib/documents/version-sync-service.d.ts.map +1 -0
- package/lib/documents/version-sync-service.js +177 -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 +3 -0
- package/lib/index.js.map +1 -1
- package/lib/langium/ast-extension/ast-extension-service.d.ts +42 -31
- package/lib/langium/ast-extension/ast-extension-service.d.ts.map +1 -1
- package/lib/langium/ast-extension/ast-extension-service.js +45 -19
- package/lib/langium/ast-extension/ast-extension-service.js.map +1 -1
- package/lib/langium/ast-extension/ast-node-builder.d.ts +12 -7
- package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
- package/lib/langium/ast-extension/ast-node-builder.js +25 -9
- package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
- package/lib/langium/bootstrap.d.ts +49 -36
- package/lib/langium/bootstrap.d.ts.map +1 -1
- package/lib/langium/bootstrap.js +75 -36
- package/lib/langium/bootstrap.js.map +1 -1
- package/lib/langium/build-phase-pass/build-phase-pass-service.d.ts +9 -9
- 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 +25 -16
- package/lib/langium/build-phase-pass/build-phase-pass-service.js.map +1 -1
- package/lib/langium/build-phase-pass/build-phase-pass.d.ts +20 -8
- package/lib/langium/build-phase-pass/build-phase-pass.d.ts.map +1 -1
- package/lib/langium/build-phase-pass/build-phase-pass.js +20 -0
- package/lib/langium/build-phase-pass/build-phase-pass.js.map +1 -1
- package/lib/langium/composite-ast-reflection.d.ts +46 -0
- package/lib/langium/composite-ast-reflection.d.ts.map +1 -0
- package/lib/langium/composite-ast-reflection.js +97 -0
- package/lib/langium/composite-ast-reflection.js.map +1 -0
- 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/console-tee.d.ts +27 -0
- package/lib/langium/diagnostics/console-tee.d.ts.map +1 -0
- package/lib/langium/diagnostics/console-tee.js +141 -0
- package/lib/langium/diagnostics/console-tee.js.map +1 -0
- package/lib/langium/diagnostics/hydranium-langium-profiler.d.ts +9 -20
- package/lib/langium/diagnostics/hydranium-langium-profiler.d.ts.map +1 -1
- package/lib/langium/diagnostics/hydranium-langium-profiler.js +15 -24
- package/lib/langium/diagnostics/hydranium-langium-profiler.js.map +1 -1
- package/lib/langium/diagnostics/log-preamble.js +1 -1
- package/lib/langium/diagnostics/log-preamble.js.map +1 -1
- package/lib/langium/diagnostics/logger.d.ts +25 -6
- package/lib/langium/diagnostics/logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/logger.js +49 -17
- package/lib/langium/diagnostics/logger.js.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.d.ts +25 -0
- package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.js +81 -46
- 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 +43 -30
- package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
- package/lib/langium/document-builder/build-session.d.ts +6 -5
- package/lib/langium/document-builder/build-session.d.ts.map +1 -1
- package/lib/langium/document-builder/build-session.js +5 -5
- package/lib/langium/document-builder/build-session.js.map +1 -1
- package/lib/langium/document-builder/document-builder.d.ts +355 -39
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +578 -102
- package/lib/langium/document-builder/document-builder.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 +39 -23
- package/lib/langium/integration-services.d.ts.map +1 -1
- package/lib/langium/integration-services.js +7 -4
- package/lib/langium/integration-services.js.map +1 -1
- package/lib/langium/integrity/integrity-rule.d.ts +22 -8
- 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 +104 -27
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-service.js +319 -65
- package/lib/langium/integrity/integrity-service.js.map +1 -1
- package/lib/langium/keys/containment.d.ts +3 -2
- package/lib/langium/keys/containment.d.ts.map +1 -1
- package/lib/langium/keys/containment.js.map +1 -1
- package/lib/langium/keys/name-based-key-provider.d.ts +17 -10
- package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
- package/lib/langium/keys/name-based-key-provider.js +24 -12
- 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 +30 -5
- package/lib/langium/language-module.d.ts.map +1 -1
- package/lib/langium/language-module.js +21 -5
- package/lib/langium/language-module.js.map +1 -1
- package/lib/langium/model-service/client-session.d.ts +405 -0
- package/lib/langium/model-service/client-session.d.ts.map +1 -0
- package/lib/langium/model-service/client-session.js +479 -0
- package/lib/langium/model-service/client-session.js.map +1 -0
- package/lib/langium/model-service/index.d.ts +2 -0
- package/lib/langium/model-service/index.d.ts.map +1 -1
- package/lib/langium/model-service/index.js +2 -0
- package/lib/langium/model-service/index.js.map +1 -1
- package/lib/langium/model-service/model-events.d.ts +68 -0
- package/lib/langium/model-service/model-events.d.ts.map +1 -0
- package/lib/langium/model-service/model-events.js +10 -0
- package/lib/langium/model-service/model-events.js.map +1 -0
- package/lib/langium/model-service/model-service.d.ts +441 -323
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +602 -397
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/module.d.ts +96 -37
- package/lib/langium/module.d.ts.map +1 -1
- package/lib/langium/module.js +39 -27
- package/lib/langium/module.js.map +1 -1
- package/lib/langium/naming/name-provider.d.ts +32 -7
- package/lib/langium/naming/name-provider.d.ts.map +1 -1
- package/lib/langium/naming/name-provider.js +10 -4
- package/lib/langium/naming/name-provider.js.map +1 -1
- package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
- package/lib/langium/project/abstract-project-manager.d.ts +184 -13
- package/lib/langium/project/abstract-project-manager.d.ts.map +1 -1
- package/lib/langium/project/abstract-project-manager.js +336 -16
- package/lib/langium/project/abstract-project-manager.js.map +1 -1
- package/lib/langium/project/project-change-event.d.ts +22 -3
- package/lib/langium/project/project-change-event.d.ts.map +1 -1
- package/lib/langium/project/project-manager.d.ts +28 -2
- package/lib/langium/project/project-manager.d.ts.map +1 -1
- package/lib/langium/project/single-project-manager.d.ts +7 -1
- package/lib/langium/project/single-project-manager.d.ts.map +1 -1
- package/lib/langium/project/single-project-manager.js +12 -0
- package/lib/langium/project/single-project-manager.js.map +1 -1
- package/lib/langium/residency/cst-residency-service.d.ts +81 -44
- package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
- package/lib/langium/residency/cst-residency-service.js +142 -46
- package/lib/langium/residency/cst-residency-service.js.map +1 -1
- package/lib/langium/scope/ast-node-description-provider.d.ts +46 -10
- package/lib/langium/scope/ast-node-description-provider.d.ts.map +1 -1
- package/lib/langium/scope/ast-node-description-provider.js +88 -15
- package/lib/langium/scope/ast-node-description-provider.js.map +1 -1
- package/lib/langium/scope/built-local-symbols.d.ts +12 -0
- package/lib/langium/scope/built-local-symbols.d.ts.map +1 -0
- package/lib/langium/scope/built-local-symbols.js +11 -0
- package/lib/langium/scope/built-local-symbols.js.map +1 -0
- package/lib/langium/scope/fallback-scope.d.ts +27 -0
- package/lib/langium/scope/fallback-scope.d.ts.map +1 -0
- package/lib/langium/scope/fallback-scope.js +35 -0
- package/lib/langium/scope/fallback-scope.js.map +1 -0
- package/lib/langium/scope/hydranium-scope-computation.d.ts +21 -18
- package/lib/langium/scope/hydranium-scope-computation.d.ts.map +1 -1
- package/lib/langium/scope/hydranium-scope-computation.js +38 -39
- package/lib/langium/scope/hydranium-scope-computation.js.map +1 -1
- package/lib/langium/scope/hydranium-scope-provider.d.ts +122 -32
- package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
- package/lib/langium/scope/hydranium-scope-provider.js +217 -39
- package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
- package/lib/langium/scope/index.d.ts +1 -0
- package/lib/langium/scope/index.d.ts.map +1 -1
- package/lib/langium/scope/index.js +1 -0
- package/lib/langium/scope/index.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 +6 -5
- package/lib/langium/scope/reference-candidate-provider.d.ts.map +1 -1
- package/lib/langium/scope/reference-candidate-provider.js +3 -2
- package/lib/langium/scope/reference-candidate-provider.js.map +1 -1
- package/lib/langium/scope/scope-extension-service.d.ts +69 -37
- package/lib/langium/scope/scope-extension-service.d.ts.map +1 -1
- package/lib/langium/scope/scope-extension-service.js +102 -29
- package/lib/langium/scope/scope-extension-service.js.map +1 -1
- package/lib/langium/scope/scoped-ast-node-description.d.ts +1 -3
- package/lib/langium/scope/scoped-ast-node-description.d.ts.map +1 -1
- package/lib/langium/scope/scoped-ast-node-description.js +1 -3
- package/lib/langium/scope/scoped-ast-node-description.js.map +1 -1
- package/lib/langium/serialization/abstract-serializer.d.ts +38 -27
- package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
- package/lib/langium/serialization/abstract-serializer.js +54 -30
- package/lib/langium/serialization/abstract-serializer.js.map +1 -1
- package/lib/langium/serialization/json-serializer.d.ts +8 -1
- package/lib/langium/serialization/json-serializer.d.ts.map +1 -1
- package/lib/langium/serialization/json-serializer.js +13 -3
- package/lib/langium/serialization/json-serializer.js.map +1 -1
- package/lib/langium/serialization/serializer.d.ts +1 -1
- package/lib/langium/serialization/yaml-serializer.d.ts +12 -0
- package/lib/langium/serialization/yaml-serializer.d.ts.map +1 -1
- package/lib/langium/serialization/yaml-serializer.js +24 -4
- package/lib/langium/serialization/yaml-serializer.js.map +1 -1
- package/lib/langium/service-registry.d.ts +29 -1
- package/lib/langium/service-registry.d.ts.map +1 -1
- package/lib/langium/service-registry.js +37 -0
- package/lib/langium/service-registry.js.map +1 -1
- package/lib/langium/shared-services.d.ts +10 -4
- package/lib/langium/shared-services.d.ts.map +1 -1
- package/lib/langium/shared-services.js.map +1 -1
- package/lib/langium/single-langium-copy.d.ts +22 -0
- package/lib/langium/single-langium-copy.d.ts.map +1 -0
- package/lib/langium/single-langium-copy.js +36 -0
- package/lib/langium/single-langium-copy.js.map +1 -0
- package/lib/langium/transfer/transfer-encoder.d.ts +60 -39
- package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
- package/lib/langium/transfer/transfer-encoder.js +35 -24
- package/lib/langium/transfer/transfer-encoder.js.map +1 -1
- package/lib/langium/trivia/comment-preserver.d.ts +543 -0
- package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/comment-preserver.js +1165 -0
- package/lib/langium/trivia/comment-preserver.js.map +1 -0
- package/lib/langium/trivia/document-ending-preserver.d.ts +51 -0
- package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/document-ending-preserver.js +62 -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 +52 -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 +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 +168 -23
- package/lib/langium/validation/document-validator.d.ts.map +1 -1
- package/lib/langium/validation/document-validator.js +264 -26
- package/lib/langium/validation/document-validator.js.map +1 -1
- package/lib/langium/validation/validation-contribution-collector.d.ts +9 -1
- package/lib/langium/validation/validation-contribution-collector.d.ts.map +1 -1
- package/lib/langium/validation/validation-contribution-collector.js +1 -1
- package/lib/langium/validation/validation-contribution-collector.js.map +1 -1
- package/lib/langium/workspace/ast-node-locator.d.ts +24 -0
- package/lib/langium/workspace/ast-node-locator.d.ts.map +1 -0
- package/lib/langium/workspace/ast-node-locator.js +32 -0
- package/lib/langium/workspace/ast-node-locator.js.map +1 -0
- package/lib/langium/workspace/document-uri-policy.d.ts +24 -15
- package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
- package/lib/langium/workspace/document-uri-policy.js +22 -13
- package/lib/langium/workspace/document-uri-policy.js.map +1 -1
- package/lib/langium/workspace/file-not-found.d.ts +23 -0
- package/lib/langium/workspace/file-not-found.d.ts.map +1 -0
- package/lib/langium/workspace/file-not-found.js +31 -0
- package/lib/langium/workspace/file-not-found.js.map +1 -0
- package/lib/langium/workspace/file-system-provider.d.ts +167 -8
- package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/file-system-provider.js +115 -13
- package/lib/langium/workspace/file-system-provider.js.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +35 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.js +77 -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 +105 -21
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.js +186 -31
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts +26 -9
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.js +45 -22
- 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/index.d.ts +3 -0
- package/lib/langium/workspace/index.d.ts.map +1 -1
- package/lib/langium/workspace/index.js +3 -0
- package/lib/langium/workspace/index.js.map +1 -1
- package/lib/langium/workspace/initialize-workspace.d.ts +40 -3
- package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
- package/lib/langium/workspace/initialize-workspace.js +68 -3
- package/lib/langium/workspace/initialize-workspace.js.map +1 -1
- package/lib/langium/workspace/langium-documents.d.ts +7 -0
- package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
- package/lib/langium/workspace/langium-documents.js +18 -1
- package/lib/langium/workspace/langium-documents.js.map +1 -1
- package/lib/langium/workspace/persistent-file-system-provider.d.ts +16 -10
- package/lib/langium/workspace/persistent-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/persistent-file-system-provider.js +25 -13
- package/lib/langium/workspace/persistent-file-system-provider.js.map +1 -1
- package/lib/{documents → langium/workspace}/self-save-registry.d.ts +7 -9
- package/lib/langium/workspace/self-save-registry.d.ts.map +1 -0
- package/lib/{documents → langium/workspace}/self-save-registry.js +9 -11
- package/lib/langium/workspace/self-save-registry.js.map +1 -0
- package/lib/langium/workspace/syntax-errors.d.ts +23 -0
- package/lib/langium/workspace/syntax-errors.d.ts.map +1 -0
- package/lib/langium/workspace/syntax-errors.js +29 -0
- package/lib/langium/workspace/syntax-errors.js.map +1 -0
- package/lib/langium/workspace/virtual-document.d.ts +55 -21
- package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
- package/lib/langium/workspace/virtual-document.js +87 -24
- package/lib/langium/workspace/virtual-document.js.map +1 -1
- package/lib/langium/workspace/write-lock-scope.d.ts +22 -10
- package/lib/langium/workspace/write-lock-scope.d.ts.map +1 -1
- package/lib/langium/workspace/write-lock-scope.js +15 -5
- package/lib/langium/workspace/write-lock-scope.js.map +1 -1
- package/lib/launcher/integrated-server.d.ts +3 -1
- package/lib/launcher/integrated-server.d.ts.map +1 -1
- package/lib/locale/server-locale.d.ts +6 -6
- package/lib/locale/server-locale.d.ts.map +1 -1
- package/lib/locale/server-locale.js.map +1 -1
- 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/edit-requests-guard.d.ts +31 -0
- package/lib/lsp/edit-requests-guard.d.ts.map +1 -0
- package/lib/lsp/edit-requests-guard.js +125 -0
- package/lib/lsp/edit-requests-guard.js.map +1 -0
- package/lib/lsp/hydranium-document-update-handler.d.ts +126 -82
- package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
- package/lib/lsp/hydranium-document-update-handler.js +234 -101
- package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
- package/lib/lsp/index.d.ts +6 -2
- package/lib/lsp/index.d.ts.map +1 -1
- package/lib/lsp/index.js +6 -2
- 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/save-text-document.d.ts +31 -0
- package/lib/lsp/save-text-document.d.ts.map +1 -0
- package/lib/lsp/save-text-document.js +52 -0
- package/lib/lsp/save-text-document.js.map +1 -0
- package/lib/lsp/semantic-token-provider.d.ts +58 -2
- package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
- package/lib/lsp/semantic-token-provider.js +98 -9
- 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 +12 -7
- package/lib/lsp/start-language-server.d.ts.map +1 -1
- package/lib/lsp/start-language-server.js +21 -7
- package/lib/lsp/start-language-server.js.map +1 -1
- package/lib/messages/carriers.d.ts +4 -6
- package/lib/messages/carriers.d.ts.map +1 -1
- package/lib/messages/carriers.js +4 -6
- package/lib/messages/carriers.js.map +1 -1
- package/lib/messages/index.d.ts +2 -2
- package/lib/messages/index.d.ts.map +1 -1
- package/lib/messages/index.js +2 -2
- package/lib/messages/index.js.map +1 -1
- package/lib/messages/renderer.d.ts +10 -4
- package/lib/messages/renderer.d.ts.map +1 -1
- package/lib/messages/renderer.js +6 -0
- package/lib/messages/renderer.js.map +1 -1
- package/lib/node/event-loop-monitor.d.ts.map +1 -1
- package/lib/node/event-loop-monitor.js +2 -1
- 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 -1
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/index.js +5 -1
- 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/log-file-sink.d.ts +3 -2
- package/lib/node/log-file-sink.d.ts.map +1 -1
- package/lib/node/log-file-sink.js +6 -2
- package/lib/node/log-file-sink.js.map +1 -1
- package/lib/node/log-preamble-node.d.ts +5 -3
- package/lib/node/log-preamble-node.d.ts.map +1 -1
- package/lib/node/log-preamble-node.js +13 -10
- package/lib/node/log-preamble-node.js.map +1 -1
- package/lib/node/measure-memory.d.ts.map +1 -1
- package/lib/node/measure-memory.js +5 -4
- package/lib/node/measure-memory.js.map +1 -1
- package/lib/node/memory-monitor.d.ts +2 -2
- package/lib/node/memory-monitor.d.ts.map +1 -1
- package/lib/node/memory-monitor.js +9 -10
- package/lib/node/memory-monitor.js.map +1 -1
- package/lib/node/node-file-system-provider.d.ts +67 -9
- package/lib/node/node-file-system-provider.d.ts.map +1 -1
- package/lib/node/node-file-system-provider.js +198 -53
- package/lib/node/node-file-system-provider.js.map +1 -1
- package/lib/node/profile-capture.d.ts +12 -4
- package/lib/node/profile-capture.d.ts.map +1 -1
- package/lib/node/profile-capture.js +8 -6
- 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 +61 -0
- package/lib/node/rename-over-open-readers.d.ts.map +1 -0
- package/lib/node/rename-over-open-readers.js +80 -0
- package/lib/node/rename-over-open-readers.js.map +1 -0
- package/lib/node/server-diagnostics.js +1 -1
- package/lib/node/server-diagnostics.js.map +1 -1
- package/lib/node/server-state-snapshot.d.ts +1 -2
- package/lib/node/server-state-snapshot.d.ts.map +1 -1
- package/lib/node/server-state-snapshot.js +4 -4
- package/lib/node/server-state-snapshot.js.map +1 -1
- package/lib/node/socket-launcher.d.ts +43 -7
- package/lib/node/socket-launcher.d.ts.map +1 -1
- package/lib/node/socket-launcher.js +42 -7
- package/lib/node/socket-launcher.js.map +1 -1
- package/lib/node/startup-cpu-profile.d.ts +29 -0
- package/lib/node/startup-cpu-profile.d.ts.map +1 -0
- package/lib/node/startup-cpu-profile.js +91 -0
- package/lib/node/startup-cpu-profile.js.map +1 -0
- package/lib/node/stdio-launcher.d.ts +13 -13
- package/lib/node/stdio-launcher.d.ts.map +1 -1
- package/lib/node/stdio-launcher.js +21 -11
- package/lib/node/stdio-launcher.js.map +1 -1
- package/lib/node/validate-workspace.d.ts +1 -1
- package/lib/node/validate-workspace.d.ts.map +1 -1
- package/lib/node/validate-workspace.js +5 -1
- package/lib/node/validate-workspace.js.map +1 -1
- package/lib/testing/document-uri-policy-conformance.js +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 +2 -1
- package/lib/testing/fake-document.d.ts.map +1 -1
- 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.js.map +1 -1
- package/lib/testing/make-test-services.d.ts +45 -14
- package/lib/testing/make-test-services.d.ts.map +1 -1
- package/lib/testing/make-test-services.js +23 -7
- package/lib/testing/make-test-services.js.map +1 -1
- package/lib/testing/make-test-tracer.d.ts +12 -0
- package/lib/testing/make-test-tracer.d.ts.map +1 -1
- package/lib/testing/make-test-tracer.js +13 -0
- package/lib/testing/make-test-tracer.js.map +1 -1
- package/lib/testing/node/applied-edit-capture.d.ts +29 -0
- package/lib/testing/node/applied-edit-capture.d.ts.map +1 -0
- package/lib/testing/node/applied-edit-capture.js +117 -0
- package/lib/testing/node/applied-edit-capture.js.map +1 -0
- 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 +21 -6
- package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
- package/lib/testing/node/lsp-server-connection.js +49 -106
- 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 +30 -1
- package/lib/testing/node/spawned-server.d.ts.map +1 -1
- package/lib/testing/node/spawned-server.js +39 -2
- 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 +46 -16
- package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-capture.js +111 -40
- package/lib/testing/playwright/server-log-capture.js.map +1 -1
- package/lib/testing/playwright/server-log-fixture.d.ts +16 -0
- package/lib/testing/playwright/server-log-fixture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-fixture.js +25 -6
- package/lib/testing/playwright/server-log-fixture.js.map +1 -1
- package/lib/testing/playwright/server-log-region.d.ts +15 -0
- package/lib/testing/playwright/server-log-region.d.ts.map +1 -0
- package/lib/testing/playwright/server-log-region.js +30 -0
- package/lib/testing/playwright/server-log-region.js.map +1 -0
- package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-rename-reporter.js +4 -3
- package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
- package/lib/testing/run-update-pipeline.d.ts +8 -5
- package/lib/testing/run-update-pipeline.d.ts.map +1 -1
- package/lib/testing/run-update-pipeline.js.map +1 -1
- package/lib/testing/stub-ast-document-manager.d.ts +11 -51
- package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
- package/lib/testing/stub-ast-document-manager.js +31 -59
- package/lib/testing/stub-ast-document-manager.js.map +1 -1
- package/lib/testing/stub-document-builder.d.ts +33 -11
- package/lib/testing/stub-document-builder.d.ts.map +1 -1
- package/lib/testing/stub-document-builder.js +110 -5
- package/lib/testing/stub-document-builder.js.map +1 -1
- package/lib/testing/stub-hydranium-text-documents.d.ts +33 -10
- package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
- package/lib/testing/stub-hydranium-text-documents.js +132 -16
- 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 +8 -7
- 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.d.ts.map +1 -1
- package/lib/testing/stub-project-manager.js +10 -0
- package/lib/testing/stub-project-manager.js.map +1 -1
- package/lib/testing/stub-self-save-registry.d.ts +1 -1
- package/lib/testing/stub-self-save-registry.d.ts.map +1 -1
- package/lib/testing/stub-service-registry.d.ts +1 -1
- package/lib/testing/stub-service-registry.js.map +1 -1
- package/lib/testing/stub-writable-file-system.d.ts +1 -1
- package/lib/testing/stub-writable-file-system.d.ts.map +1 -1
- package/lib/testing/stub-writable-file-system.js +2 -1
- package/lib/testing/stub-writable-file-system.js.map +1 -1
- package/lib/util/connection-liveness.d.ts +22 -6
- package/lib/util/connection-liveness.d.ts.map +1 -1
- package/lib/util/connection-liveness.js +75 -15
- package/lib/util/connection-liveness.js.map +1 -1
- package/lib/util/environment.d.ts +7 -0
- package/lib/util/environment.d.ts.map +1 -1
- package/lib/util/environment.js +15 -1
- package/lib/util/environment.js.map +1 -1
- package/lib/util/registry.d.ts.map +1 -1
- package/package.json +41 -60
- package/src/browser/host-heads-in-worker.ts +218 -0
- package/src/browser/index.ts +17 -0
- package/src/browser/indexeddb-file-system-store.ts +192 -0
- package/src/browser/start-language-server-in-worker.ts +56 -0
- package/src/documents/ast-document-manager.ts +477 -249
- package/src/documents/client-ids.ts +5 -22
- package/src/documents/client-session-errors.ts +26 -0
- package/src/documents/client-session-registry.ts +262 -0
- package/src/documents/dirty-state-tracker.ts +167 -0
- package/src/documents/document-release-handler.ts +404 -0
- package/src/documents/document-release-scheduler.ts +121 -0
- package/src/documents/file-system-task-queue.ts +74 -0
- package/src/documents/hydranium-text-documents.ts +1029 -616
- package/src/documents/index.ts +10 -2
- package/src/documents/language-client-shadow.ts +501 -0
- package/src/documents/model-ledger.ts +66 -0
- package/src/documents/text-ledger.ts +112 -0
- package/src/documents/version-sync-service.ts +289 -0
- package/src/index.ts +3 -0
- package/src/langium/ast-extension/ast-extension-service.ts +77 -41
- package/src/langium/ast-extension/ast-node-builder.ts +35 -13
- package/src/langium/bootstrap.ts +79 -43
- package/src/langium/build-phase-pass/build-phase-pass-service.ts +29 -19
- package/src/langium/build-phase-pass/build-phase-pass.ts +37 -8
- package/src/langium/composite-ast-reflection.ts +112 -0
- package/src/langium/config/configuration-provider.ts +119 -0
- package/src/langium/config/index.ts +1 -0
- package/src/langium/diagnostics/console-tee.ts +150 -0
- package/src/langium/diagnostics/hydranium-langium-profiler.ts +15 -39
- package/src/langium/diagnostics/log-preamble.ts +1 -1
- package/src/langium/diagnostics/logger.ts +51 -17
- package/src/langium/diagnostics/lsp-logger.ts +83 -45
- package/src/langium/document-builder/build-pipeline-integration.ts +57 -32
- package/src/langium/document-builder/build-session.ts +4 -3
- package/src/langium/document-builder/document-builder.ts +709 -105
- package/src/langium/integration-services.ts +58 -27
- package/src/langium/integrity/integrity-rule.ts +22 -8
- package/src/langium/integrity/integrity-service.ts +348 -71
- package/src/langium/keys/containment.ts +3 -2
- package/src/langium/keys/name-based-key-provider.ts +24 -12
- package/src/langium/language-module.ts +52 -9
- package/src/langium/model-service/client-session.ts +769 -0
- package/src/langium/model-service/index.ts +2 -0
- package/src/langium/model-service/model-events.ts +82 -0
- package/src/langium/model-service/model-service.ts +885 -522
- package/src/langium/module.ts +139 -63
- package/src/langium/naming/name-provider.ts +34 -8
- package/src/langium/project/abstract-project-manager.ts +409 -20
- package/src/langium/project/project-change-event.ts +25 -3
- package/src/langium/project/project-manager.ts +31 -2
- package/src/langium/project/single-project-manager.ts +16 -1
- package/src/langium/residency/cst-residency-service.ts +197 -73
- package/src/langium/scope/ast-node-description-provider.ts +111 -16
- package/src/langium/scope/built-local-symbols.ts +13 -0
- package/src/langium/scope/fallback-scope.ts +38 -0
- package/src/langium/scope/hydranium-scope-computation.ts +45 -44
- package/src/langium/scope/hydranium-scope-provider.ts +245 -41
- package/src/langium/scope/index.ts +1 -0
- package/src/langium/scope/reference-candidate-provider.ts +6 -5
- package/src/langium/scope/scope-extension-service.ts +148 -56
- package/src/langium/scope/scoped-ast-node-description.ts +1 -3
- package/src/langium/serialization/abstract-serializer.ts +57 -41
- package/src/langium/serialization/json-serializer.ts +14 -3
- package/src/langium/serialization/serializer.ts +1 -1
- package/src/langium/serialization/yaml-serializer.ts +26 -4
- package/src/langium/service-registry.ts +41 -2
- package/src/langium/shared-services.ts +13 -4
- package/src/langium/single-langium-copy.ts +42 -0
- package/src/langium/transfer/transfer-encoder.ts +76 -50
- package/src/langium/trivia/comment-preserver.ts +1410 -0
- package/src/langium/trivia/document-ending-preserver.ts +71 -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 +56 -0
- package/src/langium/trivia/trivia-service.ts +99 -0
- package/src/langium/update-rewrite/update-rewrite-service.ts +1 -1
- package/src/langium/update-rewrite/update-rewrite.ts +1 -1
- package/src/langium/validation/document-validator.ts +312 -31
- package/src/langium/validation/validation-contribution-collector.ts +10 -1
- package/src/langium/workspace/ast-node-locator.ts +33 -0
- package/src/langium/workspace/document-uri-policy.ts +32 -18
- package/src/langium/workspace/file-not-found.ts +35 -0
- package/src/langium/workspace/file-system-provider.ts +258 -16
- package/src/langium/workspace/hydranium-langium-document-factory.ts +107 -0
- package/src/langium/workspace/hydranium-workspace-lock.ts +86 -2
- package/src/langium/workspace/hydranium-workspace-manager.ts +214 -35
- package/src/langium/workspace/in-memory-file-system-provider.ts +54 -23
- package/src/langium/workspace/index.ts +3 -0
- package/src/langium/workspace/initialize-workspace.ts +96 -4
- package/src/langium/workspace/langium-documents.ts +19 -1
- package/src/langium/workspace/persistent-file-system-provider.ts +27 -15
- package/src/{documents → langium/workspace}/self-save-registry.ts +12 -14
- package/src/langium/workspace/syntax-errors.ts +34 -0
- package/src/langium/workspace/virtual-document.ts +106 -25
- package/src/langium/workspace/write-lock-scope.ts +27 -12
- package/src/launcher/integrated-server.ts +3 -1
- package/src/locale/server-locale.ts +6 -6
- 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/edit-requests-guard.ts +152 -0
- package/src/lsp/hydranium-document-update-handler.ts +284 -110
- package/src/lsp/index.ts +6 -2
- package/src/lsp/lsp-latency.ts +62 -0
- package/src/lsp/save-text-document.ts +63 -0
- package/src/lsp/semantic-token-provider.ts +146 -8
- package/src/lsp/shared-module.ts +4 -8
- package/src/lsp/start-language-server.ts +21 -7
- package/src/messages/carriers.ts +4 -6
- package/src/messages/index.ts +11 -2
- package/src/messages/renderer.ts +11 -3
- package/src/node/event-loop-monitor.ts +2 -1
- package/src/node/heap-ceiling.ts +108 -0
- package/src/node/index.ts +5 -1
- package/src/node/latency-from-env.ts +1 -1
- package/src/node/log-file-sink.ts +6 -2
- package/src/node/log-preamble-node.ts +20 -10
- package/src/node/measure-memory.ts +5 -4
- package/src/node/memory-monitor.ts +10 -10
- package/src/node/node-file-system-provider.ts +214 -57
- package/src/node/profile-capture.ts +16 -15
- package/src/node/profiling-run.ts +30 -8
- package/src/node/rename-over-open-readers.ts +94 -0
- package/src/node/server-diagnostics.ts +1 -1
- package/src/node/server-state-snapshot.ts +6 -7
- package/src/node/socket-launcher.ts +78 -14
- package/src/node/startup-cpu-profile.ts +94 -0
- package/src/node/stdio-launcher.ts +29 -19
- package/src/node/validate-workspace.ts +8 -2
- package/src/testing/document-uri-policy-conformance.ts +1 -1
- package/src/testing/fake-document.ts +2 -1
- package/src/testing/index.ts +2 -1
- package/src/testing/langium-test-helpers.ts +1 -0
- package/src/testing/make-test-services.ts +83 -31
- package/src/testing/make-test-tracer.ts +26 -0
- package/src/testing/node/applied-edit-capture.ts +141 -0
- 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 +83 -119
- package/src/testing/node/scratch-workspace.ts +10 -5
- package/src/testing/node/spawned-server.ts +57 -3
- package/src/testing/node/unhandled-rejections.ts +31 -0
- package/src/testing/parse-semantic-root.ts +1 -1
- 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 +137 -47
- package/src/testing/playwright/server-log-fixture.ts +46 -6
- package/src/testing/playwright/server-log-region.ts +32 -0
- package/src/testing/playwright/server-log-rename-reporter.ts +4 -3
- package/src/testing/run-update-pipeline.ts +8 -5
- package/src/testing/stub-ast-document-manager.ts +47 -123
- package/src/testing/stub-document-builder.ts +155 -18
- package/src/testing/stub-hydranium-text-documents.ts +184 -27
- package/src/testing/stub-langium-documents.ts +3 -2
- package/src/testing/stub-model-service.ts +9 -8
- package/src/testing/stub-project-manager.ts +10 -0
- package/src/testing/stub-self-save-registry.ts +1 -1
- package/src/testing/stub-service-registry.ts +1 -1
- package/src/testing/stub-writable-file-system.ts +3 -2
- package/src/util/connection-liveness.ts +81 -15
- package/src/util/environment.ts +24 -2
- package/lib/documents/language-client-text-shadow.d.ts +0 -124
- package/lib/documents/language-client-text-shadow.d.ts.map +0 -1
- package/lib/documents/language-client-text-shadow.js +0 -190
- package/lib/documents/language-client-text-shadow.js.map +0 -1
- package/lib/documents/self-save-registry.d.ts.map +0 -1
- package/lib/documents/self-save-registry.js.map +0 -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/lib/node/process-memory.d.ts +0 -66
- package/lib/node/process-memory.d.ts.map +0 -1
- package/lib/node/process-memory.js +0 -252
- package/lib/node/process-memory.js.map +0 -1
- package/src/documents/language-client-text-shadow.ts +0 -211
- package/src/lsp/instrument-connection.ts +0 -66
- package/src/node/process-memory.ts +0 -299
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, 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
|
+
import { AstUtils, GrammarAST, GrammarUtils, IndentationAwareTokenBuilder, isAstNode } from '@hydranium/langium';
|
|
10
|
+
import { hasSyntaxErrors } from '../workspace/syntax-errors.js';
|
|
11
|
+
function isComposite(node) {
|
|
12
|
+
return 'content' in node;
|
|
13
|
+
}
|
|
14
|
+
function tokenNameOf(node) {
|
|
15
|
+
return 'tokenType' in node ? node.tokenType.name : undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Carries a document's comments across a write that re-serializes it from the
|
|
19
|
+
* AST: extract against the node each comment hangs off, then splice into the
|
|
20
|
+
* serializer's output, located by re-parsing it.
|
|
21
|
+
*
|
|
22
|
+
* **Emitting comments from inside a serializer instead drops them silently for
|
|
23
|
+
* most nodes.** A hand-written concrete-syntax serializer reaches its children
|
|
24
|
+
* through its own per-`$type` emitters, not through
|
|
25
|
+
* `AbstractSerializer.serializeNode`, so a hook on that seam is reached by some
|
|
26
|
+
* nodes and bypassed by the rest — and the ones it misses fail as absence,
|
|
27
|
+
* which no test notices unless it asserts on exact text.
|
|
28
|
+
*/
|
|
29
|
+
export class CommentPreserver {
|
|
30
|
+
services;
|
|
31
|
+
/**
|
|
32
|
+
* **Registering a second preserver under this id throws.** A subclass added
|
|
33
|
+
* ALONGSIDE the framework's — rather than replacing it by rebinding the
|
|
34
|
+
* `comments` contribution sub-key — collides here, and the throw surfaces
|
|
35
|
+
* from the first write rather than from server start, because the service is
|
|
36
|
+
* constructed lazily. Pass an `id` to run both.
|
|
37
|
+
*/
|
|
38
|
+
id;
|
|
39
|
+
label = 'Comments';
|
|
40
|
+
/** Default {@link CommentPreserverOptions.maxIsolatedEdits}. */
|
|
41
|
+
static DEFAULT_ISOLATED_EDITS = 24;
|
|
42
|
+
tracer;
|
|
43
|
+
maxIsolatedEdits;
|
|
44
|
+
commentTokenNames;
|
|
45
|
+
indentationTokenNames;
|
|
46
|
+
/**
|
|
47
|
+
* Set for the duration of one {@link extract}: the run
|
|
48
|
+
* {@link resolveCommentRun} resolved last, so the run's next comment reuses
|
|
49
|
+
* it.
|
|
50
|
+
*/
|
|
51
|
+
commentRun;
|
|
52
|
+
/**
|
|
53
|
+
* Set for the duration of one {@link extract}: {@link syntaxSpan} per node,
|
|
54
|
+
* as a block a run is lifted out of would otherwise be measured once per
|
|
55
|
+
* comment.
|
|
56
|
+
*/
|
|
57
|
+
spanMemo;
|
|
58
|
+
/** Set for the duration of one {@link apply}; see {@link cachedAnchorKey}. */
|
|
59
|
+
keyMemo;
|
|
60
|
+
/** Set for the duration of one {@link apply}; see {@link ambiguousRetainedKey}. */
|
|
61
|
+
listVerdicts;
|
|
62
|
+
/** Set for the duration of one {@link apply}; see {@link parse}. */
|
|
63
|
+
lastParse;
|
|
64
|
+
constructor(services, options = {}) {
|
|
65
|
+
this.services = services;
|
|
66
|
+
this.id = options.id ?? 'comments';
|
|
67
|
+
this.maxIsolatedEdits = options.maxIsolatedEdits ?? CommentPreserver.DEFAULT_ISOLATED_EDITS;
|
|
68
|
+
this.tracer = services.shared.Tracer.for(options.logName ?? 'CommentPreserver');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Every comment terminal the grammar declares, by token name.
|
|
72
|
+
*
|
|
73
|
+
* Deliberately NOT `GrammarConfig.multilineCommentRules`, which Langium
|
|
74
|
+
* populates only with terminals whose regex spans lines — so a grammar that
|
|
75
|
+
* comments with `//` answers an empty list there and every one of its
|
|
76
|
+
* comments would be invisible to a capture keyed on it.
|
|
77
|
+
*/
|
|
78
|
+
getCommentTokenNames() {
|
|
79
|
+
if (!this.commentTokenNames) {
|
|
80
|
+
this.commentTokenNames = this.services.Grammar.rules
|
|
81
|
+
.filter(GrammarAST.isTerminalRule)
|
|
82
|
+
.filter(rule => GrammarUtils.isCommentTerminal(rule))
|
|
83
|
+
.map(rule => rule.name);
|
|
84
|
+
}
|
|
85
|
+
return this.commentTokenNames;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The names of the INDENT and DEDENT tokens the language's
|
|
89
|
+
* `IndentationAwareTokenBuilder` emits, `undefined` for a lexer that emits
|
|
90
|
+
* none. Override it for a lexer of your own that emits such tokens.
|
|
91
|
+
*
|
|
92
|
+
* They are syntax to the parser only. A DEDENT is zero-width at the start of
|
|
93
|
+
* the line after its block, and an INDENT covers the whitespace in front of
|
|
94
|
+
* the block's first line, so a comment line beside either reads as sharing a
|
|
95
|
+
* line with the block, and moves onto the block's last line or its header.
|
|
96
|
+
*/
|
|
97
|
+
getIndentationTokenNames() {
|
|
98
|
+
if (this.indentationTokenNames === undefined) {
|
|
99
|
+
const builder = this.services.parser.TokenBuilder;
|
|
100
|
+
this.indentationTokenNames =
|
|
101
|
+
builder instanceof IndentationAwareTokenBuilder
|
|
102
|
+
? { indent: builder.indentTokenType.name, dedent: builder.dedentTokenType.name }
|
|
103
|
+
: null;
|
|
104
|
+
}
|
|
105
|
+
return this.indentationTokenNames ?? undefined;
|
|
106
|
+
}
|
|
107
|
+
isIndentation(node) {
|
|
108
|
+
const names = this.getIndentationTokenNames();
|
|
109
|
+
const name = tokenNameOf(node);
|
|
110
|
+
return names !== undefined && (name === names.indent || name === names.dedent);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Where the syntax of `node` begins and ends without the indentation tokens
|
|
114
|
+
* at its edges.
|
|
115
|
+
*/
|
|
116
|
+
syntaxSpan(node) {
|
|
117
|
+
if (this.getIndentationTokenNames() === undefined) {
|
|
118
|
+
return node;
|
|
119
|
+
}
|
|
120
|
+
if (this.spanMemo?.has(node)) {
|
|
121
|
+
return this.spanMemo.get(node);
|
|
122
|
+
}
|
|
123
|
+
const first = this.edgeToken(node, 1);
|
|
124
|
+
const last = this.edgeToken(node, -1);
|
|
125
|
+
const span = first && last && { offset: first.offset, end: last.end };
|
|
126
|
+
this.spanMemo?.set(node, span);
|
|
127
|
+
return span;
|
|
128
|
+
}
|
|
129
|
+
/** The first token of `node` for a `step` of 1, the last for -1, that is neither hidden nor indentation. */
|
|
130
|
+
edgeToken(node, step) {
|
|
131
|
+
if (!isComposite(node)) {
|
|
132
|
+
return node.hidden || this.isIndentation(node) ? undefined : node;
|
|
133
|
+
}
|
|
134
|
+
const content = node.content;
|
|
135
|
+
for (let index = step > 0 ? 0 : content.length - 1; index >= 0 && index < content.length; index += step) {
|
|
136
|
+
const found = this.edgeToken(content[index], step);
|
|
137
|
+
if (found !== undefined) {
|
|
138
|
+
return found;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Stable identity for one AST node, used to find it again in the
|
|
145
|
+
* re-serialized text — or `undefined` when no stable identity exists.
|
|
146
|
+
*
|
|
147
|
+
* **Read through the `NameProvider`, so the anchor is what the grammar treats
|
|
148
|
+
* as IDENTITY rather than whatever sits on `name`.** A grammar whose `name`
|
|
149
|
+
* is a display label, carrying its identifier on another property, would
|
|
150
|
+
* otherwise anchor comments to a value users retitle freely. A simple
|
|
151
|
+
* retitle can be matched, but the real identifier remains the safer key.
|
|
152
|
+
* `NameProviderOptions.nameProperties` names that property.
|
|
153
|
+
*
|
|
154
|
+
* **Whatever an override returns must survive a sibling being inserted into
|
|
155
|
+
* the same list.** A positional key does not: every comment after the
|
|
156
|
+
* insertion point is then written against the following declaration — a move,
|
|
157
|
+
* which reads as text the author wrote there and which nothing downstream can
|
|
158
|
+
* detect. That is why an unidentified node answers `undefined` here rather
|
|
159
|
+
* than falling back to a container index, and why this cannot delegate to
|
|
160
|
+
* `ElementKeyProvider`, whose name-based strategy makes exactly that fallback:
|
|
161
|
+
* its keys are derived and consumed within one snapshot, where no position can
|
|
162
|
+
* have shifted underneath them.
|
|
163
|
+
*
|
|
164
|
+
* Overriding is the supported route for a grammar that identifies some nodes
|
|
165
|
+
* outside the naming surface altogether — by a cross-reference that is unique
|
|
166
|
+
* among its siblings, say. Capture and lookup both read this one definition,
|
|
167
|
+
* so an override cannot make those two disagree.
|
|
168
|
+
*
|
|
169
|
+
* **Matching a rename does not read it.** That comparison asks the
|
|
170
|
+
* `NameProvider` what changed, so a node identified only by an override is
|
|
171
|
+
* carried across a write that leaves its identity alone and dropped by one
|
|
172
|
+
* that rewrites it — never misplaced, but never followed either.
|
|
173
|
+
*/
|
|
174
|
+
anchorKey(node) {
|
|
175
|
+
const nameProvider = this.services.references.NameProvider;
|
|
176
|
+
const segments = [];
|
|
177
|
+
let current = node;
|
|
178
|
+
while (current?.$container) {
|
|
179
|
+
const name = nameProvider.getOwnName(current);
|
|
180
|
+
if (name === undefined || name.length === 0) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
segments.unshift(`${current.$type}#${this.escapeKeySegment(name)}`);
|
|
184
|
+
current = current.$container;
|
|
185
|
+
}
|
|
186
|
+
return segments.length === 0 ? `@root:${node.$type}` : segments.join('/');
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Escape the characters {@link anchorKey} builds its keys out of, so a name
|
|
190
|
+
* containing one cannot spell a key another node also answers to — which
|
|
191
|
+
* would make two distinct nodes indistinguishable to the lookup.
|
|
192
|
+
*/
|
|
193
|
+
escapeKeySegment(name) {
|
|
194
|
+
return name.replace(/[\\#/]/g, character => `\\${character}`);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Collect every comment in `document`, each bound to the node it hangs off.
|
|
198
|
+
*
|
|
199
|
+
* Call this BEFORE the mutation that prompts the write. The anchors are AST
|
|
200
|
+
* objects, so a rule that renames one in place is reflected automatically;
|
|
201
|
+
* capturing afterwards would work too, but capturing first is what keeps the
|
|
202
|
+
* CST and the comments describing the same state.
|
|
203
|
+
*/
|
|
204
|
+
extract(document) {
|
|
205
|
+
// The capture reads the CST, which a residency policy may have shed.
|
|
206
|
+
// Restored rather than skipped: skipping would lose the whole file's
|
|
207
|
+
// comments for a document that had gone idle, silently and only under a
|
|
208
|
+
// shedding strategy.
|
|
209
|
+
this.services.shared.workspace.CstResidencyService.rehydrate(document);
|
|
210
|
+
const root = document.parseResult.value.$cstNode;
|
|
211
|
+
const commentTokens = this.getCommentTokenNames();
|
|
212
|
+
if (!root || commentTokens.length === 0) {
|
|
213
|
+
return { comments: [], sourceRoot: document.parseResult.value };
|
|
214
|
+
}
|
|
215
|
+
const source = document.textDocument.getText();
|
|
216
|
+
const comments = [];
|
|
217
|
+
this.commentRun = undefined;
|
|
218
|
+
this.spanMemo = new Map();
|
|
219
|
+
try {
|
|
220
|
+
this.captureFrom(root, source, commentTokens, comments);
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
this.commentRun = undefined;
|
|
224
|
+
this.spanMemo = undefined;
|
|
225
|
+
}
|
|
226
|
+
return { comments, sourceRoot: document.parseResult.value };
|
|
227
|
+
}
|
|
228
|
+
/** Recursive half of {@link extract}: one composite node's content, then its children. */
|
|
229
|
+
captureFrom(node, source, commentTokens, into) {
|
|
230
|
+
if (!isComposite(node)) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const content = node.content;
|
|
234
|
+
content.forEach((child, index) => {
|
|
235
|
+
const tokenName = tokenNameOf(child);
|
|
236
|
+
if (child.hidden && tokenName !== undefined && commentTokens.includes(tokenName)) {
|
|
237
|
+
into.push(this.classify(child, index, node, source));
|
|
238
|
+
}
|
|
239
|
+
this.captureFrom(child, source, commentTokens, into);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Decide which node a comment belongs to, and how it sat against it.
|
|
244
|
+
*
|
|
245
|
+
* A comment on the same line the previous node ENDED on is that node's
|
|
246
|
+
* trailing comment — checked first, because by source order it also precedes
|
|
247
|
+
* whatever comes next, and reading it as the NEXT node's leading comment is
|
|
248
|
+
* what relocates an end-of-line note onto the following declaration.
|
|
249
|
+
*/
|
|
250
|
+
classify(comment, index, container, source) {
|
|
251
|
+
const placed = this.placeAmongSyntax(comment, index, container, source);
|
|
252
|
+
const owner = placed.container;
|
|
253
|
+
const content = placed.content;
|
|
254
|
+
const at = placed.index;
|
|
255
|
+
const ownsOwnNode = (candidate) => candidate?.astNode !== undefined && candidate.astNode !== owner.astNode;
|
|
256
|
+
const realSibling = (from, step) => {
|
|
257
|
+
for (let scan = from; scan >= 0 && scan < content.length; scan += step) {
|
|
258
|
+
if (!content[scan].hidden) {
|
|
259
|
+
return content[scan];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return undefined;
|
|
263
|
+
};
|
|
264
|
+
const offsetOf = (node) => this.syntaxSpan(node)?.offset ?? node.offset;
|
|
265
|
+
const endOf = (node) => this.syntaxSpan(node)?.end ?? node.end;
|
|
266
|
+
const previous = realSibling(at - 1, -1);
|
|
267
|
+
const next = realSibling(at + 1, 1);
|
|
268
|
+
// Measured to whatever comes NEXT IN SOURCE, comment or not — never to the
|
|
269
|
+
// next real node, which would skip the rest of a comment block and make
|
|
270
|
+
// every line of it re-emit the whole gap that follows the block.
|
|
271
|
+
const following = content[at + 1];
|
|
272
|
+
const blankLinesAfter = following === undefined ? 0 : this.countBlankLines(source.slice(comment.end, offsetOf(following)));
|
|
273
|
+
const preceding = at > 0 ? content[at - 1] : undefined;
|
|
274
|
+
// From the last text before the comment: a block it is lifted out of can
|
|
275
|
+
// end with comment lines of its own, after the block's syntax.
|
|
276
|
+
const precedingEnd = preceding === undefined ? comment.offset : endOf(preceding);
|
|
277
|
+
let gapStart = comment.offset;
|
|
278
|
+
while (gapStart > precedingEnd && /\s/.test(source[gapStart - 1])) {
|
|
279
|
+
gapStart--;
|
|
280
|
+
}
|
|
281
|
+
const blankLinesBefore = preceding === undefined ? 0 : this.countBlankLines(source.slice(gapStart, comment.offset));
|
|
282
|
+
// Only a comment that OWNS its line can be reindented — one sharing a line
|
|
283
|
+
// with code keeps whatever spacing that line gives it.
|
|
284
|
+
const lineStart = source.lastIndexOf('\n', comment.offset - 1) + 1;
|
|
285
|
+
const beforeOnLine = source.slice(lineStart, comment.offset);
|
|
286
|
+
const sourceIndent = beforeOnLine.trim().length === 0 ? beforeOnLine : undefined;
|
|
287
|
+
const text = this.normalizeLineEndings(comment.text);
|
|
288
|
+
const sharesLineWithPrevious = previous !== undefined && !this.breaksLine(source, endOf(previous), comment.offset);
|
|
289
|
+
if (sharesLineWithPrevious) {
|
|
290
|
+
// On the same line as whatever precedes it, so it annotates that line.
|
|
291
|
+
// When the preceding sibling is the CONTAINER's own syntax — an opening
|
|
292
|
+
// brace, a name token, a keyword — the line is the container's header
|
|
293
|
+
// and the comment belongs to the container, NOT to the first member
|
|
294
|
+
// that happens to follow. Reading it as that member's leading comment
|
|
295
|
+
// moves a note about the declaration onto its first child.
|
|
296
|
+
return ownsOwnNode(previous)
|
|
297
|
+
? { text, anchor: previous.astNode, placement: 'trailing', blankLinesAfter: 0, blankLinesBefore: 0 }
|
|
298
|
+
: { text, anchor: owner.astNode, placement: 'trailingOnContainer', blankLinesAfter: 0, blankLinesBefore: 0 };
|
|
299
|
+
}
|
|
300
|
+
if (ownsOwnNode(next)) {
|
|
301
|
+
return { text, anchor: next.astNode, placement: 'leading', blankLinesAfter, blankLinesBefore: 0, sourceIndent };
|
|
302
|
+
}
|
|
303
|
+
if (ownsOwnNode(previous)) {
|
|
304
|
+
return { text, anchor: previous.astNode, placement: 'afterNode', blankLinesAfter: 0, blankLinesBefore, sourceIndent };
|
|
305
|
+
}
|
|
306
|
+
if (previous === undefined) {
|
|
307
|
+
return { text, anchor: owner.astNode, placement: 'atContainerStart', blankLinesAfter, blankLinesBefore: 0, sourceIndent };
|
|
308
|
+
}
|
|
309
|
+
// Surrounded by the container's own syntax on both sides. A further
|
|
310
|
+
// sibling after it means the comment sits INSIDE the construct — between
|
|
311
|
+
// the braces of an empty body, or partway through a header — so it stays
|
|
312
|
+
// with the container rather than being pushed past its closing syntax,
|
|
313
|
+
// which would move it out to the enclosing scope. Only a comment with
|
|
314
|
+
// nothing after it at all actually trails the container.
|
|
315
|
+
const placement = next === undefined ? 'atContainerEnd' : 'trailingOnContainer';
|
|
316
|
+
return { text, anchor: owner.astNode, placement, blankLinesAfter, blankLinesBefore, sourceIndent };
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Resolves the container {@link classify} anchors `comment` in, and the
|
|
320
|
+
* comment's adjacent siblings there and the nearest syntax on each side,
|
|
321
|
+
* without INDENT and DEDENT tokens. The content is that window, not all of
|
|
322
|
+
* the container's siblings.
|
|
323
|
+
*
|
|
324
|
+
* An indentation-aware lexer can emit a block's DEDENT after the comment
|
|
325
|
+
* lines that follow the block, so the parser nests them in it. An own-line
|
|
326
|
+
* comment left of the column a block's INDENT opened is lifted out of each
|
|
327
|
+
* block that ends right after it, with its adjacent hidden nodes, and placed
|
|
328
|
+
* after that block in the enclosing container.
|
|
329
|
+
*/
|
|
330
|
+
placeAmongSyntax(comment, index, container, source) {
|
|
331
|
+
const content = container.content;
|
|
332
|
+
const run = this.resolveCommentRun(index, container, source);
|
|
333
|
+
let placed = 0;
|
|
334
|
+
if (run.targets.length > 1 && (run.previous === undefined || this.breaksLine(source, run.previous.end, comment.offset))) {
|
|
335
|
+
const column = this.columnAt(source, comment.offset);
|
|
336
|
+
while (placed + 1 < run.targets.length && column < (run.targets[placed + 1].liftBelow ?? 0)) {
|
|
337
|
+
placed++;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const { container: parent, closed } = run.targets[placed];
|
|
341
|
+
const before = closed ? [closed] : run.before;
|
|
342
|
+
const around = content.slice(Math.max(index - 1, run.start), Math.min(index + 1, run.end) + 1);
|
|
343
|
+
const view = [...before, ...around, ...run.after[placed]];
|
|
344
|
+
return { container: parent, content: view, index: before.length + (index > run.start ? 1 : 0) };
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* The run of hidden nodes around `container.content[index]`, and the blocks
|
|
348
|
+
* it can be lifted out of: each that ends right after the run and whose
|
|
349
|
+
* INDENT opened a column, up to the first that does not.
|
|
350
|
+
*/
|
|
351
|
+
resolveCommentRun(index, container, source) {
|
|
352
|
+
const content = container.content;
|
|
353
|
+
const cached = this.commentRun;
|
|
354
|
+
// Resolved once per run, not per comment: a run of k comment lines would
|
|
355
|
+
// otherwise cost k scans of the run.
|
|
356
|
+
if (cached?.content === content && index >= cached.start && index <= cached.end) {
|
|
357
|
+
return cached;
|
|
358
|
+
}
|
|
359
|
+
let start = index;
|
|
360
|
+
while (start > 0 && content[start - 1].hidden) {
|
|
361
|
+
start--;
|
|
362
|
+
}
|
|
363
|
+
let end = index;
|
|
364
|
+
while (end + 1 < content.length && content[end + 1].hidden) {
|
|
365
|
+
end++;
|
|
366
|
+
}
|
|
367
|
+
let previous;
|
|
368
|
+
const targets = [{ container, last: end }];
|
|
369
|
+
if (this.getIndentationTokenNames() !== undefined) {
|
|
370
|
+
for (let scan = start - 1; scan >= 0 && previous === undefined; scan--) {
|
|
371
|
+
previous = content[scan].hidden ? undefined : this.syntaxSpan(content[scan]);
|
|
372
|
+
}
|
|
373
|
+
let target = targets[0];
|
|
374
|
+
while (target.container.container && this.closesAfter(target.container.content, target.last)) {
|
|
375
|
+
const opened = this.openedColumn(target.container.content, target.closed ? target.last : start, source) ?? 0;
|
|
376
|
+
if (opened === 0) {
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
const parent = target.container.container;
|
|
380
|
+
target = {
|
|
381
|
+
container: parent,
|
|
382
|
+
last: this.indexIn(parent.content, target.container),
|
|
383
|
+
closed: target.container,
|
|
384
|
+
liftBelow: opened
|
|
385
|
+
};
|
|
386
|
+
targets.push(target);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const before = this.neighbours(content, start - 1, -1).reverse();
|
|
390
|
+
const after = targets.map(target => this.neighbours(target.container.content, target.last + 1, 1));
|
|
391
|
+
return (this.commentRun = { content, start, end, previous, targets, before, after });
|
|
392
|
+
}
|
|
393
|
+
/** The nodes of `content` from `from` in `step` direction up to the first that is syntax, without indentation tokens. */
|
|
394
|
+
neighbours(content, from, step) {
|
|
395
|
+
const found = [];
|
|
396
|
+
for (let scan = from; scan >= 0 && scan < content.length; scan += step) {
|
|
397
|
+
const node = content[scan];
|
|
398
|
+
if (!this.isIndentation(node)) {
|
|
399
|
+
found.push(node);
|
|
400
|
+
if (!node.hidden) {
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return found;
|
|
406
|
+
}
|
|
407
|
+
/** Whether only hidden and indentation tokens follow `index` in `content`. */
|
|
408
|
+
closesAfter(content, index) {
|
|
409
|
+
for (let scan = index + 1; scan < content.length; scan++) {
|
|
410
|
+
if (!content[scan].hidden && !this.isIndentation(content[scan])) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
/** The column the last INDENT before `content[index]` opens, `undefined` without one. */
|
|
417
|
+
openedColumn(content, index, source) {
|
|
418
|
+
const indent = this.getIndentationTokenNames()?.indent;
|
|
419
|
+
for (let scan = index - 1; scan >= 0; scan--) {
|
|
420
|
+
if (tokenNameOf(content[scan]) === indent) {
|
|
421
|
+
return this.columnAt(source, content[scan].end);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return undefined;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* The index of `node` in `content`, found by offset: `indexOf` per lifted
|
|
428
|
+
* block makes extraction quadratic in a block's siblings.
|
|
429
|
+
*/
|
|
430
|
+
indexIn(content, node) {
|
|
431
|
+
let low = 0;
|
|
432
|
+
let high = content.length;
|
|
433
|
+
while (low < high) {
|
|
434
|
+
const middle = (low + high) >> 1;
|
|
435
|
+
if (content[middle].offset < node.offset) {
|
|
436
|
+
low = middle + 1;
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
high = middle;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
// Zero-width tokens can share the node's offset.
|
|
443
|
+
for (let index = low; index < content.length && content[index].offset === node.offset; index++) {
|
|
444
|
+
if (content[index] === node) {
|
|
445
|
+
return index;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return content.indexOf(node);
|
|
449
|
+
}
|
|
450
|
+
/** Whether `text` has a line break from `from` up to `to`, without copying the span. */
|
|
451
|
+
breaksLine(text, from, to) {
|
|
452
|
+
// Not indexOf, which scans on to the next line break past `to`.
|
|
453
|
+
for (let offset = from; offset < to; offset++) {
|
|
454
|
+
if (text.charCodeAt(offset) === 10) {
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
/** The column of `offset` in `text`. */
|
|
461
|
+
columnAt(text, offset) {
|
|
462
|
+
return offset - (text.lastIndexOf('\n', offset - 1) + 1);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Comment text with CRLF reduced to LF.
|
|
466
|
+
*
|
|
467
|
+
* Serializers emit LF, so splicing a comment captured from a CRLF document
|
|
468
|
+
* verbatim puts lone CR bytes in the middle of LF-terminated lines.
|
|
469
|
+
*/
|
|
470
|
+
normalizeLineEndings(text) {
|
|
471
|
+
return text.includes('\r') ? text.replace(/\r\n/g, '\n') : text;
|
|
472
|
+
}
|
|
473
|
+
/** Blank lines in a run of whitespace — one fewer than its newlines. */
|
|
474
|
+
countBlankLines(gap) {
|
|
475
|
+
return Math.max(0, (gap.match(/\n/g)?.length ?? 0) - 1);
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Splice the captured comments back into `serialized`.
|
|
479
|
+
*
|
|
480
|
+
* A comment whose anchor the write deleted, or whose anchor has no stable
|
|
481
|
+
* key, is dropped — counted at `debug`, because a silent drop is the failure
|
|
482
|
+
* this preserver exists to remove and an unexplained one just moves it.
|
|
483
|
+
*/
|
|
484
|
+
apply(serialized, trivia, uri) {
|
|
485
|
+
if (trivia.comments.length === 0) {
|
|
486
|
+
return serialized;
|
|
487
|
+
}
|
|
488
|
+
// The memos are scoped to this call and not to the instance, because an
|
|
489
|
+
// in-place write changes what a node's key IS: a repair that renames a
|
|
490
|
+
// node between two writes would otherwise be answered from the first.
|
|
491
|
+
this.keyMemo = new Map();
|
|
492
|
+
this.listVerdicts = new Map();
|
|
493
|
+
this.lastParse = {};
|
|
494
|
+
try {
|
|
495
|
+
return this.applyComments(serialized, trivia, uri);
|
|
496
|
+
}
|
|
497
|
+
finally {
|
|
498
|
+
this.keyMemo = undefined;
|
|
499
|
+
this.listVerdicts = undefined;
|
|
500
|
+
this.lastParse = undefined;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/** {@link apply}'s body, inside the per-write memos it sets up. */
|
|
504
|
+
applyComments(serialized, trivia, uri) {
|
|
505
|
+
const spliced = this.spliceComments(serialized, trivia, uri);
|
|
506
|
+
// **Never hand back text the grammar cannot read.** A splice edits syntax
|
|
507
|
+
// it did not produce, and a line comment in particular ends whatever
|
|
508
|
+
// shares its line — so a placement that is merely wrong becomes a file
|
|
509
|
+
// that no longer parses, written to disk by an integrity repair with no
|
|
510
|
+
// user gesture. Dropping the comments is recoverable; corrupting the
|
|
511
|
+
// document is not.
|
|
512
|
+
if (spliced !== serialized && this.parse(spliced, uri) === undefined) {
|
|
513
|
+
this.tracer.withUri(uri.toString()).warn('Reattaching comments produced text that does not parse; writing without them');
|
|
514
|
+
return serialized;
|
|
515
|
+
}
|
|
516
|
+
return spliced;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* {@link anchorKey}, answered once per node per write.
|
|
520
|
+
*
|
|
521
|
+
* The key walks `$container` to the root building a segment each step, and
|
|
522
|
+
* every stage of a write asks about the same nodes — locating spans, matching
|
|
523
|
+
* a rename, judging a key the output kept. Recomputing makes each of those
|
|
524
|
+
* stages cost nodes × depth, and the stages that scan a sibling list do it
|
|
525
|
+
* once per comment.
|
|
526
|
+
*/
|
|
527
|
+
cachedAnchorKey(node) {
|
|
528
|
+
const memo = this.keyMemo;
|
|
529
|
+
if (memo === undefined) {
|
|
530
|
+
return this.anchorKey(node);
|
|
531
|
+
}
|
|
532
|
+
if (!memo.has(node)) {
|
|
533
|
+
memo.set(node, this.anchorKey(node));
|
|
534
|
+
}
|
|
535
|
+
return memo.get(node);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Parse `text` as an unregistered document, or `undefined` when it is not
|
|
539
|
+
* readable — by reported error, or by a throw from a URI that routes to no
|
|
540
|
+
* services. Does NOT register the result, so `LangiumDocuments` is untouched.
|
|
541
|
+
*
|
|
542
|
+
* Within one {@link apply}, the text and URI parsed last are answered with
|
|
543
|
+
* the same document rather than parsed again, so a caller must not change
|
|
544
|
+
* it. One entry, so no document outlives the next parse.
|
|
545
|
+
*/
|
|
546
|
+
parse(text, uri) {
|
|
547
|
+
const memo = this.lastParse;
|
|
548
|
+
if (memo !== undefined && memo.text === text && memo.uri === uri.toString()) {
|
|
549
|
+
return memo.document;
|
|
550
|
+
}
|
|
551
|
+
let document;
|
|
552
|
+
try {
|
|
553
|
+
document = this.services.shared.workspace.LangiumDocumentFactory.fromString(text, uri);
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
document = undefined;
|
|
557
|
+
}
|
|
558
|
+
if (document !== undefined && hasSyntaxErrors(document.parseResult)) {
|
|
559
|
+
document = undefined;
|
|
560
|
+
}
|
|
561
|
+
if (memo !== undefined) {
|
|
562
|
+
memo.text = text;
|
|
563
|
+
memo.uri = uri.toString();
|
|
564
|
+
memo.document = document;
|
|
565
|
+
}
|
|
566
|
+
return document;
|
|
567
|
+
}
|
|
568
|
+
/** The splice itself, run before {@link apply}'s parse check. */
|
|
569
|
+
spliceComments(serialized, trivia, uri) {
|
|
570
|
+
const located = this.locate(serialized, uri);
|
|
571
|
+
if (located === undefined) {
|
|
572
|
+
return serialized;
|
|
573
|
+
}
|
|
574
|
+
const sourceOwners = new Map();
|
|
575
|
+
const sourceCollisions = new Set();
|
|
576
|
+
const liveAnchors = new Set();
|
|
577
|
+
for (const node of [trivia.sourceRoot, ...AstUtils.streamAllContents(trivia.sourceRoot)]) {
|
|
578
|
+
liveAnchors.add(node);
|
|
579
|
+
const key = this.cachedAnchorKey(node);
|
|
580
|
+
if (key === undefined) {
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
const owner = sourceOwners.get(key);
|
|
584
|
+
if (owner !== undefined && owner !== node) {
|
|
585
|
+
sourceCollisions.add(key);
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
sourceOwners.set(key, node);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
const needsRename = trivia.comments.some(comment => {
|
|
592
|
+
const key = this.cachedAnchorKey(comment.anchor);
|
|
593
|
+
return key !== undefined && !located.has(key);
|
|
594
|
+
});
|
|
595
|
+
const renamed = needsRename
|
|
596
|
+
? this.matchRenamedAnchors(trivia.sourceRoot, serialized, uri, located, sourceOwners, sourceCollisions)
|
|
597
|
+
: new Map();
|
|
598
|
+
const edits = [];
|
|
599
|
+
let dropped = 0;
|
|
600
|
+
trivia.comments.forEach((comment, order) => {
|
|
601
|
+
const key = this.cachedAnchorKey(comment.anchor);
|
|
602
|
+
const destination = key === undefined ? undefined : located.has(key) ? key : this.renamedKey(comment.anchor, key, renamed);
|
|
603
|
+
const retained = destination === key ? located.get(key) : undefined;
|
|
604
|
+
const span = key === undefined ||
|
|
605
|
+
!liveAnchors.has(comment.anchor) ||
|
|
606
|
+
sourceCollisions.has(key) ||
|
|
607
|
+
destination === undefined ||
|
|
608
|
+
(retained !== undefined && this.ambiguousRetainedKey(comment.anchor, retained.owner))
|
|
609
|
+
? undefined
|
|
610
|
+
: located.get(destination);
|
|
611
|
+
if (span === undefined) {
|
|
612
|
+
dropped++;
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const edit = this.editFor(comment, span, serialized);
|
|
616
|
+
edits.push({
|
|
617
|
+
...edit,
|
|
618
|
+
order,
|
|
619
|
+
inline: this.needsReadBack(comment, span, serialized) ? { text: comment.text, key: destination } : undefined
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
if (dropped > 0) {
|
|
623
|
+
this.tracer.withUri(uri.toString()).debug(`Dropped ${dropped} comment(s): anchor deleted by the write, or carrying no stable key`);
|
|
624
|
+
}
|
|
625
|
+
// Stable by offset, then by capture order, so several comments landing on
|
|
626
|
+
// one anchor keep the order the author wrote them in.
|
|
627
|
+
edits.sort((left, right) => left.at - right.at || left.order - right.order);
|
|
628
|
+
// **This is what licenses the mid-line split** `editFor` makes for a
|
|
629
|
+
// `leading` comment whose anchor does not start its line. Parsing alone
|
|
630
|
+
// cannot license it: text can parse perfectly well having handed the
|
|
631
|
+
// comment to the following declaration instead. Re-extracting and
|
|
632
|
+
// requiring the same anchor back is the only evidence the comment still
|
|
633
|
+
// belongs where its author put it.
|
|
634
|
+
//
|
|
635
|
+
// A failed edit is dropped and the rest reassembled, since removing one
|
|
636
|
+
// changes what the others land in; two rounds, then the inline edits go
|
|
637
|
+
// as a set rather than iterating towards a text nothing has verified.
|
|
638
|
+
let remaining = edits;
|
|
639
|
+
let result = this.assembleComments(serialized, remaining);
|
|
640
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
641
|
+
const failed = this.unverifiedInlineEdits(result, remaining, uri, serialized);
|
|
642
|
+
if (failed.size === 0) {
|
|
643
|
+
return result;
|
|
644
|
+
}
|
|
645
|
+
remaining = remaining.filter(edit => !failed.has(edit));
|
|
646
|
+
result = this.assembleComments(serialized, remaining);
|
|
647
|
+
}
|
|
648
|
+
return this.assembleComments(serialized, remaining.filter(edit => edit.inline === undefined));
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Whether this comment's edit has to be read back before it can be trusted.
|
|
652
|
+
*
|
|
653
|
+
* Both cases are the serializer having put the anchor on a line it shares.
|
|
654
|
+
* A comment that wants the line above has to open one mid-construct; a
|
|
655
|
+
* comment that wants the end of the anchor's line gets the end of a line that
|
|
656
|
+
* may belong to a later sibling, and a second such comment lands inside the
|
|
657
|
+
* first one's text and stops being a comment at all. Neither can be judged
|
|
658
|
+
* from the offsets alone — only from what the text says once re-read.
|
|
659
|
+
*/
|
|
660
|
+
needsReadBack(comment, span, serialized) {
|
|
661
|
+
if (comment.placement === 'leading' || comment.placement === 'atContainerStart') {
|
|
662
|
+
return !this.startOfLineIsBlank(serialized, span.offset);
|
|
663
|
+
}
|
|
664
|
+
return comment.placement === 'trailing' && this.endOfLineAt(serialized, span.end) !== span.end;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Splice `edits` into `serialized`, in the order given.
|
|
668
|
+
*
|
|
669
|
+
* Two inline edits landing on one offset collapse into a single opened line,
|
|
670
|
+
* so a stack of comments above one inline member comes back as a stack rather
|
|
671
|
+
* than with a blank line between each pair.
|
|
672
|
+
*/
|
|
673
|
+
assembleComments(serialized, edits) {
|
|
674
|
+
let result = '';
|
|
675
|
+
let cursor = 0;
|
|
676
|
+
let previousInlineAt;
|
|
677
|
+
for (const edit of edits) {
|
|
678
|
+
const text = edit.inline !== undefined && edit.at === previousInlineAt ? edit.text.replace(/^\n[ \t]*/, '') : edit.text;
|
|
679
|
+
result += serialized.slice(cursor, edit.at) + text;
|
|
680
|
+
cursor = edit.at;
|
|
681
|
+
previousInlineAt = edit.inline !== undefined ? edit.at : undefined;
|
|
682
|
+
}
|
|
683
|
+
return result + serialized.slice(cursor);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Whether the output node now answering to `source`'s key is too likely to be
|
|
687
|
+
* a DIFFERENT node for the comment to follow it.
|
|
688
|
+
*
|
|
689
|
+
* A key surviving the write normally means the node did. It can also mean a
|
|
690
|
+
* sibling took the name over, which reads as the author having written the
|
|
691
|
+
* comment about a declaration they never saw.
|
|
692
|
+
*
|
|
693
|
+
* **What is detectable is a list whose members changed places; what is not is
|
|
694
|
+
* a payload that renames a node and gives its old name to another.** Those
|
|
695
|
+
* two writes produce the same text and the same keys, and the node carrying
|
|
696
|
+
* the old name afterwards is byte-identical under both readings, so no rule
|
|
697
|
+
* over this evidence separates them. A caller that knows which node it
|
|
698
|
+
* renamed is the only thing that could, and the transfer model carries no
|
|
699
|
+
* such record.
|
|
700
|
+
*
|
|
701
|
+
* **The evidence is the ORDER of the keys the two lists share.** Adding or
|
|
702
|
+
* removing siblings shifts the rest but leaves them in the same relative
|
|
703
|
+
* order; only names changing places can put two shared keys out of order. A
|
|
704
|
+
* list whose shared keys invert is therefore read as exchanged identities and
|
|
705
|
+
* those siblings lose their comments — which costs a deliberate reorder its
|
|
706
|
+
* comments, the conservative half of a trade whose other half is that a bare
|
|
707
|
+
* swap cannot move one.
|
|
708
|
+
*
|
|
709
|
+
* **Comparing counts or key SETS instead misses cases each way.** A swap
|
|
710
|
+
* alongside an insertion leaves the counts differing and the key set whole,
|
|
711
|
+
* so neither test sees it; a deletion alongside an insertion leaves the counts
|
|
712
|
+
* equal while every surviving sibling is still itself, so a count test drops
|
|
713
|
+
* comments that were never in doubt.
|
|
714
|
+
*/
|
|
715
|
+
ambiguousRetainedKey(source, output) {
|
|
716
|
+
const sourceParent = source.$container;
|
|
717
|
+
const outputParent = output.$container;
|
|
718
|
+
if (!sourceParent || !outputParent) {
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
const parentKey = this.cachedAnchorKey(sourceParent);
|
|
722
|
+
if (parentKey !== this.cachedAnchorKey(outputParent) || source.$containerProperty !== output.$containerProperty) {
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
if (source.$containerIndex === output.$containerIndex) {
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
const property = source.$containerProperty;
|
|
729
|
+
if (property === undefined) {
|
|
730
|
+
return true;
|
|
731
|
+
}
|
|
732
|
+
// Answered once per list rather than once per comment. One insertion moves
|
|
733
|
+
// every later sibling, so each of their comments asks the same question of
|
|
734
|
+
// the same two lists — and scanning both per comment costs the list's
|
|
735
|
+
// length squared.
|
|
736
|
+
//
|
|
737
|
+
// Memoised against the parent NODE rather than its key, because two
|
|
738
|
+
// same-named parents answer to one key and would otherwise share a verdict
|
|
739
|
+
// about lists that have nothing to do with each other.
|
|
740
|
+
const cached = this.listVerdicts?.get(sourceParent)?.get(property);
|
|
741
|
+
if (cached !== undefined) {
|
|
742
|
+
return cached;
|
|
743
|
+
}
|
|
744
|
+
const verdict = this.reidentifiedList(sourceParent, outputParent, property);
|
|
745
|
+
if (this.listVerdicts !== undefined) {
|
|
746
|
+
const byProperty = this.listVerdicts.get(sourceParent) ?? new Map();
|
|
747
|
+
byProperty.set(property, verdict);
|
|
748
|
+
this.listVerdicts.set(sourceParent, byProperty);
|
|
749
|
+
}
|
|
750
|
+
return verdict;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Whether the two lists differ in a way a plain insertion or deletion cannot
|
|
754
|
+
* explain — the expensive half of {@link ambiguousRetainedKey}, split out so
|
|
755
|
+
* it can be answered once per list.
|
|
756
|
+
*
|
|
757
|
+
* What says the members may have swapped names rather than moved:
|
|
758
|
+
*
|
|
759
|
+
* - **The lists are the same length.** Every rename chain over a fixed set of
|
|
760
|
+
* slots looks exactly like the shift a deletion plus an insertion produces,
|
|
761
|
+
* and for members carrying nothing but a name the properties match under
|
|
762
|
+
* both readings. Neither this nor anything downstream can separate them.
|
|
763
|
+
* - **Keys present on both sides have changed places.** Adding or removing
|
|
764
|
+
* siblings shifts the rest but never reorders them, so an inversion is
|
|
765
|
+
* evidence no insertion or deletion can account for — and it is evidence
|
|
766
|
+
* the lengths alone miss, since a swap alongside an insertion leaves the
|
|
767
|
+
* counts differing and the key set whole.
|
|
768
|
+
*/
|
|
769
|
+
reidentifiedList(sourceParent, outputParent, property) {
|
|
770
|
+
const before = sourceParent[property];
|
|
771
|
+
const after = outputParent[property];
|
|
772
|
+
if (!Array.isArray(before) || !Array.isArray(after)) {
|
|
773
|
+
return true;
|
|
774
|
+
}
|
|
775
|
+
if (before.length === after.length) {
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
const positions = new Map();
|
|
779
|
+
after.filter(isAstNode).forEach((node, index) => {
|
|
780
|
+
const key = this.cachedAnchorKey(node);
|
|
781
|
+
if (key !== undefined && !positions.has(key)) {
|
|
782
|
+
positions.set(key, index);
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
let furthest = -1;
|
|
786
|
+
for (const node of before.filter(isAstNode)) {
|
|
787
|
+
const key = this.cachedAnchorKey(node);
|
|
788
|
+
const position = key === undefined ? undefined : positions.get(key);
|
|
789
|
+
if (position === undefined) {
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
if (position < furthest) {
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
furthest = position;
|
|
796
|
+
}
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* The inline edits `candidate` did not round-trip: each one whose comment came
|
|
801
|
+
* back on a different anchor, and — when `candidate` does not parse at all —
|
|
802
|
+
* each one that cannot be put back beside the others without breaking it
|
|
803
|
+
* again. `serialized` is the unspliced text those trials are rebuilt from,
|
|
804
|
+
* since finding the single edit at fault means assembling the rest without it.
|
|
805
|
+
*
|
|
806
|
+
* Costs a parse and an extract, so it answers empty without either when
|
|
807
|
+
* nothing was spliced inline — which is every write into a document the
|
|
808
|
+
* serializer lays out one declaration per line.
|
|
809
|
+
*/
|
|
810
|
+
unverifiedInlineEdits(candidate, edits, uri, serialized) {
|
|
811
|
+
const inlineEdits = edits.filter(edit => edit.inline !== undefined);
|
|
812
|
+
if (inlineEdits.length === 0) {
|
|
813
|
+
return new Set();
|
|
814
|
+
}
|
|
815
|
+
const reparsed = this.parse(candidate, uri);
|
|
816
|
+
if (reparsed === undefined) {
|
|
817
|
+
if (inlineEdits.length > this.maxIsolatedEdits) {
|
|
818
|
+
this.tracer
|
|
819
|
+
.withUri(uri.toString())
|
|
820
|
+
.debug(`Spliced text did not parse; dropping ${inlineEdits.length} shared-line comments without isolating`);
|
|
821
|
+
return new Set(inlineEdits);
|
|
822
|
+
}
|
|
823
|
+
const retained = edits.filter(edit => edit.inline === undefined);
|
|
824
|
+
const failed = new Set();
|
|
825
|
+
for (const edit of inlineEdits) {
|
|
826
|
+
const trial = [...retained, edit].sort((left, right) => left.at - right.at || left.order - right.order);
|
|
827
|
+
const text = this.assembleComments(serialized, trial);
|
|
828
|
+
if (this.parse(text, uri) !== undefined && this.unverifiedInlineEdits(text, trial, uri, serialized).size === 0) {
|
|
829
|
+
retained.push(edit);
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
failed.add(edit);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
return failed;
|
|
836
|
+
}
|
|
837
|
+
// Counted rather than scanned per edit: several comments can share an
|
|
838
|
+
// anchor and a text, and each edit must consume one of them.
|
|
839
|
+
const captured = new Map();
|
|
840
|
+
for (const comment of this.extract(reparsed).comments) {
|
|
841
|
+
const seen = `${this.cachedAnchorKey(comment.anchor) ?? ''}\u0000${comment.text}`;
|
|
842
|
+
captured.set(seen, (captured.get(seen) ?? 0) + 1);
|
|
843
|
+
}
|
|
844
|
+
const failed = new Set();
|
|
845
|
+
for (const edit of inlineEdits) {
|
|
846
|
+
const expected = edit.inline;
|
|
847
|
+
const wanted = `${expected.key}\u0000${expected.text}`;
|
|
848
|
+
const remaining = captured.get(wanted) ?? 0;
|
|
849
|
+
if (remaining === 0) {
|
|
850
|
+
failed.add(edit);
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
captured.set(wanted, remaining - 1);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return failed;
|
|
857
|
+
}
|
|
858
|
+
renamedKey(anchor, oldKey, renamed) {
|
|
859
|
+
let current = anchor;
|
|
860
|
+
while (current !== undefined) {
|
|
861
|
+
const newPrefix = renamed.get(current);
|
|
862
|
+
const oldPrefix = this.cachedAnchorKey(current);
|
|
863
|
+
if (newPrefix !== undefined && oldPrefix !== undefined && (oldKey === oldPrefix || oldKey.startsWith(`${oldPrefix}/`))) {
|
|
864
|
+
return newPrefix + oldKey.slice(oldPrefix.length);
|
|
865
|
+
}
|
|
866
|
+
current = current.$container;
|
|
867
|
+
}
|
|
868
|
+
return undefined;
|
|
869
|
+
}
|
|
870
|
+
matchRenamedAnchors(sourceRoot, serialized, uri, located, sourceOwners, sourceCollisions) {
|
|
871
|
+
const matches = new Map();
|
|
872
|
+
const output = this.parse(serialized, uri);
|
|
873
|
+
if (output === undefined) {
|
|
874
|
+
return matches;
|
|
875
|
+
}
|
|
876
|
+
const outputNodes = [output.parseResult.value, ...AstUtils.streamAllContents(output.parseResult.value)];
|
|
877
|
+
const unmatchedBySlot = new Map();
|
|
878
|
+
for (const node of outputNodes) {
|
|
879
|
+
const key = this.cachedAnchorKey(node);
|
|
880
|
+
const parent = node.$container && this.cachedAnchorKey(node.$container);
|
|
881
|
+
if (key === undefined || !located.has(key) || sourceOwners.has(key) || parent === undefined) {
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
const slot = JSON.stringify([parent, node.$containerProperty, node.$containerIndex, node.$type]);
|
|
885
|
+
const candidates = unmatchedBySlot.get(slot) ?? [];
|
|
886
|
+
candidates.push(node);
|
|
887
|
+
unmatchedBySlot.set(slot, candidates);
|
|
888
|
+
}
|
|
889
|
+
const claimed = new Map();
|
|
890
|
+
const ambiguous = new Set();
|
|
891
|
+
for (const source of [sourceRoot, ...AstUtils.streamAllContents(sourceRoot)]) {
|
|
892
|
+
const key = this.cachedAnchorKey(source);
|
|
893
|
+
if (key === undefined || sourceCollisions.has(key) || located.has(key) || !source.$container) {
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
const parentKey = this.cachedAnchorKey(source.$container);
|
|
897
|
+
if (parentKey === undefined) {
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
const slot = JSON.stringify([parentKey, source.$containerProperty, source.$containerIndex, source.$type]);
|
|
901
|
+
const candidates = (unmatchedBySlot.get(slot) ?? []).filter(candidate => this.sameAsideFromName(source, candidate));
|
|
902
|
+
if (candidates.length === 1) {
|
|
903
|
+
const candidate = candidates[0];
|
|
904
|
+
if (ambiguous.has(candidate)) {
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
if (claimed.has(candidate)) {
|
|
908
|
+
matches.delete(claimed.get(candidate));
|
|
909
|
+
ambiguous.add(candidate);
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
matches.set(source, this.cachedAnchorKey(candidate));
|
|
913
|
+
}
|
|
914
|
+
claimed.set(candidate, source);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return matches;
|
|
918
|
+
}
|
|
919
|
+
sameAsideFromName(source, target) {
|
|
920
|
+
const nameProvider = this.services.references.NameProvider;
|
|
921
|
+
const before = nameProvider.getOwnName(source);
|
|
922
|
+
const after = nameProvider.getOwnName(target);
|
|
923
|
+
if (before === undefined || after === undefined || before === after) {
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
let renamed = false;
|
|
927
|
+
for (const key of this.comparableProperties(source.$type)) {
|
|
928
|
+
const oldValue = source[key];
|
|
929
|
+
const newValue = target[key];
|
|
930
|
+
if (oldValue === before && newValue === after) {
|
|
931
|
+
renamed = true;
|
|
932
|
+
}
|
|
933
|
+
else if (!this.sameValue(oldValue, newValue)) {
|
|
934
|
+
return false;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return renamed;
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* The properties `type` declares in the grammar — everything a rename match
|
|
941
|
+
* may compare, and nothing else.
|
|
942
|
+
*
|
|
943
|
+
* **Comparing own keys instead compares derived state, and matches nothing.**
|
|
944
|
+
* A built document carries whatever `ast.extensions` computed onto its nodes;
|
|
945
|
+
* the re-parsed serializer output is never built and carries none of it. Every
|
|
946
|
+
* candidate then differs on a property the grammar never mentioned, so no
|
|
947
|
+
* rename is ever matched — on the real write path only, because a document a
|
|
948
|
+
* test parses from a string has no computed state to disagree about.
|
|
949
|
+
*/
|
|
950
|
+
comparableProperties(type) {
|
|
951
|
+
return Object.keys(this.services.shared.AstReflection.getTypeMetaData(type).properties);
|
|
952
|
+
}
|
|
953
|
+
sameValue(left, right) {
|
|
954
|
+
if (left === right) {
|
|
955
|
+
return true;
|
|
956
|
+
}
|
|
957
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
958
|
+
return left.length === right.length && left.every((value, index) => this.sameValue(value, right[index]));
|
|
959
|
+
}
|
|
960
|
+
if (isAstNode(left) && isAstNode(right)) {
|
|
961
|
+
if (left.$type !== right.$type) {
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
return this.comparableProperties(left.$type).every(key => this.sameValue(left[key], right[key]));
|
|
965
|
+
}
|
|
966
|
+
if (left !== null && right !== null && typeof left === 'object' && typeof right === 'object') {
|
|
967
|
+
if ('$refText' in left && '$refText' in right) {
|
|
968
|
+
return left.$refText === right.$refText;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
/** The insertion this comment's placement implies, against its anchor's span. */
|
|
974
|
+
editFor(comment, span, serialized) {
|
|
975
|
+
const blanks = '\n'.repeat(comment.blankLinesAfter);
|
|
976
|
+
const blanksBefore = '\n'.repeat(comment.blankLinesBefore);
|
|
977
|
+
switch (comment.placement) {
|
|
978
|
+
case 'trailing':
|
|
979
|
+
return { at: this.endOfLineAt(serialized, span.end), text: ` ${comment.text}` };
|
|
980
|
+
// Appended to the container's FIRST line, which is its header. The
|
|
981
|
+
// comment may have sat further along that line in the source — mid
|
|
982
|
+
// header, or between braces the serializer has since collapsed to `{}`
|
|
983
|
+
// — and the exact column is not recoverable from a re-emitted
|
|
984
|
+
// document. The line is, and that is what keeps it on its own
|
|
985
|
+
// declaration instead of on a neighbour.
|
|
986
|
+
case 'trailingOnContainer':
|
|
987
|
+
return { at: this.endOfLineAt(serialized, span.offset), text: ` ${comment.text}` };
|
|
988
|
+
// Both of these open a NEW line after the anchor, which is only safe
|
|
989
|
+
// while nothing else shares the anchor's line. When the serializer put
|
|
990
|
+
// the anchor and its container's closing syntax together — an inline
|
|
991
|
+
// enum body, say — splitting there pushes that syntax onto the comment's
|
|
992
|
+
// line, and a line comment then ends the construct. Appending to the
|
|
993
|
+
// line instead keeps the comment on the same declaration and the
|
|
994
|
+
// document readable.
|
|
995
|
+
case 'afterNode': {
|
|
996
|
+
if (!this.restOfLineIsBlank(serialized, span.end)) {
|
|
997
|
+
return { at: this.endOfLineAt(serialized, span.end), text: ` ${comment.text}` };
|
|
998
|
+
}
|
|
999
|
+
const indent = this.indentAt(serialized, span.offset);
|
|
1000
|
+
return { at: span.end, text: `\n${blanksBefore}${indent}${this.reindent(comment, indent)}` };
|
|
1001
|
+
}
|
|
1002
|
+
case 'atContainerEnd':
|
|
1003
|
+
return this.restOfLineIsBlank(serialized, span.end)
|
|
1004
|
+
? { at: span.end, text: `\n${blanksBefore}${comment.text}` }
|
|
1005
|
+
: { at: this.endOfLineAt(serialized, span.end), text: ` ${comment.text}` };
|
|
1006
|
+
case 'atContainerStart':
|
|
1007
|
+
case 'leading':
|
|
1008
|
+
default: {
|
|
1009
|
+
// `leading` means "on the line above", and the anchor may sit
|
|
1010
|
+
// mid-line — a member of a body the serializer emitted inline.
|
|
1011
|
+
// **Opening a line there is sound only because the caller reads the
|
|
1012
|
+
// result back and requires this comment on this same anchor**,
|
|
1013
|
+
// dropping the edit when it is not. Unguarded, the split strands the
|
|
1014
|
+
// rest of the construct at column zero and lands somewhere different
|
|
1015
|
+
// again on the next write.
|
|
1016
|
+
if (!this.startOfLineIsBlank(serialized, span.offset)) {
|
|
1017
|
+
const indent = comment.sourceIndent ?? this.indentAt(serialized, span.offset);
|
|
1018
|
+
return { at: span.offset, text: `\n${indent}${this.reindent(comment, indent)}\n${blanks}${indent}` };
|
|
1019
|
+
}
|
|
1020
|
+
const indent = this.indentAt(serialized, span.offset);
|
|
1021
|
+
return { at: span.offset, text: `${this.reindent(comment, indent)}\n${blanks}${indent}` };
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* A multi-line comment's continuation lines, shifted by the same delta its
|
|
1027
|
+
* first line moves — so a block emitted at a new indentation stays square
|
|
1028
|
+
* instead of trailing its original column.
|
|
1029
|
+
*
|
|
1030
|
+
* **Only the leading whitespace run is touched, and an outdent removes at
|
|
1031
|
+
* most what is there** — shifting further would eat the comment's own text.
|
|
1032
|
+
*
|
|
1033
|
+
* Single-line comments and ones sharing a line with code are returned
|
|
1034
|
+
* unchanged.
|
|
1035
|
+
*/
|
|
1036
|
+
reindent(comment, targetIndent) {
|
|
1037
|
+
if (comment.sourceIndent === undefined || !comment.text.includes('\n')) {
|
|
1038
|
+
return comment.text;
|
|
1039
|
+
}
|
|
1040
|
+
// Indentation is compared as characters, which only means anything while
|
|
1041
|
+
// both sides use the SAME whitespace character. A tab-indented source
|
|
1042
|
+
// re-emitted with spaces has no meaningful delta, and shifting by one
|
|
1043
|
+
// anyway prepends spaces in front of tabs.
|
|
1044
|
+
const sourceUnit = /^\t*$/.test(comment.sourceIndent) ? '\t' : ' ';
|
|
1045
|
+
const targetUnit = /^\t*$/.test(targetIndent) ? '\t' : ' ';
|
|
1046
|
+
const delta = targetIndent.length - comment.sourceIndent.length;
|
|
1047
|
+
if (delta === 0 || sourceUnit !== targetUnit) {
|
|
1048
|
+
return comment.text;
|
|
1049
|
+
}
|
|
1050
|
+
const [first, ...rest] = comment.text.split('\n');
|
|
1051
|
+
const shifted = rest.map(line => {
|
|
1052
|
+
if (delta > 0) {
|
|
1053
|
+
return targetUnit.repeat(delta) + line;
|
|
1054
|
+
}
|
|
1055
|
+
const removable = /^[ \t]*/.exec(line)?.[0].length ?? 0;
|
|
1056
|
+
return line.slice(Math.min(-delta, removable));
|
|
1057
|
+
});
|
|
1058
|
+
return [first, ...shifted].join('\n');
|
|
1059
|
+
}
|
|
1060
|
+
/** Whether everything from `offset` to the end of its line is whitespace. */
|
|
1061
|
+
restOfLineIsBlank(text, offset) {
|
|
1062
|
+
return text.slice(offset, this.endOfLineAt(text, offset)).trim().length === 0;
|
|
1063
|
+
}
|
|
1064
|
+
/** Whether `offset` is preceded on its own line by whitespace alone. */
|
|
1065
|
+
startOfLineIsBlank(text, offset) {
|
|
1066
|
+
return text.slice(text.lastIndexOf('\n', offset - 1) + 1, offset).trim().length === 0;
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* End of the line `offset` sits on, as an insertion point for a trailing
|
|
1070
|
+
* comment. Stops before a CR so an insertion into CRLF text lands inside the
|
|
1071
|
+
* line rather than between its two terminator bytes.
|
|
1072
|
+
*/
|
|
1073
|
+
endOfLineAt(text, offset) {
|
|
1074
|
+
const newline = text.indexOf('\n', offset);
|
|
1075
|
+
const lineEnd = newline < 0 ? text.length : newline;
|
|
1076
|
+
return lineEnd > 0 && text[lineEnd - 1] === '\r' ? lineEnd - 1 : lineEnd;
|
|
1077
|
+
}
|
|
1078
|
+
/** Leading whitespace of the line `offset` sits on, so an insertion lines up with it. */
|
|
1079
|
+
indentAt(text, offset) {
|
|
1080
|
+
const lineStart = text.lastIndexOf('\n', offset - 1) + 1;
|
|
1081
|
+
return /^\s*/.exec(text.slice(lineStart, offset))?.[0] ?? '';
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Where every node landed in the serializer's output, keyed by
|
|
1085
|
+
* {@link anchorKey}.
|
|
1086
|
+
*
|
|
1087
|
+
* Re-parses `serialized` through the document factory, which does NOT
|
|
1088
|
+
* register the result, so this leaves `LangiumDocuments` alone. Output the
|
|
1089
|
+
* grammar cannot read, by reported error or by a throw from a URI that
|
|
1090
|
+
* routes to no services, yields `undefined`: the caller then writes the
|
|
1091
|
+
* serializer's text unchanged rather than splicing into text already wrong.
|
|
1092
|
+
*
|
|
1093
|
+
* **A key claimed by two DIFFERENT nodes is removed, not merged.** A repeated
|
|
1094
|
+
* identity is exactly what the integrity tier exists to repair, so collisions
|
|
1095
|
+
* reach this method routinely; merging their spans puts every one of their
|
|
1096
|
+
* comments on whichever came first. Removing the key drops those instead,
|
|
1097
|
+
* which is the only outcome here that keeps a comment off a declaration its
|
|
1098
|
+
* author did not write it on.
|
|
1099
|
+
*/
|
|
1100
|
+
locate(serialized, uri) {
|
|
1101
|
+
const document = this.parse(serialized, uri);
|
|
1102
|
+
if (document === undefined) {
|
|
1103
|
+
this.tracer.withUri(uri.toString()).warn('Serialized output did not re-parse; writing it without comments');
|
|
1104
|
+
return undefined;
|
|
1105
|
+
}
|
|
1106
|
+
const root = document.parseResult.value.$cstNode;
|
|
1107
|
+
if (!root) {
|
|
1108
|
+
return undefined;
|
|
1109
|
+
}
|
|
1110
|
+
const found = new Map();
|
|
1111
|
+
// One AST node owns many CST nodes — its composite plus every token under
|
|
1112
|
+
// it — so a repeated key is only a collision when a DIFFERENT node claims
|
|
1113
|
+
// it. Tracking the owner is what separates the two.
|
|
1114
|
+
const owners = new Map();
|
|
1115
|
+
const collided = new Set();
|
|
1116
|
+
// One AST node owns every CST node beneath it, so this asks for the same
|
|
1117
|
+
// key once per token; `cachedAnchorKey` is what keeps the pass from
|
|
1118
|
+
// costing nodes x depth.
|
|
1119
|
+
const visit = (node) => {
|
|
1120
|
+
const key = node.astNode !== undefined && !node.hidden ? this.cachedAnchorKey(node.astNode) : undefined;
|
|
1121
|
+
// Without the indentation tokens, whose DEDENT would end a block at
|
|
1122
|
+
// the start of the line after it.
|
|
1123
|
+
const span = key === undefined ? undefined : this.syntaxSpan(node);
|
|
1124
|
+
if (key !== undefined && span !== undefined) {
|
|
1125
|
+
const owner = owners.get(key);
|
|
1126
|
+
if (owner === undefined) {
|
|
1127
|
+
owners.set(key, node.astNode);
|
|
1128
|
+
found.set(key, { offset: span.offset, end: span.end, owner: node.astNode });
|
|
1129
|
+
}
|
|
1130
|
+
else if (owner === node.astNode) {
|
|
1131
|
+
// Widest span per node: its first CST node gives the start, a
|
|
1132
|
+
// later token contributing to the same node extends the end.
|
|
1133
|
+
const existing = found.get(key);
|
|
1134
|
+
found.set(key, {
|
|
1135
|
+
offset: Math.min(existing.offset, span.offset),
|
|
1136
|
+
end: Math.max(existing.end, span.end),
|
|
1137
|
+
owner: existing.owner
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
collided.add(key);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
if (isComposite(node)) {
|
|
1145
|
+
node.content.forEach(visit);
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
visit(root);
|
|
1149
|
+
for (const key of collided) {
|
|
1150
|
+
found.delete(key);
|
|
1151
|
+
}
|
|
1152
|
+
return found;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
/** Registers {@link CommentPreserver}; bound at `trivia.preservers.comments`. */
|
|
1156
|
+
export class CommentPreserverContribution {
|
|
1157
|
+
services;
|
|
1158
|
+
constructor(services) {
|
|
1159
|
+
this.services = services;
|
|
1160
|
+
}
|
|
1161
|
+
registerTriviaPreservers(registry) {
|
|
1162
|
+
registry.register(new CommentPreserver(this.services));
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
//# sourceMappingURL=comment-preserver.js.map
|