@hydranium/core 1.0.0-next.10
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/LICENSE +21 -0
- package/README.md +100 -0
- package/lib/documents/ast-document-manager.d.ts +243 -0
- package/lib/documents/ast-document-manager.d.ts.map +1 -0
- package/lib/documents/ast-document-manager.js +302 -0
- package/lib/documents/ast-document-manager.js.map +1 -0
- package/lib/documents/client-ids.d.ts +30 -0
- package/lib/documents/client-ids.d.ts.map +1 -0
- package/lib/documents/client-ids.js +30 -0
- package/lib/documents/client-ids.js.map +1 -0
- package/lib/documents/hydranium-text-documents.d.ts +490 -0
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -0
- package/lib/documents/hydranium-text-documents.js +893 -0
- package/lib/documents/hydranium-text-documents.js.map +1 -0
- package/lib/documents/index.d.ts +14 -0
- package/lib/documents/index.d.ts.map +1 -0
- package/lib/documents/index.js +14 -0
- package/lib/documents/index.js.map +1 -0
- package/lib/documents/language-client-text-shadow.d.ts +101 -0
- package/lib/documents/language-client-text-shadow.d.ts.map +1 -0
- package/lib/documents/language-client-text-shadow.js +156 -0
- package/lib/documents/language-client-text-shadow.js.map +1 -0
- package/lib/documents/self-save-registry.d.ts +55 -0
- package/lib/documents/self-save-registry.d.ts.map +1 -0
- package/lib/documents/self-save-registry.js +68 -0
- package/lib/documents/self-save-registry.js.map +1 -0
- package/lib/index.d.ts +38 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +39 -0
- package/lib/index.js.map +1 -0
- package/lib/langium/ast-extension/ast-extension-contribution.d.ts +33 -0
- package/lib/langium/ast-extension/ast-extension-contribution.d.ts.map +1 -0
- package/lib/langium/ast-extension/ast-extension-contribution.js +10 -0
- package/lib/langium/ast-extension/ast-extension-contribution.js.map +1 -0
- package/lib/langium/ast-extension/ast-extension-service.d.ts +212 -0
- package/lib/langium/ast-extension/ast-extension-service.d.ts.map +1 -0
- package/lib/langium/ast-extension/ast-extension-service.js +181 -0
- package/lib/langium/ast-extension/ast-extension-service.js.map +1 -0
- package/lib/langium/ast-extension/ast-node-builder.d.ts +90 -0
- package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -0
- package/lib/langium/ast-extension/ast-node-builder.js +61 -0
- package/lib/langium/ast-extension/ast-node-builder.js.map +1 -0
- package/lib/langium/ast-extension/index.d.ts +12 -0
- package/lib/langium/ast-extension/index.d.ts.map +1 -0
- package/lib/langium/ast-extension/index.js +12 -0
- package/lib/langium/ast-extension/index.js.map +1 -0
- package/lib/langium/bootstrap.d.ts +224 -0
- package/lib/langium/bootstrap.d.ts.map +1 -0
- package/lib/langium/bootstrap.js +512 -0
- package/lib/langium/bootstrap.js.map +1 -0
- package/lib/langium/build-phase-pass/build-phase-pass-service.d.ts +77 -0
- package/lib/langium/build-phase-pass/build-phase-pass-service.d.ts.map +1 -0
- package/lib/langium/build-phase-pass/build-phase-pass-service.js +102 -0
- package/lib/langium/build-phase-pass/build-phase-pass-service.js.map +1 -0
- package/lib/langium/build-phase-pass/build-phase-pass.d.ts +106 -0
- package/lib/langium/build-phase-pass/build-phase-pass.d.ts.map +1 -0
- package/lib/langium/build-phase-pass/build-phase-pass.js +43 -0
- package/lib/langium/build-phase-pass/build-phase-pass.js.map +1 -0
- package/lib/langium/build-phase-pass/index.d.ts +11 -0
- package/lib/langium/build-phase-pass/index.d.ts.map +1 -0
- package/lib/langium/build-phase-pass/index.js +11 -0
- package/lib/langium/build-phase-pass/index.js.map +1 -0
- package/lib/langium/config/index.d.ts +10 -0
- package/lib/langium/config/index.d.ts.map +1 -0
- package/lib/langium/config/index.js +10 -0
- package/lib/langium/config/index.js.map +1 -0
- package/lib/langium/config/settings.d.ts +173 -0
- package/lib/langium/config/settings.d.ts.map +1 -0
- package/lib/langium/config/settings.js +196 -0
- package/lib/langium/config/settings.js.map +1 -0
- package/lib/langium/diagnostics/hydranium-langium-profiler.d.ts +109 -0
- package/lib/langium/diagnostics/hydranium-langium-profiler.d.ts.map +1 -0
- package/lib/langium/diagnostics/hydranium-langium-profiler.js +153 -0
- package/lib/langium/diagnostics/hydranium-langium-profiler.js.map +1 -0
- package/lib/langium/diagnostics/index.d.ts +14 -0
- package/lib/langium/diagnostics/index.d.ts.map +1 -0
- package/lib/langium/diagnostics/index.js +19 -0
- package/lib/langium/diagnostics/index.js.map +1 -0
- package/lib/langium/diagnostics/log-file-token.d.ts +16 -0
- package/lib/langium/diagnostics/log-file-token.d.ts.map +1 -0
- package/lib/langium/diagnostics/log-file-token.js +25 -0
- package/lib/langium/diagnostics/log-file-token.js.map +1 -0
- package/lib/langium/diagnostics/log-preamble.d.ts +42 -0
- package/lib/langium/diagnostics/log-preamble.d.ts.map +1 -0
- package/lib/langium/diagnostics/log-preamble.js +57 -0
- package/lib/langium/diagnostics/log-preamble.js.map +1 -0
- package/lib/langium/diagnostics/logger.d.ts +104 -0
- package/lib/langium/diagnostics/logger.d.ts.map +1 -0
- package/lib/langium/diagnostics/logger.js +178 -0
- package/lib/langium/diagnostics/logger.js.map +1 -0
- package/lib/langium/diagnostics/lsp-logger.d.ts +76 -0
- package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -0
- package/lib/langium/diagnostics/lsp-logger.js +223 -0
- package/lib/langium/diagnostics/lsp-logger.js.map +1 -0
- package/lib/langium/diagnostics/server-tracer.d.ts +27 -0
- package/lib/langium/diagnostics/server-tracer.d.ts.map +1 -0
- package/lib/langium/diagnostics/server-tracer.js +66 -0
- package/lib/langium/diagnostics/server-tracer.js.map +1 -0
- package/lib/langium/document-builder/build-pipeline-integration.d.ts +88 -0
- package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -0
- package/lib/langium/document-builder/build-pipeline-integration.js +151 -0
- package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -0
- package/lib/langium/document-builder/document-builder.d.ts +405 -0
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -0
- package/lib/langium/document-builder/document-builder.js +729 -0
- package/lib/langium/document-builder/document-builder.js.map +1 -0
- package/lib/langium/document-builder/index.d.ts +12 -0
- package/lib/langium/document-builder/index.d.ts.map +1 -0
- package/lib/langium/document-builder/index.js +12 -0
- package/lib/langium/document-builder/index.js.map +1 -0
- package/lib/langium/document-builder/labeled-phase-listener.d.ts +21 -0
- package/lib/langium/document-builder/labeled-phase-listener.d.ts.map +1 -0
- package/lib/langium/document-builder/labeled-phase-listener.js +15 -0
- package/lib/langium/document-builder/labeled-phase-listener.js.map +1 -0
- package/lib/langium/documentation/comment-provider.d.ts +31 -0
- package/lib/langium/documentation/comment-provider.d.ts.map +1 -0
- package/lib/langium/documentation/comment-provider.js +36 -0
- package/lib/langium/documentation/comment-provider.js.map +1 -0
- package/lib/langium/documentation/index.d.ts +10 -0
- package/lib/langium/documentation/index.d.ts.map +1 -0
- package/lib/langium/documentation/index.js +10 -0
- package/lib/langium/documentation/index.js.map +1 -0
- package/lib/langium/integration-services.d.ts +200 -0
- package/lib/langium/integration-services.d.ts.map +1 -0
- package/lib/langium/integration-services.js +65 -0
- package/lib/langium/integration-services.js.map +1 -0
- package/lib/langium/integrity/index.d.ts +12 -0
- package/lib/langium/integrity/index.d.ts.map +1 -0
- package/lib/langium/integrity/index.js +12 -0
- package/lib/langium/integrity/index.js.map +1 -0
- package/lib/langium/integrity/integrity-contribution.d.ts +42 -0
- package/lib/langium/integrity/integrity-contribution.d.ts.map +1 -0
- package/lib/langium/integrity/integrity-contribution.js +10 -0
- package/lib/langium/integrity/integrity-contribution.js.map +1 -0
- package/lib/langium/integrity/integrity-rule.d.ts +82 -0
- package/lib/langium/integrity/integrity-rule.d.ts.map +1 -0
- package/lib/langium/integrity/integrity-rule.js +52 -0
- package/lib/langium/integrity/integrity-rule.js.map +1 -0
- package/lib/langium/integrity/integrity-service.d.ts +221 -0
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -0
- package/lib/langium/integrity/integrity-service.js +328 -0
- package/lib/langium/integrity/integrity-service.js.map +1 -0
- package/lib/langium/keys/default-element-key-provider.d.ts +19 -0
- package/lib/langium/keys/default-element-key-provider.d.ts.map +1 -0
- package/lib/langium/keys/default-element-key-provider.js +18 -0
- package/lib/langium/keys/default-element-key-provider.js.map +1 -0
- package/lib/langium/keys/element-key-provider.d.ts +106 -0
- package/lib/langium/keys/element-key-provider.d.ts.map +1 -0
- package/lib/langium/keys/element-key-provider.js +25 -0
- package/lib/langium/keys/element-key-provider.js.map +1 -0
- package/lib/langium/keys/index.d.ts +13 -0
- package/lib/langium/keys/index.d.ts.map +1 -0
- package/lib/langium/keys/index.js +13 -0
- package/lib/langium/keys/index.js.map +1 -0
- package/lib/langium/keys/name-based-key-provider.d.ts +99 -0
- package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -0
- package/lib/langium/keys/name-based-key-provider.js +134 -0
- package/lib/langium/keys/name-based-key-provider.js.map +1 -0
- package/lib/langium/keys/positional-key-provider.d.ts +41 -0
- package/lib/langium/keys/positional-key-provider.d.ts.map +1 -0
- package/lib/langium/keys/positional-key-provider.js +48 -0
- package/lib/langium/keys/positional-key-provider.js.map +1 -0
- package/lib/langium/labeling/index.d.ts +10 -0
- package/lib/langium/labeling/index.d.ts.map +1 -0
- package/lib/langium/labeling/index.js +10 -0
- package/lib/langium/labeling/index.js.map +1 -0
- package/lib/langium/labeling/label-provider.d.ts +79 -0
- package/lib/langium/labeling/label-provider.d.ts.map +1 -0
- package/lib/langium/labeling/label-provider.js +44 -0
- package/lib/langium/labeling/label-provider.js.map +1 -0
- package/lib/langium/language-module.d.ts +281 -0
- package/lib/langium/language-module.d.ts.map +1 -0
- package/lib/langium/language-module.js +103 -0
- package/lib/langium/language-module.js.map +1 -0
- package/lib/langium/language-types.d.ts +85 -0
- package/lib/langium/language-types.d.ts.map +1 -0
- package/lib/langium/language-types.js +173 -0
- package/lib/langium/language-types.js.map +1 -0
- package/lib/langium/model-service/index.d.ts +10 -0
- package/lib/langium/model-service/index.d.ts.map +1 -0
- package/lib/langium/model-service/index.js +10 -0
- package/lib/langium/model-service/index.js.map +1 -0
- package/lib/langium/model-service/model-service.d.ts +598 -0
- package/lib/langium/model-service/model-service.d.ts.map +1 -0
- package/lib/langium/model-service/model-service.js +889 -0
- package/lib/langium/model-service/model-service.js.map +1 -0
- package/lib/langium/module.d.ts +308 -0
- package/lib/langium/module.d.ts.map +1 -0
- package/lib/langium/module.js +165 -0
- package/lib/langium/module.js.map +1 -0
- package/lib/langium/naming/index.d.ts +11 -0
- package/lib/langium/naming/index.d.ts.map +1 -0
- package/lib/langium/naming/index.js +11 -0
- package/lib/langium/naming/index.js.map +1 -0
- package/lib/langium/naming/name-provider.d.ts +360 -0
- package/lib/langium/naming/name-provider.d.ts.map +1 -0
- package/lib/langium/naming/name-provider.js +240 -0
- package/lib/langium/naming/name-provider.js.map +1 -0
- package/lib/langium/naming/name-separator-validation.d.ts +47 -0
- package/lib/langium/naming/name-separator-validation.d.ts.map +1 -0
- package/lib/langium/naming/name-separator-validation.js +61 -0
- package/lib/langium/naming/name-separator-validation.js.map +1 -0
- package/lib/langium/project/abstract-project-manager.d.ts +242 -0
- package/lib/langium/project/abstract-project-manager.d.ts.map +1 -0
- package/lib/langium/project/abstract-project-manager.js +545 -0
- package/lib/langium/project/abstract-project-manager.js.map +1 -0
- package/lib/langium/project/index.d.ts +13 -0
- package/lib/langium/project/index.d.ts.map +1 -0
- package/lib/langium/project/index.js +13 -0
- package/lib/langium/project/index.js.map +1 -0
- package/lib/langium/project/project-change-event.d.ts +64 -0
- package/lib/langium/project/project-change-event.d.ts.map +1 -0
- package/lib/langium/project/project-change-event.js +10 -0
- package/lib/langium/project/project-change-event.js.map +1 -0
- package/lib/langium/project/project-manager.d.ts +196 -0
- package/lib/langium/project/project-manager.d.ts.map +1 -0
- package/lib/langium/project/project-manager.js +10 -0
- package/lib/langium/project/project-manager.js.map +1 -0
- package/lib/langium/project/single-project-manager.d.ts +69 -0
- package/lib/langium/project/single-project-manager.d.ts.map +1 -0
- package/lib/langium/project/single-project-manager.js +104 -0
- package/lib/langium/project/single-project-manager.js.map +1 -0
- package/lib/langium/residency/cst-residency-service.d.ts +214 -0
- package/lib/langium/residency/cst-residency-service.d.ts.map +1 -0
- package/lib/langium/residency/cst-residency-service.js +349 -0
- package/lib/langium/residency/cst-residency-service.js.map +1 -0
- package/lib/langium/residency/index.d.ts +10 -0
- package/lib/langium/residency/index.d.ts.map +1 -0
- package/lib/langium/residency/index.js +10 -0
- package/lib/langium/residency/index.js.map +1 -0
- package/lib/langium/scope/ast-node-description-provider.d.ts +122 -0
- package/lib/langium/scope/ast-node-description-provider.d.ts.map +1 -0
- package/lib/langium/scope/ast-node-description-provider.js +115 -0
- package/lib/langium/scope/ast-node-description-provider.js.map +1 -0
- package/lib/langium/scope/hydranium-scope-computation.d.ts +163 -0
- package/lib/langium/scope/hydranium-scope-computation.d.ts.map +1 -0
- package/lib/langium/scope/hydranium-scope-computation.js +220 -0
- package/lib/langium/scope/hydranium-scope-computation.js.map +1 -0
- package/lib/langium/scope/hydranium-scope-provider.d.ts +411 -0
- package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -0
- package/lib/langium/scope/hydranium-scope-provider.js +543 -0
- package/lib/langium/scope/hydranium-scope-provider.js.map +1 -0
- package/lib/langium/scope/index.d.ts +18 -0
- package/lib/langium/scope/index.d.ts.map +1 -0
- package/lib/langium/scope/index.js +18 -0
- package/lib/langium/scope/index.js.map +1 -0
- package/lib/langium/scope/reference-builder.d.ts +125 -0
- package/lib/langium/scope/reference-builder.d.ts.map +1 -0
- package/lib/langium/scope/reference-builder.js +115 -0
- package/lib/langium/scope/reference-builder.js.map +1 -0
- package/lib/langium/scope/reference-candidate-provider.d.ts +228 -0
- package/lib/langium/scope/reference-candidate-provider.d.ts.map +1 -0
- package/lib/langium/scope/reference-candidate-provider.js +185 -0
- package/lib/langium/scope/reference-candidate-provider.js.map +1 -0
- package/lib/langium/scope/scope-extension-contribution.d.ts +32 -0
- package/lib/langium/scope/scope-extension-contribution.d.ts.map +1 -0
- package/lib/langium/scope/scope-extension-contribution.js +10 -0
- package/lib/langium/scope/scope-extension-contribution.js.map +1 -0
- package/lib/langium/scope/scope-extension-service.d.ts +149 -0
- package/lib/langium/scope/scope-extension-service.d.ts.map +1 -0
- package/lib/langium/scope/scope-extension-service.js +94 -0
- package/lib/langium/scope/scope-extension-service.js.map +1 -0
- package/lib/langium/scope/scoped-ast-node-description.d.ts +160 -0
- package/lib/langium/scope/scoped-ast-node-description.d.ts.map +1 -0
- package/lib/langium/scope/scoped-ast-node-description.js +98 -0
- package/lib/langium/scope/scoped-ast-node-description.js.map +1 -0
- package/lib/langium/scope/tier-specificity.d.ts +47 -0
- package/lib/langium/scope/tier-specificity.d.ts.map +1 -0
- package/lib/langium/scope/tier-specificity.js +83 -0
- package/lib/langium/scope/tier-specificity.js.map +1 -0
- package/lib/langium/serialization/abstract-serializer.d.ts +316 -0
- package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -0
- package/lib/langium/serialization/abstract-serializer.js +351 -0
- package/lib/langium/serialization/abstract-serializer.js.map +1 -0
- package/lib/langium/serialization/index.d.ts +14 -0
- package/lib/langium/serialization/index.d.ts.map +1 -0
- package/lib/langium/serialization/index.js +14 -0
- package/lib/langium/serialization/index.js.map +1 -0
- package/lib/langium/serialization/json-serializer.d.ts +106 -0
- package/lib/langium/serialization/json-serializer.d.ts.map +1 -0
- package/lib/langium/serialization/json-serializer.js +198 -0
- package/lib/langium/serialization/json-serializer.js.map +1 -0
- package/lib/langium/serialization/serialization-util.d.ts +29 -0
- package/lib/langium/serialization/serialization-util.d.ts.map +1 -0
- package/lib/langium/serialization/serialization-util.js +37 -0
- package/lib/langium/serialization/serialization-util.js.map +1 -0
- package/lib/langium/serialization/serializer.d.ts +66 -0
- package/lib/langium/serialization/serializer.d.ts.map +1 -0
- package/lib/langium/serialization/serializer.js +10 -0
- package/lib/langium/serialization/serializer.js.map +1 -0
- package/lib/langium/serialization/yaml-serializer.d.ts +166 -0
- package/lib/langium/serialization/yaml-serializer.d.ts.map +1 -0
- package/lib/langium/serialization/yaml-serializer.js +228 -0
- package/lib/langium/serialization/yaml-serializer.js.map +1 -0
- package/lib/langium/service-registry.d.ts +220 -0
- package/lib/langium/service-registry.d.ts.map +1 -0
- package/lib/langium/service-registry.js +258 -0
- package/lib/langium/service-registry.js.map +1 -0
- package/lib/langium/shared-services.d.ts +98 -0
- package/lib/langium/shared-services.d.ts.map +1 -0
- package/lib/langium/shared-services.js +30 -0
- package/lib/langium/shared-services.js.map +1 -0
- package/lib/langium/transfer/index.d.ts +10 -0
- package/lib/langium/transfer/index.d.ts.map +1 -0
- package/lib/langium/transfer/index.js +10 -0
- package/lib/langium/transfer/index.js.map +1 -0
- package/lib/langium/transfer/transfer-encoder.d.ts +294 -0
- package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -0
- package/lib/langium/transfer/transfer-encoder.js +328 -0
- package/lib/langium/transfer/transfer-encoder.js.map +1 -0
- package/lib/langium/update-rewrite/index.d.ts +14 -0
- package/lib/langium/update-rewrite/index.d.ts.map +1 -0
- package/lib/langium/update-rewrite/index.js +14 -0
- package/lib/langium/update-rewrite/index.js.map +1 -0
- package/lib/langium/update-rewrite/normalize-empty-strings.d.ts +52 -0
- package/lib/langium/update-rewrite/normalize-empty-strings.d.ts.map +1 -0
- package/lib/langium/update-rewrite/normalize-empty-strings.js +83 -0
- package/lib/langium/update-rewrite/normalize-empty-strings.js.map +1 -0
- package/lib/langium/update-rewrite/semantic-root-rewrite.d.ts +37 -0
- package/lib/langium/update-rewrite/semantic-root-rewrite.d.ts.map +1 -0
- package/lib/langium/update-rewrite/semantic-root-rewrite.js +51 -0
- package/lib/langium/update-rewrite/semantic-root-rewrite.js.map +1 -0
- package/lib/langium/update-rewrite/update-rewrite-contribution.d.ts +42 -0
- package/lib/langium/update-rewrite/update-rewrite-contribution.d.ts.map +1 -0
- package/lib/langium/update-rewrite/update-rewrite-contribution.js +10 -0
- package/lib/langium/update-rewrite/update-rewrite-contribution.js.map +1 -0
- package/lib/langium/update-rewrite/update-rewrite-service.d.ts +89 -0
- package/lib/langium/update-rewrite/update-rewrite-service.d.ts.map +1 -0
- package/lib/langium/update-rewrite/update-rewrite-service.js +75 -0
- package/lib/langium/update-rewrite/update-rewrite-service.js.map +1 -0
- package/lib/langium/update-rewrite/update-rewrite.d.ts +59 -0
- package/lib/langium/update-rewrite/update-rewrite.d.ts.map +1 -0
- package/lib/langium/update-rewrite/update-rewrite.js +10 -0
- package/lib/langium/update-rewrite/update-rewrite.js.map +1 -0
- package/lib/langium/validation/document-validator.d.ts +106 -0
- package/lib/langium/validation/document-validator.d.ts.map +1 -0
- package/lib/langium/validation/document-validator.js +118 -0
- package/lib/langium/validation/document-validator.js.map +1 -0
- package/lib/langium/validation/index.d.ts +12 -0
- package/lib/langium/validation/index.d.ts.map +1 -0
- package/lib/langium/validation/index.js +12 -0
- package/lib/langium/validation/index.js.map +1 -0
- package/lib/langium/validation/validation-contribution-collector.d.ts +34 -0
- package/lib/langium/validation/validation-contribution-collector.d.ts.map +1 -0
- package/lib/langium/validation/validation-contribution-collector.js +51 -0
- package/lib/langium/validation/validation-contribution-collector.js.map +1 -0
- package/lib/langium/validation/validation-contribution.d.ts +36 -0
- package/lib/langium/validation/validation-contribution.d.ts.map +1 -0
- package/lib/langium/validation/validation-contribution.js +10 -0
- package/lib/langium/validation/validation-contribution.js.map +1 -0
- package/lib/langium/workspace/additional-document-contribution.d.ts +68 -0
- package/lib/langium/workspace/additional-document-contribution.d.ts.map +1 -0
- package/lib/langium/workspace/additional-document-contribution.js +23 -0
- package/lib/langium/workspace/additional-document-contribution.js.map +1 -0
- package/lib/langium/workspace/document-uri-policy.d.ts +151 -0
- package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -0
- package/lib/langium/workspace/document-uri-policy.js +121 -0
- package/lib/langium/workspace/document-uri-policy.js.map +1 -0
- package/lib/langium/workspace/file-system-provider.d.ts +30 -0
- package/lib/langium/workspace/file-system-provider.d.ts.map +1 -0
- package/lib/langium/workspace/file-system-provider.js +41 -0
- package/lib/langium/workspace/file-system-provider.js.map +1 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +45 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.js +64 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -0
- package/lib/langium/workspace/hydranium-workspace-lock.d.ts +31 -0
- package/lib/langium/workspace/hydranium-workspace-lock.d.ts.map +1 -0
- package/lib/langium/workspace/hydranium-workspace-lock.js +33 -0
- package/lib/langium/workspace/hydranium-workspace-lock.js.map +1 -0
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts +232 -0
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -0
- package/lib/langium/workspace/hydranium-workspace-manager.js +368 -0
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -0
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts +127 -0
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -0
- package/lib/langium/workspace/in-memory-file-system-provider.js +195 -0
- package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -0
- package/lib/langium/workspace/index-manager.d.ts +142 -0
- package/lib/langium/workspace/index-manager.d.ts.map +1 -0
- package/lib/langium/workspace/index-manager.js +245 -0
- package/lib/langium/workspace/index-manager.js.map +1 -0
- package/lib/langium/workspace/index.d.ts +23 -0
- package/lib/langium/workspace/index.d.ts.map +1 -0
- package/lib/langium/workspace/index.js +23 -0
- package/lib/langium/workspace/index.js.map +1 -0
- package/lib/langium/workspace/initialize-workspace.d.ts +63 -0
- package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -0
- package/lib/langium/workspace/initialize-workspace.js +88 -0
- package/lib/langium/workspace/initialize-workspace.js.map +1 -0
- package/lib/langium/workspace/langium-documents.d.ts +47 -0
- package/lib/langium/workspace/langium-documents.d.ts.map +1 -0
- package/lib/langium/workspace/langium-documents.js +85 -0
- package/lib/langium/workspace/langium-documents.js.map +1 -0
- package/lib/langium/workspace/persistent-file-system-provider.d.ts +207 -0
- package/lib/langium/workspace/persistent-file-system-provider.d.ts.map +1 -0
- package/lib/langium/workspace/persistent-file-system-provider.js +208 -0
- package/lib/langium/workspace/persistent-file-system-provider.js.map +1 -0
- package/lib/langium/workspace/synthetic.d.ts +38 -0
- package/lib/langium/workspace/synthetic.d.ts.map +1 -0
- package/lib/langium/workspace/synthetic.js +58 -0
- package/lib/langium/workspace/synthetic.js.map +1 -0
- package/lib/langium/workspace/virtual-document.d.ts +78 -0
- package/lib/langium/workspace/virtual-document.d.ts.map +1 -0
- package/lib/langium/workspace/virtual-document.js +89 -0
- package/lib/langium/workspace/virtual-document.js.map +1 -0
- package/lib/langium/workspace/write-lock-scope.d.ts +69 -0
- package/lib/langium/workspace/write-lock-scope.d.ts.map +1 -0
- package/lib/langium/workspace/write-lock-scope.js +54 -0
- package/lib/langium/workspace/write-lock-scope.js.map +1 -0
- package/lib/launcher/index.d.ts +10 -0
- package/lib/launcher/index.d.ts.map +1 -0
- package/lib/launcher/index.js +12 -0
- package/lib/launcher/index.js.map +1 -0
- package/lib/launcher/integrated-server.d.ts +28 -0
- package/lib/launcher/integrated-server.d.ts.map +1 -0
- package/lib/launcher/integrated-server.js +10 -0
- package/lib/launcher/integrated-server.js.map +1 -0
- package/lib/lsp/completion/hydranium-completion-provider.d.ts +89 -0
- package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -0
- package/lib/lsp/completion/hydranium-completion-provider.js +123 -0
- package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -0
- package/lib/lsp/hydranium-document-update-handler.d.ts +204 -0
- package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -0
- package/lib/lsp/hydranium-document-update-handler.js +288 -0
- package/lib/lsp/hydranium-document-update-handler.js.map +1 -0
- package/lib/lsp/index.d.ts +23 -0
- package/lib/lsp/index.d.ts.map +1 -0
- package/lib/lsp/index.js +23 -0
- package/lib/lsp/index.js.map +1 -0
- package/lib/lsp/instrument-connection.d.ts +36 -0
- package/lib/lsp/instrument-connection.d.ts.map +1 -0
- package/lib/lsp/instrument-connection.js +62 -0
- package/lib/lsp/instrument-connection.js.map +1 -0
- package/lib/lsp/language-module.d.ts +53 -0
- package/lib/lsp/language-module.d.ts.map +1 -0
- package/lib/lsp/language-module.js +28 -0
- package/lib/lsp/language-module.js.map +1 -0
- package/lib/lsp/semantic-token-provider.d.ts +131 -0
- package/lib/lsp/semantic-token-provider.d.ts.map +1 -0
- package/lib/lsp/semantic-token-provider.js +148 -0
- package/lib/lsp/semantic-token-provider.js.map +1 -0
- package/lib/lsp/shared-module.d.ts +76 -0
- package/lib/lsp/shared-module.d.ts.map +1 -0
- package/lib/lsp/shared-module.js +33 -0
- package/lib/lsp/shared-module.js.map +1 -0
- package/lib/lsp/start-language-server.d.ts +43 -0
- package/lib/lsp/start-language-server.d.ts.map +1 -0
- package/lib/lsp/start-language-server.js +60 -0
- package/lib/lsp/start-language-server.js.map +1 -0
- package/lib/node/ast-ground-truth.d.ts +57 -0
- package/lib/node/ast-ground-truth.d.ts.map +1 -0
- package/lib/node/ast-ground-truth.js +46 -0
- package/lib/node/ast-ground-truth.js.map +1 -0
- package/lib/node/event-loop-monitor.d.ts +40 -0
- package/lib/node/event-loop-monitor.d.ts.map +1 -0
- package/lib/node/event-loop-monitor.js +62 -0
- package/lib/node/event-loop-monitor.js.map +1 -0
- package/lib/node/index.d.ts +29 -0
- package/lib/node/index.d.ts.map +1 -0
- package/lib/node/index.js +50 -0
- package/lib/node/index.js.map +1 -0
- package/lib/node/latency-from-env.d.ts +32 -0
- package/lib/node/latency-from-env.d.ts.map +1 -0
- package/lib/node/latency-from-env.js +36 -0
- package/lib/node/latency-from-env.js.map +1 -0
- package/lib/node/lint-grammar.d.ts +87 -0
- package/lib/node/lint-grammar.d.ts.map +1 -0
- package/lib/node/lint-grammar.js +124 -0
- package/lib/node/lint-grammar.js.map +1 -0
- package/lib/node/log-file-sink.d.ts +22 -0
- package/lib/node/log-file-sink.d.ts.map +1 -0
- package/lib/node/log-file-sink.js +43 -0
- package/lib/node/log-file-sink.js.map +1 -0
- package/lib/node/log-preamble-node.d.ts +28 -0
- package/lib/node/log-preamble-node.d.ts.map +1 -0
- package/lib/node/log-preamble-node.js +55 -0
- package/lib/node/log-preamble-node.js.map +1 -0
- package/lib/node/measure-memory.d.ts +86 -0
- package/lib/node/measure-memory.d.ts.map +1 -0
- package/lib/node/measure-memory.js +128 -0
- package/lib/node/measure-memory.js.map +1 -0
- package/lib/node/memory-monitor.d.ts +42 -0
- package/lib/node/memory-monitor.d.ts.map +1 -0
- package/lib/node/memory-monitor.js +83 -0
- package/lib/node/memory-monitor.js.map +1 -0
- package/lib/node/node-file-system-provider.d.ts +112 -0
- package/lib/node/node-file-system-provider.d.ts.map +1 -0
- package/lib/node/node-file-system-provider.js +230 -0
- package/lib/node/node-file-system-provider.js.map +1 -0
- package/lib/node/process-memory.d.ts +66 -0
- package/lib/node/process-memory.d.ts.map +1 -0
- package/lib/node/process-memory.js +252 -0
- package/lib/node/process-memory.js.map +1 -0
- package/lib/node/profile-capture.d.ts +118 -0
- package/lib/node/profile-capture.d.ts.map +1 -0
- package/lib/node/profile-capture.js +315 -0
- package/lib/node/profile-capture.js.map +1 -0
- package/lib/node/profile-digest.d.ts +49 -0
- package/lib/node/profile-digest.d.ts.map +1 -0
- package/lib/node/profile-digest.js +74 -0
- package/lib/node/profile-digest.js.map +1 -0
- package/lib/node/profiling-run.d.ts +80 -0
- package/lib/node/profiling-run.d.ts.map +1 -0
- package/lib/node/profiling-run.js +183 -0
- package/lib/node/profiling-run.js.map +1 -0
- package/lib/node/reflect-grammar.d.ts +109 -0
- package/lib/node/reflect-grammar.d.ts.map +1 -0
- package/lib/node/reflect-grammar.js +96 -0
- package/lib/node/reflect-grammar.js.map +1 -0
- package/lib/node/server-diagnostics.d.ts +43 -0
- package/lib/node/server-diagnostics.d.ts.map +1 -0
- package/lib/node/server-diagnostics.js +46 -0
- package/lib/node/server-diagnostics.js.map +1 -0
- package/lib/node/server-state-snapshot.d.ts +48 -0
- package/lib/node/server-state-snapshot.d.ts.map +1 -0
- package/lib/node/server-state-snapshot.js +139 -0
- package/lib/node/server-state-snapshot.js.map +1 -0
- package/lib/node/socket-launcher.d.ts +106 -0
- package/lib/node/socket-launcher.d.ts.map +1 -0
- package/lib/node/socket-launcher.js +138 -0
- package/lib/node/socket-launcher.js.map +1 -0
- package/lib/node/stdio-launcher.d.ts +88 -0
- package/lib/node/stdio-launcher.d.ts.map +1 -0
- package/lib/node/stdio-launcher.js +83 -0
- package/lib/node/stdio-launcher.js.map +1 -0
- package/lib/node/validate-workspace.d.ts +80 -0
- package/lib/node/validate-workspace.d.ts.map +1 -0
- package/lib/node/validate-workspace.js +72 -0
- package/lib/node/validate-workspace.js.map +1 -0
- package/lib/node/write-lock-scope-node.d.ts +27 -0
- package/lib/node/write-lock-scope-node.d.ts.map +1 -0
- package/lib/node/write-lock-scope-node.js +38 -0
- package/lib/node/write-lock-scope-node.js.map +1 -0
- package/lib/testing/ast-snapshot.d.ts +38 -0
- package/lib/testing/ast-snapshot.d.ts.map +1 -0
- package/lib/testing/ast-snapshot.js +78 -0
- package/lib/testing/ast-snapshot.js.map +1 -0
- package/lib/testing/document-uri-policy-conformance.d.ts +65 -0
- package/lib/testing/document-uri-policy-conformance.d.ts.map +1 -0
- package/lib/testing/document-uri-policy-conformance.js +69 -0
- package/lib/testing/document-uri-policy-conformance.js.map +1 -0
- package/lib/testing/fake-description.d.ts +35 -0
- package/lib/testing/fake-description.d.ts.map +1 -0
- package/lib/testing/fake-description.js +38 -0
- package/lib/testing/fake-description.js.map +1 -0
- package/lib/testing/fake-document.d.ts +71 -0
- package/lib/testing/fake-document.d.ts.map +1 -0
- package/lib/testing/fake-document.js +85 -0
- package/lib/testing/fake-document.js.map +1 -0
- package/lib/testing/fake-reflection.d.ts +29 -0
- package/lib/testing/fake-reflection.d.ts.map +1 -0
- package/lib/testing/fake-reflection.js +30 -0
- package/lib/testing/fake-reflection.js.map +1 -0
- package/lib/testing/index.d.ts +44 -0
- package/lib/testing/index.d.ts.map +1 -0
- package/lib/testing/index.js +44 -0
- package/lib/testing/index.js.map +1 -0
- package/lib/testing/langium-test-helpers.d.ts +26 -0
- package/lib/testing/langium-test-helpers.d.ts.map +1 -0
- package/lib/testing/langium-test-helpers.js +26 -0
- package/lib/testing/langium-test-helpers.js.map +1 -0
- package/lib/testing/make-noop-language-services.d.ts +39 -0
- package/lib/testing/make-noop-language-services.d.ts.map +1 -0
- package/lib/testing/make-noop-language-services.js +30 -0
- package/lib/testing/make-noop-language-services.js.map +1 -0
- package/lib/testing/make-noop-shared-services.d.ts +61 -0
- package/lib/testing/make-noop-shared-services.d.ts.map +1 -0
- package/lib/testing/make-noop-shared-services.js +49 -0
- package/lib/testing/make-noop-shared-services.js.map +1 -0
- package/lib/testing/make-test-services.d.ts +217 -0
- package/lib/testing/make-test-services.d.ts.map +1 -0
- package/lib/testing/make-test-services.js +128 -0
- package/lib/testing/make-test-services.js.map +1 -0
- package/lib/testing/make-test-tracer.d.ts +67 -0
- package/lib/testing/make-test-tracer.d.ts.map +1 -0
- package/lib/testing/make-test-tracer.js +68 -0
- package/lib/testing/make-test-tracer.js.map +1 -0
- package/lib/testing/node/generated-workspace.d.ts +95 -0
- package/lib/testing/node/generated-workspace.d.ts.map +1 -0
- package/lib/testing/node/generated-workspace.js +134 -0
- package/lib/testing/node/generated-workspace.js.map +1 -0
- package/lib/testing/node/golden-corpus.d.ts +50 -0
- package/lib/testing/node/golden-corpus.d.ts.map +1 -0
- package/lib/testing/node/golden-corpus.js +80 -0
- package/lib/testing/node/golden-corpus.js.map +1 -0
- package/lib/testing/node/index.d.ts +28 -0
- package/lib/testing/node/index.d.ts.map +1 -0
- package/lib/testing/node/index.js +28 -0
- package/lib/testing/node/index.js.map +1 -0
- package/lib/testing/node/lsp-harness.d.ts +139 -0
- package/lib/testing/node/lsp-harness.d.ts.map +1 -0
- package/lib/testing/node/lsp-harness.js +119 -0
- package/lib/testing/node/lsp-harness.js.map +1 -0
- package/lib/testing/node/lsp-server-connection.d.ts +232 -0
- package/lib/testing/node/lsp-server-connection.d.ts.map +1 -0
- package/lib/testing/node/lsp-server-connection.js +304 -0
- package/lib/testing/node/lsp-server-connection.js.map +1 -0
- package/lib/testing/node/scratch-workspace.d.ts +60 -0
- package/lib/testing/node/scratch-workspace.d.ts.map +1 -0
- package/lib/testing/node/scratch-workspace.js +87 -0
- package/lib/testing/node/scratch-workspace.js.map +1 -0
- package/lib/testing/node/spawned-server.d.ts +170 -0
- package/lib/testing/node/spawned-server.d.ts.map +1 -0
- package/lib/testing/node/spawned-server.js +265 -0
- package/lib/testing/node/spawned-server.js.map +1 -0
- package/lib/testing/parse-semantic-root.d.ts +83 -0
- package/lib/testing/parse-semantic-root.d.ts.map +1 -0
- package/lib/testing/parse-semantic-root.js +119 -0
- package/lib/testing/parse-semantic-root.js.map +1 -0
- package/lib/testing/playwright/browser-capture-bridge.d.ts +80 -0
- package/lib/testing/playwright/browser-capture-bridge.d.ts.map +1 -0
- package/lib/testing/playwright/browser-capture-bridge.js +161 -0
- package/lib/testing/playwright/browser-capture-bridge.js.map +1 -0
- package/lib/testing/playwright/e2e-profiling.d.ts +132 -0
- package/lib/testing/playwright/e2e-profiling.d.ts.map +1 -0
- package/lib/testing/playwright/e2e-profiling.js +270 -0
- package/lib/testing/playwright/e2e-profiling.js.map +1 -0
- package/lib/testing/playwright/index.d.ts +13 -0
- package/lib/testing/playwright/index.d.ts.map +1 -0
- package/lib/testing/playwright/index.js +16 -0
- package/lib/testing/playwright/index.js.map +1 -0
- package/lib/testing/playwright/server-log-capture.d.ts +230 -0
- package/lib/testing/playwright/server-log-capture.d.ts.map +1 -0
- package/lib/testing/playwright/server-log-capture.js +336 -0
- package/lib/testing/playwright/server-log-capture.js.map +1 -0
- package/lib/testing/playwright/server-log-fixture.d.ts +36 -0
- package/lib/testing/playwright/server-log-fixture.d.ts.map +1 -0
- package/lib/testing/playwright/server-log-fixture.js +38 -0
- package/lib/testing/playwright/server-log-fixture.js.map +1 -0
- package/lib/testing/playwright/server-log-rename-reporter.d.ts +22 -0
- package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -0
- package/lib/testing/playwright/server-log-rename-reporter.js +26 -0
- package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -0
- package/lib/testing/run-update-pipeline.d.ts +99 -0
- package/lib/testing/run-update-pipeline.d.ts.map +1 -0
- package/lib/testing/run-update-pipeline.js +24 -0
- package/lib/testing/run-update-pipeline.js.map +1 -0
- package/lib/testing/stub-ast-document-manager.d.ts +82 -0
- package/lib/testing/stub-ast-document-manager.d.ts.map +1 -0
- package/lib/testing/stub-ast-document-manager.js +125 -0
- package/lib/testing/stub-ast-document-manager.js.map +1 -0
- package/lib/testing/stub-document-builder.d.ts +99 -0
- package/lib/testing/stub-document-builder.d.ts.map +1 -0
- package/lib/testing/stub-document-builder.js +142 -0
- package/lib/testing/stub-document-builder.js.map +1 -0
- package/lib/testing/stub-hydranium-text-documents.d.ts +125 -0
- package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -0
- package/lib/testing/stub-hydranium-text-documents.js +164 -0
- package/lib/testing/stub-hydranium-text-documents.js.map +1 -0
- package/lib/testing/stub-index-manager.d.ts +40 -0
- package/lib/testing/stub-index-manager.d.ts.map +1 -0
- package/lib/testing/stub-index-manager.js +38 -0
- package/lib/testing/stub-index-manager.js.map +1 -0
- package/lib/testing/stub-langium-documents.d.ts +53 -0
- package/lib/testing/stub-langium-documents.d.ts.map +1 -0
- package/lib/testing/stub-langium-documents.js +62 -0
- package/lib/testing/stub-langium-documents.js.map +1 -0
- package/lib/testing/stub-model-service.d.ts +31 -0
- package/lib/testing/stub-model-service.d.ts.map +1 -0
- package/lib/testing/stub-model-service.js +45 -0
- package/lib/testing/stub-model-service.js.map +1 -0
- package/lib/testing/stub-project-manager.d.ts +43 -0
- package/lib/testing/stub-project-manager.d.ts.map +1 -0
- package/lib/testing/stub-project-manager.js +125 -0
- package/lib/testing/stub-project-manager.js.map +1 -0
- package/lib/testing/stub-self-save-registry.d.ts +31 -0
- package/lib/testing/stub-self-save-registry.d.ts.map +1 -0
- package/lib/testing/stub-self-save-registry.js +23 -0
- package/lib/testing/stub-self-save-registry.js.map +1 -0
- package/lib/testing/stub-service-registry.d.ts +95 -0
- package/lib/testing/stub-service-registry.d.ts.map +1 -0
- package/lib/testing/stub-service-registry.js +108 -0
- package/lib/testing/stub-service-registry.js.map +1 -0
- package/lib/testing/stub-writable-file-system.d.ts +42 -0
- package/lib/testing/stub-writable-file-system.d.ts.map +1 -0
- package/lib/testing/stub-writable-file-system.js +26 -0
- package/lib/testing/stub-writable-file-system.js.map +1 -0
- package/lib/util/connection-liveness.d.ts +25 -0
- package/lib/util/connection-liveness.d.ts.map +1 -0
- package/lib/util/connection-liveness.js +46 -0
- package/lib/util/connection-liveness.js.map +1 -0
- package/lib/util/environment.d.ts +53 -0
- package/lib/util/environment.d.ts.map +1 -0
- package/lib/util/environment.js +78 -0
- package/lib/util/environment.js.map +1 -0
- package/lib/util/index.d.ts +13 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +18 -0
- package/lib/util/index.js.map +1 -0
- package/lib/util/registry.d.ts +88 -0
- package/lib/util/registry.d.ts.map +1 -0
- package/lib/util/registry.js +105 -0
- package/lib/util/registry.js.map +1 -0
- package/package.json +134 -0
- package/src/documents/ast-document-manager.ts +455 -0
- package/src/documents/client-ids.ts +33 -0
- package/src/documents/hydranium-text-documents.ts +1142 -0
- package/src/documents/index.ts +14 -0
- package/src/documents/language-client-text-shadow.ts +175 -0
- package/src/documents/self-save-registry.ts +81 -0
- package/src/index.ts +39 -0
- package/src/langium/ast-extension/ast-extension-contribution.ts +35 -0
- package/src/langium/ast-extension/ast-extension-service.ts +311 -0
- package/src/langium/ast-extension/ast-node-builder.ts +125 -0
- package/src/langium/ast-extension/index.ts +12 -0
- package/src/langium/bootstrap.ts +600 -0
- package/src/langium/build-phase-pass/build-phase-pass-service.ts +141 -0
- package/src/langium/build-phase-pass/build-phase-pass.ts +123 -0
- package/src/langium/build-phase-pass/index.ts +11 -0
- package/src/langium/config/index.ts +10 -0
- package/src/langium/config/settings.ts +353 -0
- package/src/langium/diagnostics/hydranium-langium-profiler.ts +187 -0
- package/src/langium/diagnostics/index.ts +19 -0
- package/src/langium/diagnostics/log-file-token.ts +26 -0
- package/src/langium/diagnostics/log-preamble.ts +87 -0
- package/src/langium/diagnostics/logger.ts +246 -0
- package/src/langium/diagnostics/lsp-logger.ts +277 -0
- package/src/langium/diagnostics/server-tracer.ts +73 -0
- package/src/langium/document-builder/build-pipeline-integration.ts +193 -0
- package/src/langium/document-builder/document-builder.ts +839 -0
- package/src/langium/document-builder/index.ts +12 -0
- package/src/langium/document-builder/labeled-phase-listener.ts +26 -0
- package/src/langium/documentation/comment-provider.ts +37 -0
- package/src/langium/documentation/index.ts +10 -0
- package/src/langium/integration-services.ts +281 -0
- package/src/langium/integrity/index.ts +12 -0
- package/src/langium/integrity/integrity-contribution.ts +44 -0
- package/src/langium/integrity/integrity-rule.ts +100 -0
- package/src/langium/integrity/integrity-service.ts +423 -0
- package/src/langium/keys/default-element-key-provider.ts +20 -0
- package/src/langium/keys/element-key-provider.ts +114 -0
- package/src/langium/keys/index.ts +13 -0
- package/src/langium/keys/name-based-key-provider.ts +146 -0
- package/src/langium/keys/positional-key-provider.ts +57 -0
- package/src/langium/labeling/index.ts +10 -0
- package/src/langium/labeling/label-provider.ts +110 -0
- package/src/langium/language-module.ts +367 -0
- package/src/langium/language-types.ts +214 -0
- package/src/langium/model-service/index.ts +10 -0
- package/src/langium/model-service/model-service.ts +1051 -0
- package/src/langium/module.ts +446 -0
- package/src/langium/naming/index.ts +11 -0
- package/src/langium/naming/name-provider.ts +533 -0
- package/src/langium/naming/name-separator-validation.ts +73 -0
- package/src/langium/project/abstract-project-manager.ts +630 -0
- package/src/langium/project/index.ts +13 -0
- package/src/langium/project/project-change-event.ts +66 -0
- package/src/langium/project/project-manager.ts +221 -0
- package/src/langium/project/single-project-manager.ts +126 -0
- package/src/langium/residency/cst-residency-service.ts +412 -0
- package/src/langium/residency/index.ts +10 -0
- package/src/langium/scope/ast-node-description-provider.ts +141 -0
- package/src/langium/scope/hydranium-scope-computation.ts +246 -0
- package/src/langium/scope/hydranium-scope-provider.ts +659 -0
- package/src/langium/scope/index.ts +18 -0
- package/src/langium/scope/reference-builder.ts +194 -0
- package/src/langium/scope/reference-candidate-provider.ts +314 -0
- package/src/langium/scope/scope-extension-contribution.ts +34 -0
- package/src/langium/scope/scope-extension-service.ts +207 -0
- package/src/langium/scope/scoped-ast-node-description.ts +174 -0
- package/src/langium/scope/tier-specificity.ts +89 -0
- package/src/langium/serialization/abstract-serializer.ts +474 -0
- package/src/langium/serialization/index.ts +14 -0
- package/src/langium/serialization/json-serializer.ts +264 -0
- package/src/langium/serialization/serialization-util.ts +40 -0
- package/src/langium/serialization/serializer.ts +68 -0
- package/src/langium/serialization/yaml-serializer.ts +348 -0
- package/src/langium/service-registry.ts +323 -0
- package/src/langium/shared-services.ts +107 -0
- package/src/langium/transfer/index.ts +10 -0
- package/src/langium/transfer/transfer-encoder.ts +434 -0
- package/src/langium/update-rewrite/index.ts +14 -0
- package/src/langium/update-rewrite/normalize-empty-strings.ts +86 -0
- package/src/langium/update-rewrite/semantic-root-rewrite.ts +57 -0
- package/src/langium/update-rewrite/update-rewrite-contribution.ts +44 -0
- package/src/langium/update-rewrite/update-rewrite-service.ts +129 -0
- package/src/langium/update-rewrite/update-rewrite.ts +60 -0
- package/src/langium/validation/document-validator.ts +188 -0
- package/src/langium/validation/index.ts +12 -0
- package/src/langium/validation/validation-contribution-collector.ts +61 -0
- package/src/langium/validation/validation-contribution.ts +38 -0
- package/src/langium/workspace/additional-document-contribution.ts +80 -0
- package/src/langium/workspace/document-uri-policy.ts +187 -0
- package/src/langium/workspace/file-system-provider.ts +52 -0
- package/src/langium/workspace/hydranium-langium-document-factory.ts +78 -0
- package/src/langium/workspace/hydranium-workspace-lock.ts +35 -0
- package/src/langium/workspace/hydranium-workspace-manager.ts +412 -0
- package/src/langium/workspace/in-memory-file-system-provider.ts +241 -0
- package/src/langium/workspace/index-manager.ts +266 -0
- package/src/langium/workspace/index.ts +23 -0
- package/src/langium/workspace/initialize-workspace.ts +102 -0
- package/src/langium/workspace/langium-documents.ts +94 -0
- package/src/langium/workspace/persistent-file-system-provider.ts +287 -0
- package/src/langium/workspace/synthetic.ts +63 -0
- package/src/langium/workspace/virtual-document.ts +94 -0
- package/src/langium/workspace/write-lock-scope.ts +88 -0
- package/src/launcher/index.ts +12 -0
- package/src/launcher/integrated-server.ts +28 -0
- package/src/lsp/completion/hydranium-completion-provider.ts +132 -0
- package/src/lsp/hydranium-document-update-handler.ts +345 -0
- package/src/lsp/index.ts +24 -0
- package/src/lsp/instrument-connection.ts +66 -0
- package/src/lsp/language-module.ts +64 -0
- package/src/lsp/semantic-token-provider.ts +215 -0
- package/src/lsp/shared-module.ts +88 -0
- package/src/lsp/start-language-server.ts +68 -0
- package/src/node/ast-ground-truth.ts +76 -0
- package/src/node/event-loop-monitor.ts +89 -0
- package/src/node/index.ts +53 -0
- package/src/node/latency-from-env.ts +39 -0
- package/src/node/lint-grammar.ts +189 -0
- package/src/node/log-file-sink.ts +43 -0
- package/src/node/log-preamble-node.ts +63 -0
- package/src/node/measure-memory.ts +196 -0
- package/src/node/memory-monitor.ts +103 -0
- package/src/node/node-file-system-provider.ts +252 -0
- package/src/node/process-memory.ts +299 -0
- package/src/node/profile-capture.ts +377 -0
- package/src/node/profile-digest.ts +112 -0
- package/src/node/profiling-run.ts +236 -0
- package/src/node/reflect-grammar.ts +182 -0
- package/src/node/server-diagnostics.ts +63 -0
- package/src/node/server-state-snapshot.ts +192 -0
- package/src/node/socket-launcher.ts +210 -0
- package/src/node/stdio-launcher.ts +150 -0
- package/src/node/validate-workspace.ts +133 -0
- package/src/node/write-lock-scope-node.ts +41 -0
- package/src/testing/ast-snapshot.ts +88 -0
- package/src/testing/document-uri-policy-conformance.ts +104 -0
- package/src/testing/fake-description.ts +52 -0
- package/src/testing/fake-document.ts +120 -0
- package/src/testing/fake-reflection.ts +39 -0
- package/src/testing/index.ts +45 -0
- package/src/testing/langium-test-helpers.ts +26 -0
- package/src/testing/make-noop-language-services.ts +47 -0
- package/src/testing/make-noop-shared-services.ts +78 -0
- package/src/testing/make-test-services.ts +356 -0
- package/src/testing/make-test-tracer.ts +92 -0
- package/src/testing/node/generated-workspace.ts +222 -0
- package/src/testing/node/golden-corpus.ts +114 -0
- package/src/testing/node/index.ts +29 -0
- package/src/testing/node/lsp-harness.ts +230 -0
- package/src/testing/node/lsp-server-connection.ts +575 -0
- package/src/testing/node/scratch-workspace.ts +137 -0
- package/src/testing/node/spawned-server.ts +447 -0
- package/src/testing/parse-semantic-root.ts +150 -0
- package/src/testing/playwright/browser-capture-bridge.ts +258 -0
- package/src/testing/playwright/e2e-profiling.ts +362 -0
- package/src/testing/playwright/index.ts +16 -0
- package/src/testing/playwright/server-log-capture.ts +433 -0
- package/src/testing/playwright/server-log-fixture.ts +57 -0
- package/src/testing/playwright/server-log-rename-reporter.ts +29 -0
- package/src/testing/run-update-pipeline.ts +113 -0
- package/src/testing/stub-ast-document-manager.ts +224 -0
- package/src/testing/stub-document-builder.ts +242 -0
- package/src/testing/stub-hydranium-text-documents.ts +270 -0
- package/src/testing/stub-index-manager.ts +79 -0
- package/src/testing/stub-langium-documents.ts +104 -0
- package/src/testing/stub-model-service.ts +72 -0
- package/src/testing/stub-project-manager.ts +161 -0
- package/src/testing/stub-self-save-registry.ts +43 -0
- package/src/testing/stub-service-registry.ts +192 -0
- package/src/testing/stub-writable-file-system.ts +58 -0
- package/src/util/connection-liveness.ts +47 -0
- package/src/util/environment.ts +88 -0
- package/src/util/index.ts +18 -0
- package/src/util/registry.ts +129 -0
|
@@ -0,0 +1,1142 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
* Copyright (c) Microsoft Corporation and EclipseSource. All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This program and the accompanying materials are made available under the
|
|
6
|
+
* terms of the MIT License which is available in the project root.
|
|
7
|
+
*
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
********************************************************************************/
|
|
10
|
+
|
|
11
|
+
// Adapted from Langium's `NormalizedTextDocuments`, itself an adaptation of
|
|
12
|
+
// Microsoft's `TextDocuments` — hence the Microsoft copyright above. The
|
|
13
|
+
// `listen()` override below follows that handler-registration sequence:
|
|
14
|
+
// https://github.com/microsoft/vscode-languageserver-node/blob/8f5fa710d3a9f60ff5e7583a9e61b19f86e39da3/server/src/common/textDocuments.ts
|
|
15
|
+
|
|
16
|
+
// Deliberate exception to `@hydranium/core`'s no-runtime-langium/lsp rule:
|
|
17
|
+
// this class IS the shared LSP-style document-sync primitive that every
|
|
18
|
+
// protocol head (lsp-server, data-server, glsp-server) coordinates through.
|
|
19
|
+
// Moving it into lsp-server would force data-server / glsp-server to depend
|
|
20
|
+
// on lsp-server, contradicting the peer architecture.
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
|
22
|
+
import { NormalizedTextDocuments } from '@hydranium/langium/lsp';
|
|
23
|
+
import { UriUtils } from '@hydranium/langium';
|
|
24
|
+
import { type ServerSharedServices } from '../langium/module.js';
|
|
25
|
+
import {
|
|
26
|
+
type ApplyWorkspaceEditResult,
|
|
27
|
+
type CancellationToken,
|
|
28
|
+
type Connection,
|
|
29
|
+
type DidChangeTextDocumentParams,
|
|
30
|
+
type DidCloseTextDocumentParams,
|
|
31
|
+
type DidOpenTextDocumentParams,
|
|
32
|
+
type DidSaveTextDocumentParams,
|
|
33
|
+
type Disposable,
|
|
34
|
+
type Emitter,
|
|
35
|
+
type Event,
|
|
36
|
+
type HandlerResult,
|
|
37
|
+
OptionalVersionedTextDocumentIdentifier,
|
|
38
|
+
type RequestHandler,
|
|
39
|
+
type TextDocumentChangeEvent,
|
|
40
|
+
TextDocumentEdit,
|
|
41
|
+
TextDocumentSyncKind,
|
|
42
|
+
type TextDocumentWillSaveEvent,
|
|
43
|
+
type TextDocumentsConfiguration,
|
|
44
|
+
type TextEdit,
|
|
45
|
+
type WillSaveTextDocumentParams
|
|
46
|
+
} from 'vscode-languageserver';
|
|
47
|
+
import { type DocumentUri, TextDocument, type TextDocumentContentChangeEvent } from 'vscode-languageserver-textdocument';
|
|
48
|
+
import { type CanonicalUri, type LanguageClientUri, asLanguageClientUri, DisposableCollection, type Tracer } from '@hydranium/protocol';
|
|
49
|
+
import { type LogNameOptions } from '../langium/diagnostics/logger.js';
|
|
50
|
+
import { LANGUAGE_CLIENT_ID } from './client-ids.js';
|
|
51
|
+
import { isFullReplace, LanguageClientTextShadow } from './language-client-text-shadow.js';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The LSP spec's "version is intentionally unknown" for an
|
|
55
|
+
* {@link OptionalVersionedTextDocumentIdentifier} is `null`, but
|
|
56
|
+
* `OptionalVersionedTextDocumentIdentifier.create` requires `number` in its TS
|
|
57
|
+
* signature — so the null routes through this one typed bridge rather than a
|
|
58
|
+
* cast at each call site.
|
|
59
|
+
*/
|
|
60
|
+
const UNKNOWN_CLIENT_VERSION = null as unknown as number;
|
|
61
|
+
|
|
62
|
+
export interface ClientTextDocumentChangeEvent<T> extends TextDocumentChangeEvent<T> {
|
|
63
|
+
clientId: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Construction options for {@link HydraniumTextDocuments}. All fields optional —
|
|
68
|
+
* defaults reproduce the framework's behaviour exactly.
|
|
69
|
+
*/
|
|
70
|
+
export interface HydraniumTextDocumentsOptions<T extends TextDocument = TextDocument> extends LogNameOptions {
|
|
71
|
+
/**
|
|
72
|
+
* Factory for the text-document type `T` (provides `create` / `update`).
|
|
73
|
+
* Defaults to `TextDocument` from `vscode-languageserver-textdocument`.
|
|
74
|
+
* Adopters with a custom text-document type (rare) pass their factory.
|
|
75
|
+
*/
|
|
76
|
+
readonly configuration?: TextDocumentsConfiguration<T>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* All per-URI client-facing tracking the manager keys by normalized URI,
|
|
81
|
+
* collapsed into one record so a URI's full state lives in one place and the
|
|
82
|
+
* last-client close clears every axis in a single delete. (Parallel per-axis
|
|
83
|
+
* maps are the substrate of a close-on-stale-state desync — one map can be
|
|
84
|
+
* cleared while another lingers.)
|
|
85
|
+
*
|
|
86
|
+
* Two neighbours deliberately stay separate:
|
|
87
|
+
* - The inherited `__syncedDocuments` (Langium's parsed `TextDocument` store).
|
|
88
|
+
* - {@link LanguageClientTextShadow} (`__shadow`), a self-contained,
|
|
89
|
+
* separately-tested diff/apply-verify abstraction that owns its own baseline
|
|
90
|
+
* text; folding its storage here would couple a clean utility to this record
|
|
91
|
+
* for no real gain.
|
|
92
|
+
*/
|
|
93
|
+
interface DocumentTracking {
|
|
94
|
+
/** Client ids currently holding this document open (multi-client membership). */
|
|
95
|
+
readonly clients: Set<string>;
|
|
96
|
+
/** Author of each version, sparse-indexed by the SHARED (server-assigned) version number. */
|
|
97
|
+
readonly versionAuthors: string[];
|
|
98
|
+
/**
|
|
99
|
+
* Last version id each client declared for this document (didOpen baseline,
|
|
100
|
+
* advanced by every accepted didChange). Client version ids are CLIENT-owned
|
|
101
|
+
* per LSP (Monaco numbers its own buffer) and are used ONLY for this
|
|
102
|
+
* per-client staleness guard — they never leak into the shared version
|
|
103
|
+
* sequence, which the server assigns (see {@link HydraniumTextDocuments.__versionSequences}).
|
|
104
|
+
*/
|
|
105
|
+
readonly clientVersions: Map<string, number>;
|
|
106
|
+
/** Content staged by integrity rules for a closed document. Consumed on next open. */
|
|
107
|
+
pendingContent?: string;
|
|
108
|
+
/**
|
|
109
|
+
* The URI(s) the LSP textual language client opened this (canonically-keyed)
|
|
110
|
+
* document under. Usually one; a set because the same physical file can be
|
|
111
|
+
* opened under more than one URI (a symlink path and its real path). This is
|
|
112
|
+
* the egress address: `applyEditToLanguageClient`
|
|
113
|
+
* targets these, since the document is *keyed* by its canonical identity but
|
|
114
|
+
* Monaco holds it under the URI it opened. Lifecycle-bound — cleared with the
|
|
115
|
+
* record on last close.
|
|
116
|
+
*/
|
|
117
|
+
languageClientUris?: Set<LanguageClientUri>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A document held open by at least one client, with the client ids holding it —
|
|
122
|
+
* one entry of {@link HydraniumTextDocuments.openDocuments}.
|
|
123
|
+
*/
|
|
124
|
+
export interface OpenDocument {
|
|
125
|
+
/** Canonical identity the store keys the document by. */
|
|
126
|
+
readonly uri: CanonicalUri;
|
|
127
|
+
/** Client ids currently holding the document open (never empty). */
|
|
128
|
+
readonly clients: readonly string[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Where a URI's shared version sequence left off — written once at last-client
|
|
133
|
+
* close, consulted at the next open so the sequence CONTINUES instead of
|
|
134
|
+
* restarting at whatever version id the reopening client declares. One entry
|
|
135
|
+
* of {@link HydraniumTextDocuments.__versionSequences}.
|
|
136
|
+
*/
|
|
137
|
+
interface VersionSequence {
|
|
138
|
+
/** The shared version at last-client close. */
|
|
139
|
+
readonly version: number;
|
|
140
|
+
/** {@link contentHash} of the synced text at last-client close. */
|
|
141
|
+
readonly contentHash: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Upper bound on {@link HydraniumTextDocuments.__pendingPushes} entries
|
|
146
|
+
* per URI. Echoes normally return within milliseconds and consume their
|
|
147
|
+
* entry; a queue this deep means the client stopped echoing — cap instead
|
|
148
|
+
* of leaking.
|
|
149
|
+
*/
|
|
150
|
+
const PENDING_ECHO_CAP = 32;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The one field of `vscode-languageserver`'s `Connection` this class has to
|
|
154
|
+
* reach that its public type does not declare. Named here rather than cast
|
|
155
|
+
* inline so the write states which shape it assumes.
|
|
156
|
+
*/
|
|
157
|
+
interface ConnectionWithTextDocumentSync {
|
|
158
|
+
__textDocumentSync?: TextDocumentSyncKind;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* One text pushed to the LSP textual language client by
|
|
163
|
+
* {@link HydraniumTextDocuments.applyEditToLanguageClient} whose echo has not
|
|
164
|
+
* come back yet. One entry of {@link HydraniumTextDocuments.__pendingPushes}.
|
|
165
|
+
*/
|
|
166
|
+
interface PendingPush {
|
|
167
|
+
/**
|
|
168
|
+
* The text the client held BEFORE this push — the shadow baseline the
|
|
169
|
+
* push's edits were diffed against, and therefore the text the client's
|
|
170
|
+
* echo addresses with its ranges.
|
|
171
|
+
*
|
|
172
|
+
* `undefined` when the push was a full replace sent with no baseline (a
|
|
173
|
+
* first sync, or the recovery push after a rejection). There is then no
|
|
174
|
+
* knowing what the client held, so the echo is reconstructed against the
|
|
175
|
+
* synced text instead — which is sound for a full replace, whose
|
|
176
|
+
* application is position-independent.
|
|
177
|
+
*/
|
|
178
|
+
readonly before: string | undefined;
|
|
179
|
+
/** {@link contentHash} of the text this push moves the client to. */
|
|
180
|
+
readonly afterHash: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* What an incoming language-client change turns out to be once reconstructed
|
|
185
|
+
* against the pushes still in flight — the return of
|
|
186
|
+
* {@link HydraniumTextDocuments.classifyLanguageClientChange}.
|
|
187
|
+
*/
|
|
188
|
+
type LanguageClientChangeOrigin =
|
|
189
|
+
/** The client is reporting a text we pushed it. The synced document is already there. */
|
|
190
|
+
| { readonly kind: 'echo' }
|
|
191
|
+
/**
|
|
192
|
+
* The client's buffer has moved somewhere we did not push it — it coalesced
|
|
193
|
+
* a genuine keystroke into the echo, or typed before the push arrived. The
|
|
194
|
+
* reconstructed text is what it now holds, and is authoritative.
|
|
195
|
+
*/
|
|
196
|
+
| { readonly kind: 'divergent'; readonly text: string };
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Cheap, stable, non-cryptographic content hash (cyrb53) for the version
|
|
200
|
+
* sequence's "did the content change across close/reopen?" question. Only
|
|
201
|
+
* needs to be collision-resistant enough that an accidental match across two
|
|
202
|
+
* DIFFERENT revisions of the same file is practically impossible; 53 bits of
|
|
203
|
+
* a well-mixed hash over full text + length gives that without pulling in
|
|
204
|
+
* `node:crypto` (this module must stay runnable in browser hosts).
|
|
205
|
+
*/
|
|
206
|
+
function contentHash(text: string): string {
|
|
207
|
+
let h1 = 0xdeadbeef;
|
|
208
|
+
let h2 = 0x41c6ce57;
|
|
209
|
+
for (let index = 0; index < text.length; index++) {
|
|
210
|
+
const code = text.charCodeAt(index);
|
|
211
|
+
h1 = Math.imul(h1 ^ code, 2654435761);
|
|
212
|
+
h2 = Math.imul(h2 ^ code, 1597334677);
|
|
213
|
+
}
|
|
214
|
+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
|
215
|
+
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
216
|
+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
|
217
|
+
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
218
|
+
return `${(4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36)}:${text.length}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Multi-client text-document tracking on top of Langium's `NormalizedTextDocuments`.
|
|
223
|
+
*
|
|
224
|
+
* Adds the framework features used by the integrity, model-server, and GLSP layers:
|
|
225
|
+
* - Per-document client membership (multiple clients can attach to the same URI).
|
|
226
|
+
* - A SERVER-OWNED shared version sequence: per-URI, monotonic across
|
|
227
|
+
* close/reopen cycles, advancing exactly when the synced content changes.
|
|
228
|
+
* Client-declared version ids (Monaco's buffer numbering) feed only a
|
|
229
|
+
* per-client staleness guard and never leak into the shared sequence —
|
|
230
|
+
* the two are different things (an editor's edit-operation counter vs the
|
|
231
|
+
* document's content-revision number), and splicing them lets versions drift
|
|
232
|
+
* silently past `baseVersion` gate holders.
|
|
233
|
+
* - Version-author history so each edit is attributable to its originating client.
|
|
234
|
+
* - Pending-content staging used by the integrity service to thread corrections
|
|
235
|
+
* through `workspace/applyEdit` cycles for currently-closed documents.
|
|
236
|
+
* - `didOpen` notifications arriving over the LSP connection wait on the
|
|
237
|
+
* workspace-ready promise, so a client's first open cannot race workspace
|
|
238
|
+
* discovery. Direct {@link notifyDidOpenTextDocument} calls (the non-LSP
|
|
239
|
+
* heads) do not pass that gate — their caller owns the ordering.
|
|
240
|
+
*/
|
|
241
|
+
export class HydraniumTextDocuments<T extends TextDocument = TextDocument> extends NormalizedTextDocuments<T> {
|
|
242
|
+
/**
|
|
243
|
+
* Per-URI client-facing tracking ({@link DocumentTracking}): client
|
|
244
|
+
* membership, version-author history, and integrity-staged pending content,
|
|
245
|
+
* keyed by normalized URI. One record per URI so the last-client close clears
|
|
246
|
+
* every axis atomically. The language-client diff baseline stays in
|
|
247
|
+
* {@link __shadow} (its own abstraction); the parsed-document store stays in
|
|
248
|
+
* the inherited `__syncedDocuments`.
|
|
249
|
+
*/
|
|
250
|
+
protected __documents = new Map<CanonicalUri, DocumentTracking>();
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Per-URI shared-version continuity across close/reopen cycles
|
|
254
|
+
* ({@link VersionSequence}), written at last-client close and consulted by
|
|
255
|
+
* the next first-client open. DELIBERATELY outside {@link DocumentTracking}:
|
|
256
|
+
* that record is deleted on last close, while the version sequence must
|
|
257
|
+
* survive it — the shared version is a server-owned, monotonic,
|
|
258
|
+
* advances-iff-content-changes counter that never resets while the server
|
|
259
|
+
* lives. That invariant is what makes an optimistic `baseVersion` gate
|
|
260
|
+
* sound: "version unchanged ⇔ content unchanged", with no false conflicts
|
|
261
|
+
* from close/reopen version resets and no false passes from a reopened
|
|
262
|
+
* sequence coincidentally landing on a stale writer's number.
|
|
263
|
+
*
|
|
264
|
+
* Never pruned: two small values per URI ever touched — bounded by
|
|
265
|
+
* workspace size, not by activity.
|
|
266
|
+
*/
|
|
267
|
+
protected readonly __versionSequences = new Map<CanonicalUri, VersionSequence>();
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Texts pushed to the LSP textual language client via
|
|
271
|
+
* {@link applyEditToLanguageClient} whose echoes have not come back yet
|
|
272
|
+
* ({@link PendingPush}), keyed like the shadow by language-client URI.
|
|
273
|
+
* Outbound pushes and inbound echoes are uncorrelated on the wire; this
|
|
274
|
+
* FIFO is the explicit correlation, and it is what
|
|
275
|
+
* {@link classifyLanguageClientChange} reconstructs against.
|
|
276
|
+
*
|
|
277
|
+
* **Each entry keeps the client's PRE-push text, not only a hash of the
|
|
278
|
+
* post-push one.** A hash alone can classify a full-text echo, whose
|
|
279
|
+
* application is a no-op either way — and that is all it ever classified,
|
|
280
|
+
* because a conforming client echoes INCREMENTAL ranges keyed to its
|
|
281
|
+
* previous buffer. Those ranges cannot be applied to the synced text (which
|
|
282
|
+
* the authored write already advanced) and cannot be reconstructed without
|
|
283
|
+
* the baseline: the line they insert lands twice, validates cleanly, and
|
|
284
|
+
* compounds on every later edit.
|
|
285
|
+
*
|
|
286
|
+
* Lifecycle: entries are consumed by the matching echo (together with any
|
|
287
|
+
* older entries it supersedes), and the whole queue drops when the client
|
|
288
|
+
* stops being a pure mirror — a divergent change, an `applyEdit`
|
|
289
|
+
* failure/rejection (shadow invalidation), a close, or an explicit
|
|
290
|
+
* shadow (re)baseline. {@link PENDING_ECHO_CAP} bounds the queue against
|
|
291
|
+
* a pathological echo that never arrives; the memory cost until then is
|
|
292
|
+
* one pre-push text per in-flight push, for milliseconds.
|
|
293
|
+
*/
|
|
294
|
+
protected readonly __pendingPushes = new Map<LanguageClientUri, PendingPush[]>();
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Tracked text content per URI for the LSP textual language client (Monaco / VS Code).
|
|
298
|
+
* Owned here so {@link applyEditToLanguageClient} can compute minimal `workspace/applyEdit`
|
|
299
|
+
* diffs instead of full-document replaces (5–20 s → <500 ms on 20-30 KB YAML diagrams).
|
|
300
|
+
*
|
|
301
|
+
* Auto-tracked from the multi-client text-document events: open / change / close of
|
|
302
|
+
* the language client (re)baseline or invalidate the shadow. Other client ids
|
|
303
|
+
* (form editor, GLSP, integrity) do NOT touch the shadow — only what Monaco believes
|
|
304
|
+
* it has matters for the diff.
|
|
305
|
+
*
|
|
306
|
+
* Apply-verify safety net is built in: if the diff doesn't reconstruct `newText`
|
|
307
|
+
* exactly, {@link LanguageClientTextShadow.computeEdits} falls back to a full-range
|
|
308
|
+
* replace and invokes the `onFallback` callback — a diff regression becomes log
|
|
309
|
+
* noise, not a 0-byte save.
|
|
310
|
+
*
|
|
311
|
+
* Assigned in the constructor body so the `onFallback` callback can capture
|
|
312
|
+
* `this.logger` after the parameter-property assignment has run (field
|
|
313
|
+
* initializers fire BEFORE parameter-property assignment in TS).
|
|
314
|
+
*/
|
|
315
|
+
protected readonly __shadow: LanguageClientTextShadow;
|
|
316
|
+
|
|
317
|
+
protected readonly tracer: Tracer;
|
|
318
|
+
protected readonly configuration: TextDocumentsConfiguration<T>;
|
|
319
|
+
|
|
320
|
+
constructor(
|
|
321
|
+
protected services: ServerSharedServices,
|
|
322
|
+
options: HydraniumTextDocumentsOptions<T> = {}
|
|
323
|
+
) {
|
|
324
|
+
const configuration = options.configuration ?? (TextDocument as unknown as TextDocumentsConfiguration<T>);
|
|
325
|
+
super(configuration);
|
|
326
|
+
this.configuration = configuration;
|
|
327
|
+
this.tracer = services.Tracer.for(options.logName ?? 'TextDocuments').trace('instantiated');
|
|
328
|
+
this.__shadow = new LanguageClientTextShadow(
|
|
329
|
+
(uri, reason) => this.tracer.with(uri).warn(`Diff apply-verify fallback (${reason}) — using full-document replace`),
|
|
330
|
+
this
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Re-exposed configuration factories — for framework-internal callers
|
|
335
|
+
// (LanguageClientTextShadow's apply-verify probe; IntegrityService.resyncDocument)
|
|
336
|
+
// that need to materialise documents outside the canonical didOpen/didChange flow
|
|
337
|
+
// and must respect the adopter's custom text-document type.
|
|
338
|
+
|
|
339
|
+
public create(uri: string, languageId: string, version: number, content: string): T {
|
|
340
|
+
return this.configuration.create(uri, languageId, version, content);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
public update(document: T, changes: TextDocumentContentChangeEvent[], version: number): T {
|
|
344
|
+
return this.configuration.update(document, changes, version);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
protected get __syncedDocuments(): Map<string, T> {
|
|
348
|
+
return this['_syncedDocuments'];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
protected get __onDidChangeContent(): Emitter<ClientTextDocumentChangeEvent<T>> {
|
|
352
|
+
return this['_onDidChangeContent'];
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
override get onDidChangeContent(): Event<ClientTextDocumentChangeEvent<T>> {
|
|
356
|
+
return this.__onDidChangeContent.event;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
protected get __onDidOpen(): Emitter<ClientTextDocumentChangeEvent<T>> {
|
|
360
|
+
return this['_onDidOpen'];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
override get onDidOpen(): Event<ClientTextDocumentChangeEvent<T>> {
|
|
364
|
+
return this.__onDidOpen.event;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
protected get __onDidClose(): Emitter<ClientTextDocumentChangeEvent<T>> {
|
|
368
|
+
return this['_onDidClose'];
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
override get onDidClose(): Event<ClientTextDocumentChangeEvent<T>> {
|
|
372
|
+
return this.__onDidClose.event;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
protected get __onDidSave(): Emitter<ClientTextDocumentChangeEvent<T>> {
|
|
376
|
+
return this['_onDidSave'];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
override get onDidSave(): Event<ClientTextDocumentChangeEvent<T>> {
|
|
380
|
+
return this['__onDidSave'].event;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
protected get __onWillSave(): Emitter<TextDocumentWillSaveEvent<T>> {
|
|
384
|
+
return this['_onWillSave'];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
protected get __willSaveWaitUntil(): RequestHandler<TextDocumentWillSaveEvent<T>, TextEdit[], void> | undefined {
|
|
388
|
+
return this['_willSaveWaitUntil'];
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
public override listen(connection: Connection): Disposable {
|
|
392
|
+
// The advertised `textDocumentSync` capability is read off this field
|
|
393
|
+
// while `initialize` is answered, and it falls back to
|
|
394
|
+
// `TextDocumentSyncKind.None` when the field holds anything but a number
|
|
395
|
+
// — at which point the client stops sending change notifications at all
|
|
396
|
+
// and every document goes stale after its first open. Upstream's own
|
|
397
|
+
// `TextDocuments.listen` performs this write; replacing that class means
|
|
398
|
+
// taking it over. The field is private to `vscode-languageserver`, so on
|
|
399
|
+
// a bump of the pinned wire chain re-check that it still exists: a rename
|
|
400
|
+
// makes this line a silent no-op, whose symptom is no `didChange` traffic
|
|
401
|
+
// rather than an error.
|
|
402
|
+
(connection as unknown as ConnectionWithTextDocumentSync).__textDocumentSync = TextDocumentSyncKind.Incremental;
|
|
403
|
+
const disposables = new DisposableCollection();
|
|
404
|
+
disposables.push(
|
|
405
|
+
connection.onDidOpenTextDocument(async (event: DidOpenTextDocumentParams) => {
|
|
406
|
+
await this.services.workspace.WorkspaceManager.workspaceInitialized;
|
|
407
|
+
this.notifyDidOpenTextDocument(event);
|
|
408
|
+
})
|
|
409
|
+
);
|
|
410
|
+
disposables.push(
|
|
411
|
+
connection.onDidChangeTextDocument((event: DidChangeTextDocumentParams) => {
|
|
412
|
+
this.notifyDidChangeTextDocument(event);
|
|
413
|
+
})
|
|
414
|
+
);
|
|
415
|
+
disposables.push(
|
|
416
|
+
connection.onDidCloseTextDocument((event: DidCloseTextDocumentParams) => {
|
|
417
|
+
this.notifyDidCloseTextDocument(event);
|
|
418
|
+
})
|
|
419
|
+
);
|
|
420
|
+
disposables.push(
|
|
421
|
+
connection.onWillSaveTextDocument((event: WillSaveTextDocumentParams) => {
|
|
422
|
+
this.notifyWillSaveTextDocument(event);
|
|
423
|
+
})
|
|
424
|
+
);
|
|
425
|
+
disposables.push(
|
|
426
|
+
connection.onWillSaveTextDocumentWaitUntil((event: WillSaveTextDocumentParams, token: CancellationToken) =>
|
|
427
|
+
this.notifyWillSaveTextDocumentWaitUntil(event, token)
|
|
428
|
+
)
|
|
429
|
+
);
|
|
430
|
+
disposables.push(
|
|
431
|
+
connection.onDidSaveTextDocument((event: DidSaveTextDocumentParams) => {
|
|
432
|
+
this.notifyDidSaveTextDocument(event);
|
|
433
|
+
})
|
|
434
|
+
);
|
|
435
|
+
return disposables;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
public notifyDidChangeTextDocument(event: DidChangeTextDocumentParams, clientId = LANGUAGE_CLIENT_ID): void {
|
|
439
|
+
const td = event.textDocument;
|
|
440
|
+
const changes = event.contentChanges;
|
|
441
|
+
if (changes.length === 0) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const { version } = td;
|
|
446
|
+
if (version === null || version === undefined) {
|
|
447
|
+
throw new Error(`Received document change event for ${td.uri} without valid version identifier`);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const uri = this.documentKey(td.uri);
|
|
451
|
+
let document = this.__syncedDocuments.get(uri);
|
|
452
|
+
if (document !== undefined) {
|
|
453
|
+
// Per-client staleness guard: client version ids are CLIENT-owned per
|
|
454
|
+
// LSP (Monaco numbers its own buffer), so an incoming id is compared
|
|
455
|
+
// against THAT client's last declared id — never against the shared
|
|
456
|
+
// version, which the server assigns and which routinely runs ahead of
|
|
457
|
+
// a client's ids (authored ModelService writes advance it without the
|
|
458
|
+
// client knowing). Gating on the shared version drops real edits in
|
|
459
|
+
// exactly that lag window. A client with no baseline (never opened —
|
|
460
|
+
// a protocol anomaly) falls back to the shared-version compare, the
|
|
461
|
+
// conservative answer.
|
|
462
|
+
const record = this.trackingFor(uri);
|
|
463
|
+
const lastSeen = record.clientVersions.get(clientId) ?? document.version;
|
|
464
|
+
if (lastSeen >= td.version) {
|
|
465
|
+
// Distinguish "already at this version" (common: an echo from the client that triggered
|
|
466
|
+
// the update) from "incoming version older than ours" (stale race).
|
|
467
|
+
const reason =
|
|
468
|
+
lastSeen === td.version ? `already at version ${lastSeen}` : `incoming version ${td.version} older than current ${lastSeen}`;
|
|
469
|
+
this.logUri(uri, `Ignore update by ${clientId}: ${reason}`, 'debug');
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
record.clientVersions.set(clientId, td.version);
|
|
473
|
+
|
|
474
|
+
// A language-client change arriving while one of our own pushes is in
|
|
475
|
+
// flight is keyed to the buffer the client held BEFORE that push, NOT
|
|
476
|
+
// to the synced text — the authored write already advanced the latter.
|
|
477
|
+
// Resolve which it is, and against what, before touching anything.
|
|
478
|
+
const inFlight =
|
|
479
|
+
clientId === LANGUAGE_CLIENT_ID
|
|
480
|
+
? this.classifyLanguageClientChange(this.toLanguageClientUri(td.uri), document, changes)
|
|
481
|
+
: undefined;
|
|
482
|
+
if (inFlight?.kind === 'echo') {
|
|
483
|
+
// The client is reporting a text we pushed it, so the synced
|
|
484
|
+
// document is ALREADY there and the changes must not be applied a
|
|
485
|
+
// second time. Nothing is minted and no rebuild fires. The
|
|
486
|
+
// per-client baseline advanced above (the client's ids keep
|
|
487
|
+
// counting); the shadow is deliberately NOT touched — it
|
|
488
|
+
// optimistically tracks the NEWEST pushed text, and dragging it
|
|
489
|
+
// back would make the next outbound diff wrong against what the
|
|
490
|
+
// client actually holds.
|
|
491
|
+
this.logUri(uri, `Skip rebuild: echo of a server-authored push (client version ${td.version})`, 'debug');
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// The SHARED version advances iff the content actually changes — the
|
|
496
|
+
// invariant optimistic `baseVersion` gates rely on. The new text is
|
|
497
|
+
// only known after applying the (possibly incremental) changes, so
|
|
498
|
+
// apply at a tentative +1 and roll the version back on an identical
|
|
499
|
+
// result (an empty-changes update only re-stamps the version).
|
|
500
|
+
//
|
|
501
|
+
// A divergent in-flight change is applied as its RECONSTRUCTED text
|
|
502
|
+
// rather than as its own ranges: the ranges address the pre-push
|
|
503
|
+
// baseline, so applying them here would splice the wrong lines.
|
|
504
|
+
const previousText = document.getText();
|
|
505
|
+
const sharedVersion = document.version;
|
|
506
|
+
document = this.configuration.update(document, inFlight === undefined ? changes : [{ text: inFlight.text }], sharedVersion + 1);
|
|
507
|
+
const changed = document.getText() !== previousText;
|
|
508
|
+
if (!changed) {
|
|
509
|
+
document = this.configuration.update(document, [], sharedVersion);
|
|
510
|
+
}
|
|
511
|
+
this.__syncedDocuments.set(uri, document);
|
|
512
|
+
if (changed) {
|
|
513
|
+
this.setAuthor(uri, document.version, clientId);
|
|
514
|
+
}
|
|
515
|
+
if (clientId === LANGUAGE_CLIENT_ID) {
|
|
516
|
+
// Monaco just told us about its new content; record it so the next outbound
|
|
517
|
+
// applyEditToLanguageClient diffs against the right baseline. Keyed by the
|
|
518
|
+
// language-client URI (what Monaco holds), not the canonical document key.
|
|
519
|
+
this.__shadow.set(this.toLanguageClientUri(td.uri), document.getText());
|
|
520
|
+
// Content-identical echo: the language client is echoing text we already had
|
|
521
|
+
// (e.g. Monaco re-emitting a server-pushed applyEditToLanguageClient). The model is
|
|
522
|
+
// unchanged, so skip the rebuild. The per-client baseline + shadow still advanced
|
|
523
|
+
// above so future staleness checks and diffs are correct. Restricted to the
|
|
524
|
+
// language client: a ModelService-authored change is never skipped.
|
|
525
|
+
if (!changed) {
|
|
526
|
+
this.logUri(uri, `Skip rebuild: content unchanged (echo at client version ${td.version})`, 'debug');
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
this.log(document.uri, `Update to version ${document.version} by ${clientId}${changed ? '' : ' (content unchanged)'}`);
|
|
531
|
+
this.__onDidChangeContent.fire(Object.freeze({ document, clientId }));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Apply a full-text content change authored server-side (ModelService /
|
|
537
|
+
* GLSP / integrity — any writer that is not an LSP wire client). The store
|
|
538
|
+
* assigns the shared version itself: step iff `text` differs from the
|
|
539
|
+
* current synced content, keep otherwise. Contrast
|
|
540
|
+
* {@link notifyDidChangeTextDocument}, the LSP wire path, where the client
|
|
541
|
+
* declares ITS version id and that id feeds only the per-client staleness
|
|
542
|
+
* guard.
|
|
543
|
+
*
|
|
544
|
+
* A content-identical write still fires the change event (rebuild): the
|
|
545
|
+
* authored write's server-side rebuild is correctness-bearing, it just
|
|
546
|
+
* mints no new version — nothing observable changed, so watchers'
|
|
547
|
+
* `baseVersion` pointers stay valid.
|
|
548
|
+
*
|
|
549
|
+
* Returns the resulting shared version. Throws when the document is not
|
|
550
|
+
* open — callers (`AstDocumentManager.update`) open first.
|
|
551
|
+
*/
|
|
552
|
+
applyContentChange(uri: DocumentUri, text: string, clientId: string): number {
|
|
553
|
+
const key = this.documentKey(uri);
|
|
554
|
+
let document = this.__syncedDocuments.get(key);
|
|
555
|
+
if (document === undefined) {
|
|
556
|
+
throw new Error(`Document ${uri} is not open for content changes`);
|
|
557
|
+
}
|
|
558
|
+
const changed = document.getText() !== text;
|
|
559
|
+
if (changed) {
|
|
560
|
+
document = this.configuration.update(document, [{ text }], document.version + 1);
|
|
561
|
+
this.__syncedDocuments.set(key, document);
|
|
562
|
+
this.setAuthor(key, document.version, clientId);
|
|
563
|
+
}
|
|
564
|
+
this.log(document.uri, `Update to version ${document.version} by ${clientId}${changed ? '' : ' (content unchanged)'}`);
|
|
565
|
+
this.__onDidChangeContent.fire(Object.freeze({ document, clientId }));
|
|
566
|
+
return document.version;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
public notifyDidCloseTextDocument(event: DidCloseTextDocumentParams, clientId = LANGUAGE_CLIENT_ID): void {
|
|
570
|
+
const uri = this.documentKey(event.textDocument.uri);
|
|
571
|
+
if (!this.isOpenInClient(uri, clientId)) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const closingRecord = this.__documents.get(uri);
|
|
575
|
+
closingRecord?.clients.delete(clientId);
|
|
576
|
+
closingRecord?.clientVersions.delete(clientId);
|
|
577
|
+
const syncedDocument = this.__syncedDocuments.get(uri);
|
|
578
|
+
if (syncedDocument !== undefined) {
|
|
579
|
+
this.log(syncedDocument.uri, `Closed synced document: ${syncedDocument.version} by ${clientId}`);
|
|
580
|
+
this.__onDidClose.fire(Object.freeze({ document: syncedDocument, clientId }));
|
|
581
|
+
|
|
582
|
+
if (clientId === LANGUAGE_CLIENT_ID) {
|
|
583
|
+
// Monaco closed the document; drop the URI it held and the shadow
|
|
584
|
+
// baselined under it. (If this was the last URI/client the whole record
|
|
585
|
+
// is deleted below, clearing the set anyway.)
|
|
586
|
+
const droppedUri = this.toLanguageClientUri(event.textDocument.uri);
|
|
587
|
+
this.__documents.get(uri)?.languageClientUris?.delete(droppedUri);
|
|
588
|
+
this.__shadow.invalidate(droppedUri);
|
|
589
|
+
this.__pendingPushes.delete(droppedUri);
|
|
590
|
+
}
|
|
591
|
+
if (!this.__documents.get(uri)?.clients.size) {
|
|
592
|
+
// Last client closed the document; delete sync state. The downstream
|
|
593
|
+
// "rebuild from disk for file URIs / drop from index for ephemeral
|
|
594
|
+
// URIs" decision lives in `HydraniumDocumentUpdateHandler.didCloseDocument`,
|
|
595
|
+
// which subscribes to `onDidClose` and consults `isOpenInAnyClient`
|
|
596
|
+
// to detect the last-close transition.
|
|
597
|
+
this.log(syncedDocument.uri, `Remove synced document: ${syncedDocument.version} (no client left)`);
|
|
598
|
+
// Persist where the shared version sequence left off (version +
|
|
599
|
+
// content hash) so the next open CONTINUES the sequence instead of
|
|
600
|
+
// restarting at the reopening client's declared id. Hashed once
|
|
601
|
+
// here at close, not on every change.
|
|
602
|
+
this.__versionSequences.set(uri, {
|
|
603
|
+
version: syncedDocument.version,
|
|
604
|
+
contentHash: contentHash(syncedDocument.getText())
|
|
605
|
+
});
|
|
606
|
+
this.__syncedDocuments.delete(uri);
|
|
607
|
+
// One delete clears every per-URI axis (version history + any staged
|
|
608
|
+
// pending content) so a future open with the same URI starts fresh.
|
|
609
|
+
this.__documents.delete(uri);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
public notifyWillSaveTextDocument(event: WillSaveTextDocumentParams): void {
|
|
615
|
+
const syncedDocument = this.__syncedDocuments.get(this.documentKey(event.textDocument.uri));
|
|
616
|
+
if (syncedDocument !== undefined) {
|
|
617
|
+
this.__onWillSave.fire(Object.freeze({ document: syncedDocument, reason: event.reason }));
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
public notifyWillSaveTextDocumentWaitUntil(
|
|
622
|
+
event: WillSaveTextDocumentParams,
|
|
623
|
+
token: CancellationToken
|
|
624
|
+
): HandlerResult<TextEdit[], void> {
|
|
625
|
+
const syncedDocument = this.__syncedDocuments.get(this.documentKey(event.textDocument.uri));
|
|
626
|
+
if (syncedDocument !== undefined && this.__willSaveWaitUntil) {
|
|
627
|
+
return this.__willSaveWaitUntil(Object.freeze({ document: syncedDocument, reason: event.reason }), token);
|
|
628
|
+
} else {
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
public notifyDidSaveTextDocument(event: DidSaveTextDocumentParams, clientId = LANGUAGE_CLIENT_ID): void {
|
|
634
|
+
const syncedDocument = this.__syncedDocuments.get(this.documentKey(event.textDocument.uri));
|
|
635
|
+
if (syncedDocument !== undefined) {
|
|
636
|
+
this.log(syncedDocument.uri, `Saved synced document: ${syncedDocument.version} by ${clientId}`);
|
|
637
|
+
this.__onDidSave.fire(Object.freeze({ document: syncedDocument, clientId }));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
public notifyDidOpenTextDocument(event: DidOpenTextDocumentParams, clientId = LANGUAGE_CLIENT_ID): void {
|
|
642
|
+
const td = event.textDocument;
|
|
643
|
+
const uri = this.documentKey(td.uri);
|
|
644
|
+
if (this.isOpenInClient(uri, clientId)) {
|
|
645
|
+
// Already open for this client under this canonical identity. If this is a
|
|
646
|
+
// NEW client-facing URI for the same file (a second tab reached via a
|
|
647
|
+
// divergent path, e.g. a symlink and its real path), record it and baseline
|
|
648
|
+
// its shadow so outbound edits reach this tab too — but do NOT re-fire
|
|
649
|
+
// open/rebuild; the document is already live.
|
|
650
|
+
if (clientId === LANGUAGE_CLIENT_ID) {
|
|
651
|
+
const clientFacing = this.toLanguageClientUri(td.uri);
|
|
652
|
+
const record = this.__documents.get(uri);
|
|
653
|
+
if (record && !record.languageClientUris?.has(clientFacing)) {
|
|
654
|
+
(record.languageClientUris ??= new Set<LanguageClientUri>()).add(clientFacing);
|
|
655
|
+
const open = this.__syncedDocuments.get(uri);
|
|
656
|
+
if (open) {
|
|
657
|
+
this.__shadow.set(clientFacing, open.getText());
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
let document = this.__syncedDocuments.get(uri);
|
|
664
|
+
const record = this.trackingFor(uri);
|
|
665
|
+
const existingClients = [...record.clients];
|
|
666
|
+
record.clients.add(clientId);
|
|
667
|
+
// Baseline the per-client staleness guard at the version id the client
|
|
668
|
+
// declared for its own buffer (client-owned per LSP).
|
|
669
|
+
record.clientVersions.set(clientId, td.version);
|
|
670
|
+
if (clientId === LANGUAGE_CLIENT_ID) {
|
|
671
|
+
// Remember the URI Monaco opened under (may differ from the canonical key)
|
|
672
|
+
// so outbound applyEditToLanguageClient can address the URI it actually holds.
|
|
673
|
+
(record.languageClientUris ??= new Set<LanguageClientUri>()).add(this.toLanguageClientUri(td.uri));
|
|
674
|
+
}
|
|
675
|
+
if (!document) {
|
|
676
|
+
// Use integrity-staged content if available, otherwise the client-provided (disk) text.
|
|
677
|
+
const pendingText = this.consumePendingContent(uri);
|
|
678
|
+
const text = pendingText ?? td.text;
|
|
679
|
+
const source = pendingText ? ', source=pending' : '';
|
|
680
|
+
// The SHARED version is server-assigned: continue the persisted
|
|
681
|
+
// sequence — same version when the content is unchanged since the
|
|
682
|
+
// last close (so watchers' `baseVersion` pointers stay valid), one
|
|
683
|
+
// step when it changed (so no stale pointer can coincidentally pass
|
|
684
|
+
// the optimistic gate). Only a first-ever open adopts the client's
|
|
685
|
+
// declared id as the sequence seed.
|
|
686
|
+
const sequence = this.__versionSequences.get(uri);
|
|
687
|
+
const version =
|
|
688
|
+
sequence === undefined ? td.version : sequence.contentHash === contentHash(text) ? sequence.version : sequence.version + 1;
|
|
689
|
+
this.log(uri, `Open document: Version ${version} by ${clientId} [first client${source}]`);
|
|
690
|
+
document = this.configuration.create(uri, td.languageId, version, text);
|
|
691
|
+
this.__syncedDocuments.set(uri, document);
|
|
692
|
+
this.setAuthor(uri, version, clientId);
|
|
693
|
+
if (clientId === LANGUAGE_CLIENT_ID) {
|
|
694
|
+
// Baseline the shadow to what Monaco just opened so the next outbound
|
|
695
|
+
// applyEditToLanguageClient diffs against the right starting point. Keyed by the
|
|
696
|
+
// language-client URI (what Monaco holds), not the canonical document key.
|
|
697
|
+
this.__shadow.set(this.toLanguageClientUri(td.uri), document.getText());
|
|
698
|
+
}
|
|
699
|
+
const toFire = Object.freeze({ document, clientId });
|
|
700
|
+
this.__onDidOpen.fire(toFire);
|
|
701
|
+
this.__onDidChangeContent.fire(toFire);
|
|
702
|
+
} else {
|
|
703
|
+
// An additional client attaches to a document already open by another client.
|
|
704
|
+
this.log(
|
|
705
|
+
uri,
|
|
706
|
+
`Attach client: ${clientId} joined existing document (version ${document.version}, ` +
|
|
707
|
+
`now open in: ${[...existingClients, clientId].join(', ')})`
|
|
708
|
+
);
|
|
709
|
+
this.refreshContent(uri, clientId);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
refreshContent(uri: DocumentUri, clientId: string): void {
|
|
714
|
+
const syncedDocument = this.__syncedDocuments.get(this.documentKey(uri));
|
|
715
|
+
if (syncedDocument) {
|
|
716
|
+
// Trigger a (re-)build by firing a change event.
|
|
717
|
+
const timer = this.startTimerForUri(
|
|
718
|
+
syncedDocument.uri,
|
|
719
|
+
`Refresh synced document: Version ${syncedDocument.version} by ${clientId}`
|
|
720
|
+
);
|
|
721
|
+
this.__onDidChangeContent.fire(Object.freeze({ document: syncedDocument, clientId }));
|
|
722
|
+
timer.dispose();
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* The canonical key a document is stored under — the single point this store
|
|
728
|
+
* turns an incoming URI into its identity, the store-side analogue of
|
|
729
|
+
* `DocumentUriPolicy.canonicalUri`. Keying by the CANONICAL identity collapses
|
|
730
|
+
* two URIs for one physical file (a symlink path and its real path) into a
|
|
731
|
+
* single registration — dedup at the editor layer, not just in
|
|
732
|
+
* `LangiumDocuments`. The URI the client opened under is preserved separately
|
|
733
|
+
* for egress addressing (see {@link DocumentTracking.languageClientUris}). The
|
|
734
|
+
* policy is always bound (the framework defaults it to
|
|
735
|
+
* `DefaultDocumentUriPolicy`, where canonical ≡ syntactic normalize).
|
|
736
|
+
*/
|
|
737
|
+
protected documentKey(uri: string): CanonicalUri {
|
|
738
|
+
return this.services.workspace.DocumentUriPolicy.canonicalUri(uri);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* The language-client URI for `uri` — syntactic `normalize` only, NOT
|
|
743
|
+
* canonicalized. This is the URI the LSP textual client (Monaco / VS Code)
|
|
744
|
+
* actually holds the document under (what `applyEditToLanguageClient` must address and
|
|
745
|
+
* what the shadow is keyed by), distinct from {@link documentKey} (the canonical
|
|
746
|
+
* identity the document is stored under). They coincide unless the path diverges
|
|
747
|
+
* from its real path (symlink / `..` / case).
|
|
748
|
+
*/
|
|
749
|
+
protected toLanguageClientUri(uri: string): LanguageClientUri {
|
|
750
|
+
return asLanguageClientUri(UriUtils.normalize(uri));
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/** Get-or-create the per-URI tracking record. `uri` must already be a {@link documentKey}. */
|
|
754
|
+
protected trackingFor(uri: CanonicalUri): DocumentTracking {
|
|
755
|
+
let record = this.__documents.get(uri);
|
|
756
|
+
if (!record) {
|
|
757
|
+
record = { clients: new Set(), versionAuthors: [], clientVersions: new Map() };
|
|
758
|
+
this.__documents.set(uri, record);
|
|
759
|
+
}
|
|
760
|
+
return record;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
setAuthor(uri: DocumentUri, version: number, author: string): void {
|
|
764
|
+
this.trackingFor(this.documentKey(uri)).versionAuthors[version] = author;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Resolve the synced `TextDocument` for `uri`. The store keys documents by
|
|
769
|
+
* their canonical identity ({@link documentKey}), so a direct normalized hit
|
|
770
|
+
* wins the common case (the caller passes the canonical URI, or the opened URI
|
|
771
|
+
* coincides with it) with no canonicalization, and a miss falls back to the
|
|
772
|
+
* single canonical-key lookup — which is what bridges a client-space caller
|
|
773
|
+
* that addresses a symlinked path `S` to the document the build keys by its
|
|
774
|
+
* real path `R` (`documentKey(S) === R`).
|
|
775
|
+
*
|
|
776
|
+
* O(1): no scan. Because the interior is already canonically keyed, looking the
|
|
777
|
+
* canonical key up directly is equivalent to reverse-matching every entry, and
|
|
778
|
+
* the `super.get` fast path keeps the common case syscall-free (the
|
|
779
|
+
* canonical lookup, and its one `canonicalUri` realpath, runs only on a miss).
|
|
780
|
+
* Under the default policy `documentKey ≡ normalize`, so the fallback can never
|
|
781
|
+
* find a key the direct lookup didn't — `get` is then identical to the base.
|
|
782
|
+
*/
|
|
783
|
+
override get(uri: DocumentUri): T | undefined {
|
|
784
|
+
return super.get(uri) ?? this.__syncedDocuments.get(this.documentKey(uri));
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Current SHARED version of the document at `uri`: the open document's
|
|
789
|
+
* version, else where the persisted sequence left off at last close, else
|
|
790
|
+
* `0` for a URI this store has never seen. The shared sequence is
|
|
791
|
+
* server-owned and monotonic across close/reopen cycles, and advances
|
|
792
|
+
* exactly when the synced content changes — which is what makes it a sound
|
|
793
|
+
* optimistic-concurrency token (`baseVersion` gates): version unchanged ⇔
|
|
794
|
+
* content unchanged.
|
|
795
|
+
*/
|
|
796
|
+
version(uri: DocumentUri): number {
|
|
797
|
+
return this.get(uri)?.version ?? this.__versionSequences.get(this.documentKey(uri))?.version ?? 0;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Reconcile the persisted version sequence with content that reached the
|
|
802
|
+
* build OUTSIDE the store's write paths — a closed document rebuilt from
|
|
803
|
+
* disk (last-close revert) or replaced by a watched-file change. Steps the
|
|
804
|
+
* sequence iff `text` differs from the sequence's last-known content and
|
|
805
|
+
* returns the resulting sequence version so the caller can re-stamp the
|
|
806
|
+
* rebuilt document (see the `AstDocumentManager` Parsed-phase listener) —
|
|
807
|
+
* keeping the "version advances iff content changes" invariant for
|
|
808
|
+
* documents no client currently holds.
|
|
809
|
+
*
|
|
810
|
+
* Returns `undefined` — and does nothing — when the document is open (the
|
|
811
|
+
* store's own content is authoritative; external transitions never reach
|
|
812
|
+
* the Langium factory for open documents) or was never tracked (no gate
|
|
813
|
+
* holder can hold a version pointer for it, and the early exit keeps
|
|
814
|
+
* workspace-wide builds from hashing every untouched document).
|
|
815
|
+
*/
|
|
816
|
+
reconcileExternalContent(uri: DocumentUri, text: string): number | undefined {
|
|
817
|
+
const key = this.documentKey(uri);
|
|
818
|
+
if (this.__syncedDocuments.has(key)) {
|
|
819
|
+
return undefined;
|
|
820
|
+
}
|
|
821
|
+
const sequence = this.__versionSequences.get(key);
|
|
822
|
+
if (sequence === undefined) {
|
|
823
|
+
return undefined;
|
|
824
|
+
}
|
|
825
|
+
const hash = contentHash(text);
|
|
826
|
+
if (hash === sequence.contentHash) {
|
|
827
|
+
return sequence.version;
|
|
828
|
+
}
|
|
829
|
+
const stepped: VersionSequence = { version: sequence.version + 1, contentHash: hash };
|
|
830
|
+
this.__versionSequences.set(key, stepped);
|
|
831
|
+
this.logUri(key, `External content change while closed: sequence stepped to version ${stepped.version}`, 'debug');
|
|
832
|
+
return stepped.version;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
getAuthor(uri: DocumentUri, version?: number): string | undefined {
|
|
836
|
+
const history = this.__documents.get(this.documentKey(uri))?.versionAuthors;
|
|
837
|
+
// Either the requested version, or the latest. `version !== undefined` so we treat 0 correctly.
|
|
838
|
+
const clientId = version !== undefined ? history?.[version] : history?.at(-1);
|
|
839
|
+
if (!clientId && history) {
|
|
840
|
+
// Only warn when there IS a history but the specific version is missing; no history at all
|
|
841
|
+
// means the document was rebuilt internally (e.g. by a project manager), not an error.
|
|
842
|
+
this.log(uri, `Could not detect author of version ${version}.`);
|
|
843
|
+
}
|
|
844
|
+
return clientId;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
isOpen(uri: DocumentUri): boolean {
|
|
848
|
+
return this.__syncedDocuments.has(this.documentKey(uri));
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* True iff any client still holds `uri` open. Distinct from {@link isOpen},
|
|
853
|
+
* which reads `__syncedDocuments` — that map is cleared only AFTER the
|
|
854
|
+
* `onDidClose` event fires for the last client, so `isOpen` returns `true`
|
|
855
|
+
* during the close event itself. `isOpenInAnyClient` reads the tracking
|
|
856
|
+
* record's `clients` set, which is updated BEFORE the fire, so an `onDidClose`
|
|
857
|
+
* subscriber that finds this `false` knows the last client just closed and a
|
|
858
|
+
* disk re-read / rebuild can proceed.
|
|
859
|
+
*/
|
|
860
|
+
isOpenInAnyClient(uri: DocumentUri): boolean {
|
|
861
|
+
return (this.__documents.get(this.documentKey(uri))?.clients.size ?? 0) > 0;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
isOpenInClient(uri: DocumentUri, client: string): boolean {
|
|
865
|
+
return !!this.__documents.get(this.documentKey(uri))?.clients.has(client);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
isOpenInLanguageClient(uri: DocumentUri): boolean {
|
|
869
|
+
// Takes any URI form (canonicalizes internally via `documentKey`, like
|
|
870
|
+
// `isOpen`); the document is keyed canonically, so a canonical or a
|
|
871
|
+
// client-facing URI both resolve to the same registration.
|
|
872
|
+
return this.isOpenInClient(uri, LANGUAGE_CLIENT_ID);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
isOnlyOpenInClient(uri: DocumentUri, client: string): boolean {
|
|
876
|
+
const key = this.documentKey(uri);
|
|
877
|
+
return this.__documents.get(key)?.clients.size === 1 && this.isOpenInClient(key, client);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Every document currently held open by at least one client, with the client
|
|
882
|
+
* ids holding it. Reads the same per-URI tracking records as
|
|
883
|
+
* {@link isOpenInAnyClient}, so it reflects a last-client close immediately.
|
|
884
|
+
*
|
|
885
|
+
* Diagnostics-oriented — the server-state snapshot lists these so an operator
|
|
886
|
+
* can see WHY a document is pinned: a document that lingers here after its
|
|
887
|
+
* editor closed points directly at the client that failed to close it (and,
|
|
888
|
+
* under a shedding policy, explains why its CST is never shed).
|
|
889
|
+
*/
|
|
890
|
+
openDocuments(): OpenDocument[] {
|
|
891
|
+
const result: OpenDocument[] = [];
|
|
892
|
+
for (const [uri, tracking] of this.__documents) {
|
|
893
|
+
if (tracking.clients.size > 0) {
|
|
894
|
+
result.push({ uri, clients: [...tracking.clients] });
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return result;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Stages integrity-updated content for a closed document.
|
|
902
|
+
*
|
|
903
|
+
* When `workspace/applyEdit` targets a closed file, the client opens it from
|
|
904
|
+
* disk (stale) and sends `didOpen` before applying the edit. This staged
|
|
905
|
+
* content is consumed by {@link notifyDidOpenTextDocument} to replace the
|
|
906
|
+
* stale disk text, preventing a brief revert of the integrity update.
|
|
907
|
+
*
|
|
908
|
+
* Not cleared on close: pending content is only staged for already-closed
|
|
909
|
+
* documents (the integrity service checks `isOpenInLanguageClient` first), so
|
|
910
|
+
* a close → stage → open cycle cannot occur. The only scenario where an
|
|
911
|
+
* entry lingers is if `workspace/applyEdit` fails and the file is never
|
|
912
|
+
* opened — the memory cost is one serialised string per URI.
|
|
913
|
+
*/
|
|
914
|
+
stagePendingContent(uri: DocumentUri, text: string): void {
|
|
915
|
+
this.trackingFor(this.documentKey(uri)).pendingContent = text;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/**
|
|
919
|
+
* Send `newText` to the LSP textual language client (Monaco / VS Code) via
|
|
920
|
+
* `workspace/applyEdit`, using the tracked shadow to produce minimal
|
|
921
|
+
* `TextEdit`s instead of a full-document replace.
|
|
922
|
+
*
|
|
923
|
+
* Returns `undefined` when:
|
|
924
|
+
* - The shared services have no LSP {@link Connection} bound (non-LSP
|
|
925
|
+
* hosts like CLI / tests).
|
|
926
|
+
* - The shadow already matches `newText` (no edits needed; quiet skip).
|
|
927
|
+
*
|
|
928
|
+
* On `applyEdit` rejection (`result.applied === false`) or RPC failure the
|
|
929
|
+
* shadow is invalidated so the next call sends a full-replace baseline.
|
|
930
|
+
* Errors are re-thrown — callers wrap with their own retry / coalescing
|
|
931
|
+
* policy as needed.
|
|
932
|
+
*
|
|
933
|
+
* The diff path is apply-verify-safe: the shadow internally checks that
|
|
934
|
+
* `TextDocument.applyEdits(old, edits) === newText` and falls back to a
|
|
935
|
+
* full replace on mismatch (logged via the warn-callback wired in the
|
|
936
|
+
* constructor), so a diff regression becomes log noise, not data loss.
|
|
937
|
+
*
|
|
938
|
+
* That safety net verifies the diff against the SHADOW, which is what the
|
|
939
|
+
* client is *believed* to hold — so it cannot see the client's buffer moving
|
|
940
|
+
* underneath a push. A line-keyed edit is position-dependent: if a genuine
|
|
941
|
+
* client keystroke lands between {@link LanguageClientTextShadow.computeEdits}
|
|
942
|
+
* and the client applying, the ranges address the wrong lines and splice the
|
|
943
|
+
* buffer (observed as a duplicated declaration, which the integrity tier then
|
|
944
|
+
* "repairs" into a suffixed name and persists). The edit is therefore
|
|
945
|
+
* addressed at the language client's LAST DECLARED VERSION rather than at
|
|
946
|
+
* `null` ("version intentionally unknown"), which is what lets the client
|
|
947
|
+
* reject a push its buffer has outrun. On rejection the shadow is invalidated,
|
|
948
|
+
* so the caller's retry is a full-range replace — position-independent, and
|
|
949
|
+
* safe to apply to whatever the client now holds.
|
|
950
|
+
*/
|
|
951
|
+
async applyEditToLanguageClient(
|
|
952
|
+
uri: DocumentUri,
|
|
953
|
+
newText: string,
|
|
954
|
+
options?: { label?: string }
|
|
955
|
+
): Promise<ApplyWorkspaceEditResult | undefined> {
|
|
956
|
+
const connection = this.services.lsp?.Connection;
|
|
957
|
+
if (!connection) {
|
|
958
|
+
return undefined;
|
|
959
|
+
}
|
|
960
|
+
// The document is keyed by its canonical identity, but Monaco holds it under
|
|
961
|
+
// the URI(s) it opened. Address each of those language-client URIs — the one
|
|
962
|
+
// R→S translation, kept here at the egress. Usually one; more than one only
|
|
963
|
+
// when the same file was opened under a symlink and its real path. Falls back
|
|
964
|
+
// to the normalized URI when nothing is tracked. The shadow is keyed by each
|
|
965
|
+
// URI, so every diff is against the right baseline.
|
|
966
|
+
const recorded = this.__documents.get(this.documentKey(uri))?.languageClientUris;
|
|
967
|
+
const targets: Iterable<LanguageClientUri> = recorded && recorded.size > 0 ? recorded : [this.toLanguageClientUri(uri)];
|
|
968
|
+
let lastResult: ApplyWorkspaceEditResult | undefined;
|
|
969
|
+
for (const targetUri of targets) {
|
|
970
|
+
// Read BEFORE computeEdits, which overwrites it. This is the text the
|
|
971
|
+
// edits below are keyed to, and therefore the only text the client's
|
|
972
|
+
// echo of them can be reconstructed against.
|
|
973
|
+
const before = this.__shadow.get(targetUri);
|
|
974
|
+
const edits = this.__shadow.computeEdits(targetUri, newText);
|
|
975
|
+
if (edits.length === 0) {
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
// Record the push for echo correlation BEFORE the RPC — the client's
|
|
979
|
+
// echo can race the applyEdit response. See `__pendingPushes`.
|
|
980
|
+
this.recordPendingPush(targetUri, before, newText);
|
|
981
|
+
try {
|
|
982
|
+
// Version the push only when it is position-DEPENDENT. A full-range
|
|
983
|
+
// replace lands correctly on any buffer, so gating it would turn a
|
|
984
|
+
// stale-by-one version into a refused update for no safety gain — and
|
|
985
|
+
// it is exactly what the caller retries with after a rejection, so
|
|
986
|
+
// gating it there would refuse the recovery too.
|
|
987
|
+
const version = isFullReplace(edits) ? UNKNOWN_CLIENT_VERSION : this.languageClientVersion(uri);
|
|
988
|
+
const result = await connection.workspace.applyEdit({
|
|
989
|
+
label: options?.label,
|
|
990
|
+
documentChanges: [TextDocumentEdit.create(OptionalVersionedTextDocumentIdentifier.create(targetUri, version), edits)]
|
|
991
|
+
});
|
|
992
|
+
if (result && result.applied === false) {
|
|
993
|
+
this.__shadow.invalidate(targetUri);
|
|
994
|
+
this.__pendingPushes.delete(targetUri);
|
|
995
|
+
}
|
|
996
|
+
lastResult = result;
|
|
997
|
+
} catch (err) {
|
|
998
|
+
this.__shadow.invalidate(targetUri);
|
|
999
|
+
this.__pendingPushes.delete(targetUri);
|
|
1000
|
+
throw err;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
return lastResult;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* The version the LSP textual language client last declared for `uri`, for
|
|
1008
|
+
* addressing an outgoing `workspace/applyEdit`.
|
|
1009
|
+
*
|
|
1010
|
+
* Client version ids are CLIENT-owned per LSP, so this is the id the client
|
|
1011
|
+
* itself stamped on its last `didOpen` / `didChange` — never the shared
|
|
1012
|
+
* server version, which advances on authored writes the client knows nothing
|
|
1013
|
+
* about and would therefore reject every push.
|
|
1014
|
+
*
|
|
1015
|
+
* Falls back to {@link UNKNOWN_CLIENT_VERSION} when the client has never
|
|
1016
|
+
* declared one, which is the honest answer for a document it has not opened.
|
|
1017
|
+
* That is also the case in which there is no shadow, so the push is already a
|
|
1018
|
+
* position-independent full replace and has nothing to gain from a gate.
|
|
1019
|
+
*/
|
|
1020
|
+
protected languageClientVersion(uri: DocumentUri): number {
|
|
1021
|
+
return this.__documents.get(this.documentKey(uri))?.clientVersions.get(LANGUAGE_CLIENT_ID) ?? UNKNOWN_CLIENT_VERSION;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Append a push to the in-flight queue for `targetUri`
|
|
1026
|
+
* (see {@link __pendingPushes}). Bounded: beyond
|
|
1027
|
+
* {@link PENDING_ECHO_CAP} the oldest entry drops with a debug log — an
|
|
1028
|
+
* echo that far outstanding means the client is not echoing at all, and
|
|
1029
|
+
* an unbounded queue must not become the leak.
|
|
1030
|
+
*/
|
|
1031
|
+
protected recordPendingPush(targetUri: LanguageClientUri, before: string | undefined, newText: string): void {
|
|
1032
|
+
let pending = this.__pendingPushes.get(targetUri);
|
|
1033
|
+
if (!pending) {
|
|
1034
|
+
pending = [];
|
|
1035
|
+
this.__pendingPushes.set(targetUri, pending);
|
|
1036
|
+
}
|
|
1037
|
+
pending.push({ before, afterHash: contentHash(newText) });
|
|
1038
|
+
if (pending.length > PENDING_ECHO_CAP) {
|
|
1039
|
+
pending.shift();
|
|
1040
|
+
this.logUri(targetUri, `Pending-echo queue exceeded ${PENDING_ECHO_CAP} entries; dropped the oldest`, 'debug');
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Decide what an incoming language-client change actually is, by
|
|
1046
|
+
* reconstructing the client's resulting buffer against the OLDEST push still
|
|
1047
|
+
* in flight for `clientFacing`.
|
|
1048
|
+
*
|
|
1049
|
+
* `undefined` when nothing of ours is in flight, which is the ordinary path:
|
|
1050
|
+
* the client's ranges then address the synced text and the caller applies
|
|
1051
|
+
* them to it directly.
|
|
1052
|
+
*
|
|
1053
|
+
* **Why the oldest, and why reconstruct at all.** The client applies our
|
|
1054
|
+
* pushes in order and echoes each against the buffer it held before that
|
|
1055
|
+
* push, so the first echo to arrive belongs to the oldest entry — a FIFO
|
|
1056
|
+
* correspondence the queue preserves by consuming from the front. Comparing
|
|
1057
|
+
* the reconstruction against every pending hash, not only the oldest, is
|
|
1058
|
+
* what recognises an echo that a newer write already superseded: a rapid
|
|
1059
|
+
* write sequence can deliver the echo of push N after the store applied
|
|
1060
|
+
* push N+1, and everything older is then accounted for too.
|
|
1061
|
+
*
|
|
1062
|
+
* A reconstruction matching NO pending push means the client's buffer went
|
|
1063
|
+
* somewhere we did not send it — it coalesced a keystroke into the echo, or
|
|
1064
|
+
* typed before the push landed. That text is authoritative, and the queue
|
|
1065
|
+
* drops: the client has stopped being a pure mirror, so no outstanding echo
|
|
1066
|
+
* can match again. (A push still in flight at that point will be refused by
|
|
1067
|
+
* the client's own version gate, which invalidates the shadow and makes the
|
|
1068
|
+
* next sync a position-independent full replace.)
|
|
1069
|
+
*
|
|
1070
|
+
* Content equality is a sound echo proof because entries live only between a
|
|
1071
|
+
* push and its echo — a milliseconds window, never history — and the
|
|
1072
|
+
* client's `didChange` stream is ordered, so every buffer state arrives in
|
|
1073
|
+
* mutation order. An UNDO returning the buffer to previously-pushed text is
|
|
1074
|
+
* therefore never swallowed: the edit that preceded it already emptied the
|
|
1075
|
+
* queue, so undo revisits PAST states while the queue holds IN-FLIGHT ones.
|
|
1076
|
+
*/
|
|
1077
|
+
protected classifyLanguageClientChange(
|
|
1078
|
+
clientFacing: LanguageClientUri,
|
|
1079
|
+
document: T,
|
|
1080
|
+
changes: TextDocumentContentChangeEvent[]
|
|
1081
|
+
): LanguageClientChangeOrigin | undefined {
|
|
1082
|
+
const pending = this.__pendingPushes.get(clientFacing);
|
|
1083
|
+
if (pending === undefined || pending.length === 0) {
|
|
1084
|
+
return undefined;
|
|
1085
|
+
}
|
|
1086
|
+
// Through the configured factories, not `TextDocument` directly, so an
|
|
1087
|
+
// adopter's custom text-document type governs how the ranges are applied
|
|
1088
|
+
// here exactly as it does on the synced document.
|
|
1089
|
+
const probe = this.create(clientFacing, document.languageId, 0, pending[0].before ?? document.getText());
|
|
1090
|
+
const clientText = this.update(probe, changes, 0).getText();
|
|
1091
|
+
const matchIndex = pending.findIndex(push => push.afterHash === contentHash(clientText));
|
|
1092
|
+
if (matchIndex >= 0) {
|
|
1093
|
+
pending.splice(0, matchIndex + 1);
|
|
1094
|
+
return { kind: 'echo' };
|
|
1095
|
+
}
|
|
1096
|
+
pending.length = 0;
|
|
1097
|
+
return { kind: 'divergent', text: clientText };
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* Explicitly baseline the language-client text shadow for a URI. Useful in
|
|
1102
|
+
* tests and for adopters that need to seed the shadow without going through
|
|
1103
|
+
* a `didOpen` event (e.g. after a sideband save). Normal didOpen / didChange
|
|
1104
|
+
* paths from the LSP language client already auto-track the shadow.
|
|
1105
|
+
*/
|
|
1106
|
+
setLanguageClientText(uri: DocumentUri, text: string): void {
|
|
1107
|
+
const clientFacing = this.toLanguageClientUri(uri);
|
|
1108
|
+
this.__shadow.set(clientFacing, text);
|
|
1109
|
+
// An explicit rebaseline supersedes whatever was in flight.
|
|
1110
|
+
this.__pendingPushes.delete(clientFacing);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/** Drop the shadow baseline for a URI; the next applyEditToLanguageClient sends a full replace. */
|
|
1114
|
+
invalidateLanguageClientText(uri: DocumentUri): void {
|
|
1115
|
+
const clientFacing = this.toLanguageClientUri(uri);
|
|
1116
|
+
this.__shadow.invalidate(clientFacing);
|
|
1117
|
+
this.__pendingPushes.delete(clientFacing);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
protected consumePendingContent(uri: DocumentUri): string | undefined {
|
|
1121
|
+
const record = this.__documents.get(this.documentKey(uri));
|
|
1122
|
+
const content = record?.pendingContent;
|
|
1123
|
+
if (record) {
|
|
1124
|
+
record.pendingContent = undefined;
|
|
1125
|
+
}
|
|
1126
|
+
return content;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
protected log(uri: DocumentUri, message: string): void {
|
|
1130
|
+
this.logUri(uri, message, 'info');
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/** Hook for subclasses that want to attach a workspace-relative path component. */
|
|
1134
|
+
protected logUri(uri: DocumentUri, message: string, level: 'info' | 'debug' = 'info'): void {
|
|
1135
|
+
this.tracer.with(uri)[level](message);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/** Hook for subclasses that want a URI-tagged timer. */
|
|
1139
|
+
protected startTimerForUri(uri: DocumentUri, message: string): { dispose(): void } {
|
|
1140
|
+
return this.tracer.with(uri).startTimer(message);
|
|
1141
|
+
}
|
|
1142
|
+
}
|