@hydranium/core 1.0.0-next.8 → 1.0.0-next.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/documents/ast-document-manager.d.ts +75 -16
- package/lib/documents/ast-document-manager.d.ts.map +1 -1
- package/lib/documents/ast-document-manager.js +57 -19
- package/lib/documents/ast-document-manager.js.map +1 -1
- package/lib/documents/client-ids.d.ts +5 -19
- package/lib/documents/client-ids.d.ts.map +1 -1
- package/lib/documents/client-ids.js +5 -19
- package/lib/documents/client-ids.js.map +1 -1
- package/lib/documents/hydranium-text-documents.d.ts +55 -21
- package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
- package/lib/documents/hydranium-text-documents.js +142 -46
- package/lib/documents/hydranium-text-documents.js.map +1 -1
- package/lib/documents/language-client-text-shadow.d.ts +41 -9
- package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
- package/lib/documents/language-client-text-shadow.js +56 -11
- package/lib/documents/language-client-text-shadow.js.map +1 -1
- package/lib/documents/self-save-registry.d.ts +37 -6
- package/lib/documents/self-save-registry.d.ts.map +1 -1
- package/lib/documents/self-save-registry.js +28 -15
- package/lib/documents/self-save-registry.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -0
- package/lib/index.js.map +1 -1
- package/lib/langium/ast-extension/ast-node-builder.d.ts +32 -9
- package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
- package/lib/langium/ast-extension/ast-node-builder.js +60 -22
- package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
- package/lib/langium/bootstrap.d.ts.map +1 -1
- package/lib/langium/bootstrap.js +16 -9
- package/lib/langium/bootstrap.js.map +1 -1
- package/lib/langium/config/configuration-provider.d.ts +81 -0
- package/lib/langium/config/configuration-provider.d.ts.map +1 -0
- package/lib/langium/config/configuration-provider.js +112 -0
- package/lib/langium/config/configuration-provider.js.map +1 -0
- package/lib/langium/config/index.d.ts +1 -0
- package/lib/langium/config/index.d.ts.map +1 -1
- package/lib/langium/config/index.js +1 -0
- package/lib/langium/config/index.js.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.d.ts +17 -0
- package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
- package/lib/langium/diagnostics/lsp-logger.js +40 -6
- package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
- package/lib/langium/document-builder/build-pipeline-integration.d.ts +13 -1
- package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
- package/lib/langium/document-builder/build-pipeline-integration.js +1 -1
- package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
- package/lib/langium/document-builder/build-session.d.ts +93 -0
- package/lib/langium/document-builder/build-session.d.ts.map +1 -0
- package/lib/langium/document-builder/build-session.js +72 -0
- package/lib/langium/document-builder/build-session.js.map +1 -0
- package/lib/langium/document-builder/document-builder.d.ts +150 -7
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +298 -11
- package/lib/langium/document-builder/document-builder.js.map +1 -1
- package/lib/langium/document-builder/index.d.ts +1 -0
- package/lib/langium/document-builder/index.d.ts.map +1 -1
- package/lib/langium/document-builder/index.js +1 -0
- package/lib/langium/document-builder/index.js.map +1 -1
- package/lib/langium/integration-services.d.ts +45 -1
- package/lib/langium/integration-services.d.ts.map +1 -1
- package/lib/langium/integration-services.js +10 -5
- package/lib/langium/integration-services.js.map +1 -1
- package/lib/langium/integrity/integrity-service.d.ts +5 -0
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-service.js +34 -1
- package/lib/langium/integrity/integrity-service.js.map +1 -1
- package/lib/langium/keys/containment.d.ts +64 -0
- package/lib/langium/keys/containment.d.ts.map +1 -0
- package/lib/langium/keys/containment.js +53 -0
- package/lib/langium/keys/containment.js.map +1 -0
- package/lib/langium/keys/index.d.ts +1 -0
- package/lib/langium/keys/index.d.ts.map +1 -1
- package/lib/langium/keys/index.js +1 -0
- package/lib/langium/keys/index.js.map +1 -1
- package/lib/langium/keys/name-based-key-provider.d.ts +4 -0
- package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
- package/lib/langium/keys/name-based-key-provider.js +4 -0
- package/lib/langium/keys/name-based-key-provider.js.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +149 -10
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +120 -93
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/module.d.ts +84 -15
- package/lib/langium/module.d.ts.map +1 -1
- package/lib/langium/module.js +26 -12
- package/lib/langium/module.js.map +1 -1
- package/lib/langium/naming/name-provider.d.ts +25 -4
- package/lib/langium/naming/name-provider.d.ts.map +1 -1
- package/lib/langium/naming/name-provider.js +2 -1
- package/lib/langium/naming/name-provider.js.map +1 -1
- package/lib/langium/naming/name-separator-validation.d.ts +23 -0
- package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
- package/lib/langium/naming/name-separator-validation.js +30 -1
- package/lib/langium/naming/name-separator-validation.js.map +1 -1
- package/lib/langium/residency/cst-residency-service.d.ts +31 -9
- package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
- package/lib/langium/residency/cst-residency-service.js +13 -56
- package/lib/langium/residency/cst-residency-service.js.map +1 -1
- package/lib/langium/scope/hydranium-scope-provider.d.ts +32 -13
- package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
- package/lib/langium/scope/hydranium-scope-provider.js +37 -20
- package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
- package/lib/langium/service-registry.d.ts +5 -1
- package/lib/langium/service-registry.d.ts.map +1 -1
- package/lib/langium/service-registry.js +4 -0
- package/lib/langium/service-registry.js.map +1 -1
- package/lib/langium/shared-services.d.ts +15 -2
- package/lib/langium/shared-services.d.ts.map +1 -1
- package/lib/langium/shared-services.js.map +1 -1
- package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
- package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
- package/lib/langium/transfer/transfer-encoder.js +38 -16
- package/lib/langium/transfer/transfer-encoder.js.map +1 -1
- package/lib/langium/validation/document-validator.d.ts +294 -10
- package/lib/langium/validation/document-validator.d.ts.map +1 -1
- package/lib/langium/validation/document-validator.js +448 -5
- package/lib/langium/validation/document-validator.js.map +1 -1
- package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
- package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
- package/lib/langium/workspace/document-uri-policy.js +3 -4
- package/lib/langium/workspace/document-uri-policy.js.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
- package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts +8 -0
- package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
- package/lib/langium/workspace/in-memory-file-system-provider.js +11 -2
- package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
- package/lib/langium/workspace/initialize-workspace.d.ts +22 -2
- package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
- package/lib/langium/workspace/initialize-workspace.js +13 -5
- package/lib/langium/workspace/initialize-workspace.js.map +1 -1
- package/lib/langium/workspace/langium-documents.d.ts +79 -17
- package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
- package/lib/langium/workspace/langium-documents.js +83 -28
- package/lib/langium/workspace/langium-documents.js.map +1 -1
- package/lib/locale/index.d.ts +10 -0
- package/lib/locale/index.d.ts.map +1 -0
- package/lib/locale/index.js +10 -0
- package/lib/locale/index.js.map +1 -0
- package/lib/locale/server-locale.d.ts +73 -0
- package/lib/locale/server-locale.d.ts.map +1 -0
- package/lib/locale/server-locale.js +61 -0
- package/lib/locale/server-locale.js.map +1 -0
- package/lib/lsp/hydranium-document-update-handler.js +1 -1
- package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
- package/lib/lsp/index.d.ts +1 -1
- package/lib/lsp/index.d.ts.map +1 -1
- package/lib/lsp/index.js +1 -1
- package/lib/lsp/index.js.map +1 -1
- package/lib/lsp/lsp-latency.d.ts +39 -0
- package/lib/lsp/lsp-latency.d.ts.map +1 -0
- package/lib/lsp/lsp-latency.js +58 -0
- package/lib/lsp/lsp-latency.js.map +1 -0
- package/lib/lsp/semantic-token-provider.d.ts +34 -1
- package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
- package/lib/lsp/semantic-token-provider.js +55 -8
- package/lib/lsp/semantic-token-provider.js.map +1 -1
- package/lib/messages/carriers.d.ts +54 -0
- package/lib/messages/carriers.d.ts.map +1 -0
- package/lib/messages/carriers.js +62 -0
- package/lib/messages/carriers.js.map +1 -0
- package/lib/messages/index.d.ts +25 -0
- package/lib/messages/index.d.ts.map +1 -0
- package/lib/messages/index.js +25 -0
- package/lib/messages/index.js.map +1 -0
- package/lib/messages/renderer.d.ts +136 -0
- package/lib/messages/renderer.d.ts.map +1 -0
- package/lib/messages/renderer.js +159 -0
- package/lib/messages/renderer.js.map +1 -0
- package/lib/node/heap-ceiling.d.ts +61 -0
- package/lib/node/heap-ceiling.d.ts.map +1 -0
- package/lib/node/heap-ceiling.js +72 -0
- package/lib/node/heap-ceiling.js.map +1 -0
- package/lib/node/index.d.ts +1 -0
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/index.js +1 -0
- package/lib/node/index.js.map +1 -1
- package/lib/node/latency-from-env.d.ts +1 -1
- package/lib/node/latency-from-env.js +1 -1
- package/lib/node/node-file-system-provider.d.ts.map +1 -1
- package/lib/node/node-file-system-provider.js +1 -40
- package/lib/node/node-file-system-provider.js.map +1 -1
- package/lib/node/profiling-run.d.ts +2 -2
- package/lib/node/profiling-run.js +1 -1
- package/lib/node/profiling-run.js.map +1 -1
- package/lib/node/rename-over-open-readers.d.ts +52 -0
- package/lib/node/rename-over-open-readers.d.ts.map +1 -0
- package/lib/node/rename-over-open-readers.js +66 -0
- package/lib/node/rename-over-open-readers.js.map +1 -0
- package/lib/testing/fake-document.d.ts +16 -4
- package/lib/testing/fake-document.d.ts.map +1 -1
- package/lib/testing/fake-document.js +18 -3
- package/lib/testing/fake-document.js.map +1 -1
- package/lib/testing/make-noop-shared-services.d.ts +14 -0
- package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
- package/lib/testing/make-noop-shared-services.js +28 -2
- package/lib/testing/make-noop-shared-services.js.map +1 -1
- package/lib/testing/make-test-services.d.ts +44 -8
- package/lib/testing/make-test-services.d.ts.map +1 -1
- package/lib/testing/make-test-services.js +19 -3
- package/lib/testing/make-test-services.js.map +1 -1
- package/lib/testing/node/spawned-server.d.ts +14 -0
- package/lib/testing/node/spawned-server.d.ts.map +1 -1
- package/lib/testing/node/spawned-server.js +33 -1
- package/lib/testing/node/spawned-server.js.map +1 -1
- package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
- package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
- package/lib/testing/playwright/flaky-network-proxy.js +164 -0
- package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
- package/lib/testing/playwright/index.d.ts +1 -0
- package/lib/testing/playwright/index.d.ts.map +1 -1
- package/lib/testing/playwright/index.js +1 -0
- package/lib/testing/playwright/index.js.map +1 -1
- package/lib/testing/playwright/server-log-capture.d.ts +14 -4
- package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-capture.js +21 -4
- package/lib/testing/playwright/server-log-capture.js.map +1 -1
- package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
- package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
- package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
- package/lib/testing/stub-ast-document-manager.d.ts +5 -5
- package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
- package/lib/testing/stub-ast-document-manager.js +4 -6
- package/lib/testing/stub-ast-document-manager.js.map +1 -1
- package/lib/testing/stub-document-builder.d.ts +8 -0
- package/lib/testing/stub-document-builder.d.ts.map +1 -1
- package/lib/testing/stub-document-builder.js +22 -3
- package/lib/testing/stub-document-builder.js.map +1 -1
- package/lib/testing/stub-langium-documents.d.ts +3 -2
- package/lib/testing/stub-langium-documents.d.ts.map +1 -1
- package/lib/testing/stub-langium-documents.js.map +1 -1
- package/lib/testing/stub-model-service.d.ts +5 -4
- package/lib/testing/stub-model-service.d.ts.map +1 -1
- package/lib/testing/stub-model-service.js +2 -2
- package/lib/testing/stub-model-service.js.map +1 -1
- package/package.json +17 -8
- package/src/documents/ast-document-manager.ts +129 -36
- package/src/documents/client-ids.ts +5 -22
- package/src/documents/hydranium-text-documents.ts +167 -61
- package/src/documents/language-client-text-shadow.ts +59 -11
- package/src/documents/self-save-registry.ts +52 -13
- package/src/index.ts +5 -0
- package/src/langium/ast-extension/ast-node-builder.ts +66 -22
- package/src/langium/bootstrap.ts +16 -9
- package/src/langium/config/configuration-provider.ts +119 -0
- package/src/langium/config/index.ts +1 -0
- package/src/langium/diagnostics/lsp-logger.ts +41 -6
- package/src/langium/document-builder/build-pipeline-integration.ts +14 -1
- package/src/langium/document-builder/build-session.ts +87 -0
- package/src/langium/document-builder/document-builder.ts +339 -11
- package/src/langium/document-builder/index.ts +1 -0
- package/src/langium/integration-services.ts +67 -6
- package/src/langium/integrity/integrity-service.ts +35 -1
- package/src/langium/keys/containment.ts +87 -0
- package/src/langium/keys/index.ts +1 -0
- package/src/langium/keys/name-based-key-provider.ts +4 -0
- package/src/langium/model-service/model-service.ts +211 -30
- package/src/langium/module.ts +106 -21
- package/src/langium/naming/name-provider.ts +27 -5
- package/src/langium/naming/name-separator-validation.ts +34 -5
- package/src/langium/residency/cst-residency-service.ts +36 -12
- package/src/langium/scope/hydranium-scope-provider.ts +40 -22
- package/src/langium/service-registry.ts +6 -2
- package/src/langium/shared-services.ts +19 -2
- package/src/langium/transfer/transfer-encoder.ts +85 -24
- package/src/langium/validation/document-validator.ts +537 -11
- package/src/langium/workspace/document-uri-policy.ts +3 -4
- package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
- package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
- package/src/langium/workspace/in-memory-file-system-provider.ts +13 -3
- package/src/langium/workspace/initialize-workspace.ts +34 -5
- package/src/langium/workspace/langium-documents.ts +106 -33
- package/src/locale/index.ts +10 -0
- package/src/locale/server-locale.ts +86 -0
- package/src/lsp/hydranium-document-update-handler.ts +1 -1
- package/src/lsp/index.ts +1 -1
- package/src/lsp/lsp-latency.ts +62 -0
- package/src/lsp/semantic-token-provider.ts +74 -7
- package/src/messages/carriers.ts +78 -0
- package/src/messages/index.ts +36 -0
- package/src/messages/renderer.ts +204 -0
- package/src/node/heap-ceiling.ts +108 -0
- package/src/node/index.ts +1 -0
- package/src/node/latency-from-env.ts +1 -1
- package/src/node/node-file-system-provider.ts +1 -41
- package/src/node/profiling-run.ts +3 -3
- package/src/node/rename-over-open-readers.ts +78 -0
- package/src/testing/fake-document.ts +24 -5
- package/src/testing/make-noop-shared-services.ts +50 -2
- package/src/testing/make-test-services.ts +73 -18
- package/src/testing/node/spawned-server.ts +35 -1
- package/src/testing/playwright/flaky-network-proxy.ts +240 -0
- package/src/testing/playwright/index.ts +1 -0
- package/src/testing/playwright/server-log-capture.ts +24 -5
- package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
- package/src/testing/stub-ast-document-manager.ts +10 -14
- package/src/testing/stub-document-builder.ts +32 -3
- package/src/testing/stub-langium-documents.ts +3 -2
- package/src/testing/stub-model-service.ts +7 -6
- package/lib/lsp/instrument-connection.d.ts +0 -36
- package/lib/lsp/instrument-connection.d.ts.map +0 -1
- package/lib/lsp/instrument-connection.js +0 -62
- package/lib/lsp/instrument-connection.js.map +0 -1
- package/src/lsp/instrument-connection.ts +0 -66
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Containment-array maintenance for callers that build or delete AST nodes
|
|
12
|
+
* programmatically — a GLSP operation handler being the usual one.
|
|
13
|
+
*
|
|
14
|
+
* **These live beside the key provider because the constraint is the key
|
|
15
|
+
* provider's.** `NameBasedKeyProvider` keys a node the `NameProvider` gives no
|
|
16
|
+
* name from its position, as `` `${$containerProperty}@${$containerIndex}` ``,
|
|
17
|
+
* so a node appended with a bare `array.push` carries neither and
|
|
18
|
+
* `getElementKey` answers `undefined` — the element has no key at all, and the
|
|
19
|
+
* GLSP index cannot address it. Deleting from the middle of such a list must
|
|
20
|
+
* renumber the survivors, or an id derived later in the same command names the
|
|
21
|
+
* wrong node. Shipping the constraint's statement without the code that
|
|
22
|
+
* discharges it leaves every adopter in that position re-deriving thirty lines
|
|
23
|
+
* from prose, and both failures are silent.
|
|
24
|
+
*
|
|
25
|
+
* The population is narrower than "anyone creating elements from a diagram": it
|
|
26
|
+
* takes a grammar with UNNAMED element types, programmatic creation or deletion
|
|
27
|
+
* of them, and this key provider. A grammar whose diagram elements are all
|
|
28
|
+
* named never reaches it, and the two ways out are named on the provider —
|
|
29
|
+
* give the type a name, or bind a provider whose fallback is content-derived.
|
|
30
|
+
*
|
|
31
|
+
* **Free functions, and deliberately not a step on the AST builder.** Building a
|
|
32
|
+
* node detached and appending it later is legitimate, so a containment step
|
|
33
|
+
* cannot be mandatory; and an optional fluent step could not check the index
|
|
34
|
+
* against the array unless it were handed the array, at which point it is these
|
|
35
|
+
* signatures with more ceremony. `AstNodeInit` already accepts the three fields
|
|
36
|
+
* for a caller who has them, which is the case these cover: it is the INDEX that
|
|
37
|
+
* cannot be known without reading the array.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import type { AstNode } from '@hydranium/langium';
|
|
41
|
+
import type { Mutable } from '@hydranium/protocol';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Append `child` to `children`, stamping the Langium containment plumbing
|
|
45
|
+
* (`$container`, `$containerProperty`, `$containerIndex`) so a positional key
|
|
46
|
+
* can address it.
|
|
47
|
+
*
|
|
48
|
+
* `property` and `children` are separate parameters rather than one property
|
|
49
|
+
* name the function dereferences: the array is what the index is derived from,
|
|
50
|
+
* so taking it directly is what makes the two agree by construction instead of
|
|
51
|
+
* by a lookup that a typo could point elsewhere.
|
|
52
|
+
*
|
|
53
|
+
* @returns `child`, so a caller can append and use it in one expression.
|
|
54
|
+
*/
|
|
55
|
+
export function appendChild<TChild extends AstNode>(container: AstNode, property: string, children: TChild[], child: TChild): TChild {
|
|
56
|
+
const mutable = child as Mutable<TChild>;
|
|
57
|
+
mutable.$container = container;
|
|
58
|
+
mutable.$containerProperty = property;
|
|
59
|
+
mutable.$containerIndex = children.length;
|
|
60
|
+
children.push(child);
|
|
61
|
+
return child;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Remove every entry of `children` that is in `toRemove`, then renumber the
|
|
66
|
+
* survivors' `$containerIndex`.
|
|
67
|
+
*
|
|
68
|
+
* Mutates `children` in place rather than returning a new array, because the
|
|
69
|
+
* caller's array IS the AST's containment list — replacing the reference would
|
|
70
|
+
* leave the parent pointing at the old one.
|
|
71
|
+
*
|
|
72
|
+
* @returns how many entries were removed, so a caller can tell a no-op delete
|
|
73
|
+
* from one that changed the model without comparing lengths itself.
|
|
74
|
+
*/
|
|
75
|
+
export function removeChildren<TChild extends AstNode>(children: TChild[], toRemove: ReadonlySet<TChild>): number {
|
|
76
|
+
if (toRemove.size === 0) {
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
const survivors = children.filter(child => !toRemove.has(child));
|
|
80
|
+
const removed = children.length - survivors.length;
|
|
81
|
+
children.length = 0;
|
|
82
|
+
for (const [index, child] of survivors.entries()) {
|
|
83
|
+
(child as Mutable<TChild>).$containerIndex = index;
|
|
84
|
+
children.push(child);
|
|
85
|
+
}
|
|
86
|
+
return removed;
|
|
87
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
+
export * from './containment.js';
|
|
10
11
|
export * from './element-key-provider.js';
|
|
11
12
|
export * from './name-based-key-provider.js';
|
|
12
13
|
export * from './positional-key-provider.js';
|
|
@@ -55,6 +55,10 @@ import { type NameProvider } from '../naming/name-provider.js';
|
|
|
55
55
|
* - After a removal, surviving siblings need their `$containerIndex`
|
|
56
56
|
* renumbered, or a key derived afterwards addresses the wrong node.
|
|
57
57
|
*
|
|
58
|
+
* {@link appendChild} and {@link removeChildren} discharge both, and ship
|
|
59
|
+
* beside this provider because the constraint is this provider's rather than
|
|
60
|
+
* any caller's.
|
|
61
|
+
*
|
|
58
62
|
* Adopters that need insert / delete stability for unnamed types should
|
|
59
63
|
* give those types a name in the grammar, or bind a key provider whose
|
|
60
64
|
* fallback is content-derived rather than positional.
|
|
@@ -11,11 +11,12 @@ import {
|
|
|
11
11
|
type CanonicalUri,
|
|
12
12
|
type CloseModelArgs,
|
|
13
13
|
ConflictError,
|
|
14
|
+
isSnapshotVersion,
|
|
15
|
+
defineMessage,
|
|
14
16
|
Logger,
|
|
15
17
|
type MaybeObservableValue,
|
|
16
18
|
type MaybePromise,
|
|
17
19
|
ObservableValue,
|
|
18
|
-
type TransferDiagnostic,
|
|
19
20
|
type TransferElement,
|
|
20
21
|
type OpenModelArgs,
|
|
21
22
|
type Tracer,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
type TransferUpdateArgs
|
|
24
25
|
} from '@hydranium/protocol';
|
|
25
26
|
import { type AstNode, DocumentState, type LangiumDocument, UriUtils, type URI } from '@hydranium/langium';
|
|
27
|
+
import { type AstDiagnostic } from '../validation/document-validator.js';
|
|
26
28
|
import { type DocumentUriPolicy } from '../workspace/document-uri-policy.js';
|
|
27
29
|
import { ReentrantWriteLockError, isInsideWriteLock } from '../workspace/write-lock-scope.js';
|
|
28
30
|
import { type CancellationToken, type Disposable } from 'vscode-languageserver';
|
|
@@ -34,6 +36,23 @@ import { labelPhaseListener } from '../document-builder/labeled-phase-listener.j
|
|
|
34
36
|
import { LANGUAGE_CLIENT_ID } from '../../documents/client-ids.js';
|
|
35
37
|
import { type ServerSharedServices } from '../module.js';
|
|
36
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The undo-stack entry for a server-authored write pushed to the editor.
|
|
41
|
+
*
|
|
42
|
+
* **A user-facing LABEL, not a log string**, which is easy to miss because it
|
|
43
|
+
* travels as an options field rather than as a message: LSP specifies
|
|
44
|
+
* `ApplyWorkspaceEditParams.label` as "presented in the user interface for
|
|
45
|
+
* example on an undo stack to undo the workspace edit". So a user who edits
|
|
46
|
+
* through a form or drags a diagram node reads this in their editor's undo menu
|
|
47
|
+
* — which is why it is rendered like any other message the server sends rather
|
|
48
|
+
* than left as the English literal it was.
|
|
49
|
+
*
|
|
50
|
+
* Parameterless deliberately. The obvious improvement is to name the document,
|
|
51
|
+
* and it is the wrong one: an undo menu is already grouped under the file, so
|
|
52
|
+
* the URI would be noise in the one place it is redundant.
|
|
53
|
+
*/
|
|
54
|
+
export const MODEL_UPDATE_EDIT = defineMessage('hydranium/core/model-update-edit', 'Update Model');
|
|
55
|
+
|
|
37
56
|
/** Max time {@link ModelService.settleSave} waits for the build to settle and the sync chain to drain. */
|
|
38
57
|
const SAVE_SETTLE_TIMEOUT_MS = 10_000;
|
|
39
58
|
|
|
@@ -176,15 +195,91 @@ export interface ModelServiceOptions extends LogNameOptions {
|
|
|
176
195
|
* **Generic parameters.**
|
|
177
196
|
* - `TAst` — the AST root type each consumer expects on the returned
|
|
178
197
|
* {@link AstDocument}. Constrained to {@link AstNode}.
|
|
179
|
-
* - `TDiagnostic` —
|
|
180
|
-
* `
|
|
198
|
+
* - `TDiagnostic` — the AST-layer diagnostic: whatever the build left on
|
|
199
|
+
* `LangiumDocument.diagnostics`, carried through on the returned
|
|
200
|
+
* {@link AstDocument}. Defaults to {@link AstDiagnostic}.
|
|
201
|
+
* **Not the `TransferEncoder`'s parameter of the same name**, which is
|
|
202
|
+
* that encoder's OUTPUT and so names the wire shape. This one names its
|
|
203
|
+
* input, and an adopter binds the two to different types.
|
|
181
204
|
* - `TTransfer` — transfer-model root accepted by `update` / `save`
|
|
182
205
|
* args. Constrained to {@link TransferElement}. Defaults to the
|
|
183
206
|
* structural base.
|
|
184
207
|
*/
|
|
185
|
-
|
|
208
|
+
/**
|
|
209
|
+
* The seam every non-LSP head talks to: the data server, the GLSP head and an
|
|
210
|
+
* adopter's own services reach documents through this slot rather than through
|
|
211
|
+
* the workspace stores.
|
|
212
|
+
*
|
|
213
|
+
* **Two families, and the distinction matters more than the names suggest.**
|
|
214
|
+
* `waitFor*` is a pure wait — it never triggers a build, so a caller waiting on
|
|
215
|
+
* a document no build has touched waits until something else builds it.
|
|
216
|
+
* `ensureDocumentState` and the phase shorthands over it *dispatch*: warm
|
|
217
|
+
* documents are awaited, cold ones are built.
|
|
218
|
+
*
|
|
219
|
+
* **Diagnostics are typed `never` below `Validated`.** Validation is the last
|
|
220
|
+
* phase, so at any earlier landmark the array either is not yet computed or
|
|
221
|
+
* still holds the previous build's, and reading it would take stale results
|
|
222
|
+
* for fresh ones. A caller that needs diagnostics asks for {@link validated}.
|
|
223
|
+
*
|
|
224
|
+
* What the `never` buys, exactly: reading a field off an element is a compile
|
|
225
|
+
* error, and nothing can be appended. It does NOT stop a caller assigning an
|
|
226
|
+
* element to a typed variable, because `never` is assignable to everything — so
|
|
227
|
+
* this is a guard against reaching for diagnostics by accident, not a seal
|
|
228
|
+
* against doing it deliberately.
|
|
229
|
+
*
|
|
230
|
+
* The waits resolve at or ABOVE their target, so an already-validated document
|
|
231
|
+
* does carry usable diagnostics and the `never` over-forbids there. That
|
|
232
|
+
* direction is the safe one: the alternative permits stale reads silently. A
|
|
233
|
+
* member taking a phase as a PARAMETER cannot judge statically and so returns
|
|
234
|
+
* `TDiagnostic`, leaving the choice to the caller.
|
|
235
|
+
*/
|
|
236
|
+
export interface ModelService<
|
|
237
|
+
TAst extends AstNode,
|
|
238
|
+
TDiagnostic extends AstDiagnostic = AstDiagnostic,
|
|
239
|
+
TTransfer extends TransferElement = TransferElement
|
|
240
|
+
> {
|
|
241
|
+
/**
|
|
242
|
+
* Resolves once the workspace has been initialised and its first build has
|
|
243
|
+
* completed — the gate every read should wait behind, since a document
|
|
244
|
+
* queried before it may be unbuilt and reach no phase.
|
|
245
|
+
*
|
|
246
|
+
* A property rather than a method, matching `ProjectManager.ready` and
|
|
247
|
+
* Langium's `WorkspaceManager.ready`. An implementation needing a stricter
|
|
248
|
+
* gate supplies a Promise that awaits its own concern as well.
|
|
249
|
+
*/
|
|
250
|
+
readonly ready: Promise<void>;
|
|
251
|
+
|
|
252
|
+
// Pure waits — never trigger a build.
|
|
253
|
+
waitForDocumentState(uri: string, state: DocumentState, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
|
|
254
|
+
waitForDocumentSettled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
|
|
255
|
+
waitForBuilderState(state: DocumentState, cancelToken?: CancellationToken): Promise<void>;
|
|
256
|
+
|
|
257
|
+
// Wait if warm, build if cold.
|
|
258
|
+
ensureDocumentState(uri: string, state?: DocumentState, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
|
|
259
|
+
rebuild(uri: string, state?: DocumentState, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
|
|
260
|
+
parsed(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
|
|
261
|
+
linked(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
|
|
262
|
+
settled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
|
|
263
|
+
indexed(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
|
|
264
|
+
validated(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
|
|
265
|
+
|
|
266
|
+
update(args: TransferUpdateArgs<TTransfer>, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
|
|
267
|
+
save(args: TransferSaveArgs<TTransfer>, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
|
|
268
|
+
|
|
269
|
+
open(args: OpenModelArgs): Promise<Disposable>;
|
|
270
|
+
close(args: CloseModelArgs): Promise<void>;
|
|
271
|
+
isOpen(uri: string): boolean;
|
|
272
|
+
snapshot(uri: string): AstDocument<TAst, TDiagnostic> | undefined;
|
|
273
|
+
getDocument(uri: string): LangiumDocument | undefined;
|
|
274
|
+
|
|
275
|
+
onModelUpdated(uri: string, listener: (event: AstDocumentUpdatedEvent<TAst, TDiagnostic>) => void): Disposable;
|
|
276
|
+
onModelSaved(uri: string, listener: (event: AstDocumentSavedEvent<TAst, TDiagnostic>) => void): Disposable;
|
|
277
|
+
onClientClosed(uri: string, clientId: string, listener: () => void): Disposable;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export class DefaultModelService<
|
|
186
281
|
TAst extends AstNode,
|
|
187
|
-
TDiagnostic =
|
|
282
|
+
TDiagnostic extends AstDiagnostic = AstDiagnostic,
|
|
188
283
|
/**
|
|
189
284
|
* Structured payload accepted by `update` / `save`. Constrained to
|
|
190
285
|
* {@link TransferElement} — the minimal `{ readonly $type: string }`
|
|
@@ -195,7 +290,7 @@ export class ModelService<
|
|
|
195
290
|
* integrity service) that pass AST roots remain compatible.
|
|
196
291
|
*/
|
|
197
292
|
TTransfer extends TransferElement = TransferElement
|
|
198
|
-
> {
|
|
293
|
+
> implements ModelService<TAst, TDiagnostic, TTransfer> {
|
|
199
294
|
protected readonly tracer: Tracer;
|
|
200
295
|
/**
|
|
201
296
|
* The single document-identity seam (`services.workspace.DocumentUriPolicy`),
|
|
@@ -329,8 +424,12 @@ export class ModelService<
|
|
|
329
424
|
* {@link waitForDocumentState} for the common "wait until content is stable"
|
|
330
425
|
* case (e.g. settling a save). Pure wait — does not trigger a build.
|
|
331
426
|
*/
|
|
332
|
-
async waitForDocumentSettled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst,
|
|
333
|
-
return this.waitForDocumentStateCanonical(
|
|
427
|
+
async waitForDocumentSettled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
|
|
428
|
+
return (await this.waitForDocumentStateCanonical(
|
|
429
|
+
this.uriPolicy.canonicalUri(uri),
|
|
430
|
+
IntegrityService.SettledState,
|
|
431
|
+
cancelToken
|
|
432
|
+
)) as AstDocument<TAst, never>;
|
|
334
433
|
}
|
|
335
434
|
|
|
336
435
|
/**
|
|
@@ -484,11 +583,19 @@ export class ModelService<
|
|
|
484
583
|
*
|
|
485
584
|
* Phase invariants encoded in the return type:
|
|
486
585
|
* - `parsed` / `linked` / `settled` / `indexed` return
|
|
487
|
-
* `AstDocument<TAst, never>` — diagnostics
|
|
488
|
-
*
|
|
586
|
+
* `AstDocument<TAst, never>` — no diagnostics have been computed at
|
|
587
|
+
* those phases.
|
|
489
588
|
* - `validated` returns `AstDocument<TAst, TDiagnostic>` — diagnostics
|
|
490
589
|
* are populated.
|
|
491
590
|
*
|
|
591
|
+
* **The `never` is enforced, not merely declared.** The wait underneath
|
|
592
|
+
* resolves at or ABOVE the requested state, so a document something else
|
|
593
|
+
* already carried past `Validated` would otherwise come back from
|
|
594
|
+
* `settled()` carrying a full diagnostics array typed `never`; these four
|
|
595
|
+
* strip it. An empty array here therefore means "this read does not report
|
|
596
|
+
* diagnostics", never "this document is clean" — call {@link validated}
|
|
597
|
+
* when the answer has to mean the second.
|
|
598
|
+
*
|
|
492
599
|
* `settled` is the integrity-overlay name for "all integrity rules
|
|
493
600
|
* have fired"; it maps to {@link IntegrityService.SettledState} (which
|
|
494
601
|
* equals `DocumentState.IndexedReferences`), but the dedicated method
|
|
@@ -496,19 +603,19 @@ export class ModelService<
|
|
|
496
603
|
* ever moves.
|
|
497
604
|
*/
|
|
498
605
|
async parsed(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
|
|
499
|
-
return this.ensureDocumentState(uri, DocumentState.Parsed, cancelToken)
|
|
606
|
+
return this.withoutDiagnostics(await this.ensureDocumentState(uri, DocumentState.Parsed, cancelToken));
|
|
500
607
|
}
|
|
501
608
|
|
|
502
609
|
async linked(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
|
|
503
|
-
return this.ensureDocumentState(uri, DocumentState.Linked, cancelToken)
|
|
610
|
+
return this.withoutDiagnostics(await this.ensureDocumentState(uri, DocumentState.Linked, cancelToken));
|
|
504
611
|
}
|
|
505
612
|
|
|
506
613
|
async settled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
|
|
507
|
-
return this.ensureDocumentState(uri, IntegrityService.SettledState, cancelToken)
|
|
614
|
+
return this.withoutDiagnostics(await this.ensureDocumentState(uri, IntegrityService.SettledState, cancelToken));
|
|
508
615
|
}
|
|
509
616
|
|
|
510
617
|
async indexed(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
|
|
511
|
-
return this.ensureDocumentState(uri, DocumentState.IndexedReferences, cancelToken)
|
|
618
|
+
return this.withoutDiagnostics(await this.ensureDocumentState(uri, DocumentState.IndexedReferences, cancelToken));
|
|
512
619
|
}
|
|
513
620
|
|
|
514
621
|
async validated(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>> {
|
|
@@ -598,19 +705,29 @@ export class ModelService<
|
|
|
598
705
|
// created from the payload rather than read from the filesystem — `update`
|
|
599
706
|
// is an upsert. For an already-open document `open` refreshes content (the
|
|
600
707
|
// text is ignored on that branch), so existing-document behaviour is
|
|
601
|
-
// unchanged. `version` is intentionally NOT forwarded to `open
|
|
602
|
-
// create stays at its initial version
|
|
603
|
-
//
|
|
708
|
+
// unchanged. `version` is intentionally NOT forwarded to `open`, so a cold
|
|
709
|
+
// create stays at its initial version rather than adopting a number the
|
|
710
|
+
// caller chose.
|
|
711
|
+
//
|
|
712
|
+
// The gate's version is read BEFORE that open, and must be: for a document
|
|
713
|
+
// no client holds open, the open assigns the shared version from the
|
|
714
|
+
// INCOMING text, so a version read afterwards has already absorbed the
|
|
715
|
+
// caller's own write. Gating on it rejected every modifying write to a
|
|
716
|
+
// closed document, having compared the caller's `basedOn` against a
|
|
717
|
+
// number the caller itself produced — and a serialised round-trip that is
|
|
718
|
+
// not byte-identical to the stored text was enough to trigger it. Reading
|
|
719
|
+
// first keeps both cases the gate exists for: an unknown URI answers 0, so
|
|
720
|
+
// a based-on-version update of a not-yet-existing document still trips it,
|
|
721
|
+
// and a genuine conflict still trips it, another writer having advanced the
|
|
722
|
+
// sequence past the version the caller read.
|
|
723
|
+
const currentVersion = this.services.workspace.TextDocuments.version(uri);
|
|
604
724
|
const text = await run('serialize', () => this.modelToText(uri, args.model, cancelToken));
|
|
605
725
|
await run('open', () => this.open({ uri, clientId: args.clientId, text }));
|
|
606
|
-
if (args.
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
this.tracer.debug(`Conflict on ${uri}: based-on v${args.baseVersion} stale, server at v${current}`);
|
|
612
|
-
throw new ConflictError(uri, args.baseVersion, current);
|
|
613
|
-
}
|
|
726
|
+
if (isSnapshotVersion(args.basedOn) && currentVersion !== args.basedOn) {
|
|
727
|
+
// Distinct from the post-build "superseded" debug line below: this is a
|
|
728
|
+
// based-on-stale rejection (the write never applies), not two writes racing.
|
|
729
|
+
this.tracer.debug(`Conflict on ${uri}: based-on v${args.basedOn} stale, server at v${currentVersion}`);
|
|
730
|
+
throw new ConflictError(uri, args.basedOn, currentVersion);
|
|
614
731
|
}
|
|
615
732
|
const appliedVersion = await run('apply', () => this.services.workspace.AstDocumentManager.update(uri, text, args.clientId));
|
|
616
733
|
// Dispatch through the public `rebuild` (which re-canonicalizes the already-
|
|
@@ -733,6 +850,31 @@ export class ModelService<
|
|
|
733
850
|
return this.services.workspace.AstDocumentManager.isOpen(uri);
|
|
734
851
|
}
|
|
735
852
|
|
|
853
|
+
/**
|
|
854
|
+
* Snapshot of `uri` as it stands RIGHT NOW — the synchronous sibling of the
|
|
855
|
+
* phase reads, which all wait. `undefined` when no document is registered.
|
|
856
|
+
*
|
|
857
|
+
* **This is what a writer wants, and {@link getDocument} is not.** The
|
|
858
|
+
* envelope's `version` is copied by value at projection time, so it cannot
|
|
859
|
+
* move afterwards; a version read off the live document at write time is
|
|
860
|
+
* whatever the server is at *now*, which is the number an optimistic gate is
|
|
861
|
+
* about to compare it against.
|
|
862
|
+
*
|
|
863
|
+
* Diagnostics only from a document that has reached `Validated`, and an
|
|
864
|
+
* empty array otherwise. Unlike the phase reads this one names no phase, so
|
|
865
|
+
* the state it finds is the only thing that can say whether the array
|
|
866
|
+
* describes the content being handed back or whatever an earlier build left.
|
|
867
|
+
* A caller that needs them unconditionally waits, via {@link validated}.
|
|
868
|
+
*/
|
|
869
|
+
snapshot(uri: string): AstDocument<TAst, TDiagnostic> | undefined {
|
|
870
|
+
const document = this.getDocument(uri);
|
|
871
|
+
if (!document) {
|
|
872
|
+
return undefined;
|
|
873
|
+
}
|
|
874
|
+
const envelope = AstDocument.from<TAst, TDiagnostic>(document);
|
|
875
|
+
return document.state >= DocumentState.Validated ? envelope : this.withoutDiagnostics(envelope);
|
|
876
|
+
}
|
|
877
|
+
|
|
736
878
|
/**
|
|
737
879
|
* The built {@link LangiumDocument} for `uri`, looked up by canonical identity —
|
|
738
880
|
* the synchronous, phase-agnostic sibling of {@link ensureDocumentState} /
|
|
@@ -741,6 +883,13 @@ export class ModelService<
|
|
|
741
883
|
* into `LangiumDocuments` directly: a symlinked / `..` / case-divergent URI
|
|
742
884
|
* still resolves to the one document the build keys by its real path. Returns
|
|
743
885
|
* `undefined` if no document is registered for `uri`.
|
|
886
|
+
*
|
|
887
|
+
* **Live, so do not take a based-on version off it.** `textDocument` is the
|
|
888
|
+
* store's own object rather than a copy, so `.version` read here answers for
|
|
889
|
+
* the moment of the READ, not the moment of the earlier content — pass it to
|
|
890
|
+
* a write and the server compares its current version against itself, the
|
|
891
|
+
* gate passes unconditionally, and a concurrent edit is overwritten with
|
|
892
|
+
* nothing logged. Use {@link snapshot} for that, or a phase read.
|
|
744
893
|
*/
|
|
745
894
|
getDocument(uri: string): LangiumDocument | undefined {
|
|
746
895
|
return this.services.workspace.AstDocumentManager.getDocument(uri);
|
|
@@ -834,17 +983,17 @@ export class ModelService<
|
|
|
834
983
|
*
|
|
835
984
|
* The signal is "a known client other than the language client authored this
|
|
836
985
|
* version **and** the URI was in the last build's changed set
|
|
837
|
-
* (`
|
|
986
|
+
* (`isTriggeringEdit`)". A framework-internal rebuild reports no author
|
|
838
987
|
* (`getAuthor` → `undefined`), so it fails `hasKnownAuthor` without comparing
|
|
839
988
|
* against a sentinel. This is NOT redundant with content/registration — it
|
|
840
989
|
* distinguishes "client edited" from "framework rebuilt", which neither the
|
|
841
|
-
* shadow nor `
|
|
990
|
+
* shadow nor `isTriggeringEdit` alone can.
|
|
842
991
|
*/
|
|
843
992
|
protected isNonLanguageClientEdit(document: LangiumDocument): boolean {
|
|
844
993
|
const documents = this.services.workspace.AstDocumentManager;
|
|
845
994
|
const author = documents.getAuthor(document);
|
|
846
995
|
const hasKnownAuthor = !!author && author !== LANGUAGE_CLIENT_ID;
|
|
847
|
-
return hasKnownAuthor && documents.
|
|
996
|
+
return hasKnownAuthor && documents.isTriggeringEdit(document.textDocument.uri);
|
|
848
997
|
}
|
|
849
998
|
|
|
850
999
|
/**
|
|
@@ -878,13 +1027,26 @@ export class ModelService<
|
|
|
878
1027
|
this.syncChains.set(uri, chain);
|
|
879
1028
|
}
|
|
880
1029
|
|
|
1030
|
+
/**
|
|
1031
|
+
* The undo-stack label for a server-authored write, in the locale the server
|
|
1032
|
+
* was handed at init.
|
|
1033
|
+
*
|
|
1034
|
+
* One method rather than the literal at each `applyEdit`, because the two
|
|
1035
|
+
* call sites are the same edit — a push and its full-replace retry — and an
|
|
1036
|
+
* undo menu showing two different words for one operation would read as two
|
|
1037
|
+
* operations.
|
|
1038
|
+
*/
|
|
1039
|
+
protected editLabel(): string {
|
|
1040
|
+
return this.services.MessageRenderer.renderMessage(MODEL_UPDATE_EDIT);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
881
1043
|
protected async drainSyncQueue(uri: string): Promise<void> {
|
|
882
1044
|
const uriLogger = this.tracer.withUri(uri);
|
|
883
1045
|
while (this.pendingSync.has(uri)) {
|
|
884
1046
|
const text = this.pendingSync.get(uri)!;
|
|
885
1047
|
this.pendingSync.delete(uri);
|
|
886
1048
|
try {
|
|
887
|
-
let result = await this.services.workspace.TextDocuments.applyEditToLanguageClient(uri, text, { label:
|
|
1049
|
+
let result = await this.services.workspace.TextDocuments.applyEditToLanguageClient(uri, text, { label: this.editLabel() });
|
|
888
1050
|
if (result?.applied === false && !this.pendingSync.has(uri)) {
|
|
889
1051
|
// The push is addressed at the client's LAST DECLARED VERSION, so a
|
|
890
1052
|
// rejection normally means the client's buffer moved while the
|
|
@@ -905,7 +1067,7 @@ export class ModelService<
|
|
|
905
1067
|
// queued — best-effort, since a settle arriving later simply pushes
|
|
906
1068
|
// after this and still wins.
|
|
907
1069
|
uriLogger.warn(`Language client rejected applyEdit at its declared version — re-pushing a full replace`);
|
|
908
|
-
result = await this.services.workspace.TextDocuments.applyEditToLanguageClient(uri, text, { label:
|
|
1070
|
+
result = await this.services.workspace.TextDocuments.applyEditToLanguageClient(uri, text, { label: this.editLabel() });
|
|
909
1071
|
if (result?.applied === false) {
|
|
910
1072
|
uriLogger.warn(`Language client rejected the full-replace retry too — client content is stale`);
|
|
911
1073
|
}
|
|
@@ -1048,4 +1210,23 @@ export class ModelService<
|
|
|
1048
1210
|
? AstDocument.from<TAst, TDiagnostic>(document)
|
|
1049
1211
|
: AstDocument.create<TAst, TDiagnostic>(uri.toString(), 0, undefined as unknown as TAst, []);
|
|
1050
1212
|
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* The same envelope with no diagnostics, for a read that names a phase below
|
|
1216
|
+
* `Validated`.
|
|
1217
|
+
*
|
|
1218
|
+
* Langium fills `LangiumDocument.diagnostics` from inside `validateDocument`
|
|
1219
|
+
* and from nowhere else, so below that phase the array holds whatever an
|
|
1220
|
+
* EARLIER build left — a verdict about text the document may no longer have.
|
|
1221
|
+
* The wait underneath resolves at or above the phase asked for, so a document
|
|
1222
|
+
* something else carried past `Validated` would otherwise hand a full array
|
|
1223
|
+
* back from `parsed()`.
|
|
1224
|
+
*
|
|
1225
|
+
* A copy rather than a clear: the envelope is freshly built here, but
|
|
1226
|
+
* {@link toAstDocument} is overridable and an adopter's version may return
|
|
1227
|
+
* one it also keeps.
|
|
1228
|
+
*/
|
|
1229
|
+
protected withoutDiagnostics(document: AstDocument<TAst, TDiagnostic>): AstDocument<TAst, never> {
|
|
1230
|
+
return { ...document, diagnostics: [] };
|
|
1231
|
+
}
|
|
1051
1232
|
}
|