@hydranium/core 1.0.0-next.8 → 1.0.0-next.85
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/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/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
|
@@ -78,15 +78,13 @@ export type AstNodeBuilder<TMap> = <TType extends string & keyof TMap>(
|
|
|
78
78
|
* AST-interface mapping, so call sites get full inference from the type
|
|
79
79
|
* constant alone — no explicit generic parameter at every call site.
|
|
80
80
|
*
|
|
81
|
-
* Runtime behaviour:
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
* TS-augmented fields (declared via `declare module './generated/ast.js'`)
|
|
89
|
-
* are unaffected by the runtime defaulting but remain typed at call sites.
|
|
81
|
+
* Runtime behaviour: pre-fills every grammar-declared default (see
|
|
82
|
+
* {@link withTypeDefaults}) before merging the caller's `init`, so containment
|
|
83
|
+
* lists and `?=`-style boolean flags default automatically. TS-augmented fields
|
|
84
|
+
* (declared via `declare module './generated/ast.js'`) are unaffected by the
|
|
85
|
+
* runtime defaulting but remain typed at call sites.
|
|
86
|
+
*
|
|
87
|
+
* Use {@link buildAstNode} instead when the type name is only known at runtime.
|
|
90
88
|
*
|
|
91
89
|
* The init shape only relaxes array-typed fields to optional (via
|
|
92
90
|
* {@link ArrayKeys}) — there is no equivalent structural signal for "this
|
|
@@ -101,19 +99,7 @@ export function makeAstNodeBuilder<TMap>(reflection: AstReflection): AstNodeBuil
|
|
|
101
99
|
init: AstNodeInit<Extract<TMap[TType], AstNode>>,
|
|
102
100
|
extras?: Record<string, unknown>
|
|
103
101
|
): Extract<TMap[TType], AstNode> {
|
|
104
|
-
const
|
|
105
|
-
const result: Record<string, unknown> = { $type: typeConstant.$type };
|
|
106
|
-
if (meta?.properties) {
|
|
107
|
-
for (const [propName, propInfo] of Object.entries(meta.properties)) {
|
|
108
|
-
const defaultValue = propInfo.defaultValue;
|
|
109
|
-
if (defaultValue === undefined) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
// Arrays are duplicated so each node carries its own mutable instance;
|
|
113
|
-
// scalars are immutable and safe to copy by reference.
|
|
114
|
-
result[propName] = Array.isArray(defaultValue) ? [...defaultValue] : defaultValue;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
102
|
+
const result = withTypeDefaults(reflection, typeConstant.$type);
|
|
117
103
|
Object.assign(result, init);
|
|
118
104
|
if (extras) {
|
|
119
105
|
// Extras win over init: this is the explicit escape hatch, so when a
|
|
@@ -123,3 +109,61 @@ export function makeAstNodeBuilder<TMap>(reflection: AstReflection): AstNodeBuil
|
|
|
123
109
|
return result as unknown as Extract<TMap[TType], AstNode>;
|
|
124
110
|
};
|
|
125
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Build a node whose type is only known as a RUNTIME string.
|
|
115
|
+
*
|
|
116
|
+
* The typed {@link makeAstNodeBuilder} cannot express this: its `TMap` generic
|
|
117
|
+
* resolves the AST interface from a `$type` literal, and a type name that
|
|
118
|
+
* arrives over the wire — a protocol-layer reference request naming a node that
|
|
119
|
+
* does not exist yet — has no literal to resolve from. Callers that DO know the
|
|
120
|
+
* type at compile time want the typed builder, which checks their mandatory
|
|
121
|
+
* fields; this one checks nothing beyond the reflection lookup.
|
|
122
|
+
*
|
|
123
|
+
* Applies the same reflection-driven defaulting, so a node built here carries
|
|
124
|
+
* grammar-declared containment arrays rather than leaving them `undefined`. An
|
|
125
|
+
* unknown type name yields a node with `$type` and whatever `init` supplied,
|
|
126
|
+
* because reflection answers no metadata for it and refusing would turn a
|
|
127
|
+
* permissive lookup into a throw at a call site that can only guess.
|
|
128
|
+
*
|
|
129
|
+
* **The `type` argument is authoritative: `init` cannot rebind `$type`.** The
|
|
130
|
+
* typed builder gets this for free — `AstNodeInit` omits `$type`, so a call site
|
|
131
|
+
* cannot reach it — but here `init` is loosely typed, and the case that needs
|
|
132
|
+
* the guarantee is RETYPING: a caller spreading an existing node to produce one
|
|
133
|
+
* of a different type (`{ ...source }`) carries the SOURCE's `$type` in, which
|
|
134
|
+
* would silently win over the type actually requested and hand back a node of
|
|
135
|
+
* the wrong type with no error anywhere.
|
|
136
|
+
*/
|
|
137
|
+
export function buildAstNode<TAst extends AstNode = AstNode>(
|
|
138
|
+
reflection: AstReflection,
|
|
139
|
+
type: string,
|
|
140
|
+
init?: Partial<AstNode> & Record<string, unknown>
|
|
141
|
+
): TAst {
|
|
142
|
+
const result = withTypeDefaults(reflection, type);
|
|
143
|
+
if (init) {
|
|
144
|
+
Object.assign(result, init);
|
|
145
|
+
result.$type = type;
|
|
146
|
+
}
|
|
147
|
+
return result as unknown as TAst;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Seed a node with `$type` plus every property the grammar declares a default
|
|
152
|
+
* for. Array defaults are materialised as a fresh `[]` per call — never shared
|
|
153
|
+
* with the metadata's own array instance, which downstream callers mutate —
|
|
154
|
+
* while scalar defaults are copied as-is since they are immutable.
|
|
155
|
+
*/
|
|
156
|
+
function withTypeDefaults(reflection: AstReflection, type: string): Record<string, unknown> {
|
|
157
|
+
const result: Record<string, unknown> = { $type: type };
|
|
158
|
+
const meta = reflection.getTypeMetaData(type);
|
|
159
|
+
if (meta?.properties) {
|
|
160
|
+
for (const [propName, propInfo] of Object.entries(meta.properties)) {
|
|
161
|
+
const defaultValue = propInfo.defaultValue;
|
|
162
|
+
if (defaultValue === undefined) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
result[propName] = Array.isArray(defaultValue) ? [...defaultValue] : defaultValue;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}
|
package/src/langium/bootstrap.ts
CHANGED
|
@@ -12,15 +12,15 @@ import { type LangiumSharedCoreServices } from '@hydranium/langium';
|
|
|
12
12
|
import { type LangiumServices, type LangiumSharedServices } from '@hydranium/langium/lsp';
|
|
13
13
|
import type { ServerSharedServicesMinimal } from './shared-services.js';
|
|
14
14
|
import { collectProducibleTypes } from './language-types.js';
|
|
15
|
-
import {
|
|
15
|
+
import { DefaultAstDocumentManager } from '../documents/ast-document-manager.js';
|
|
16
16
|
import { HydraniumTextDocuments } from '../documents/hydranium-text-documents.js';
|
|
17
|
-
import {
|
|
17
|
+
import { DefaultBuildPipelineIntegration } from './document-builder/build-pipeline-integration.js';
|
|
18
18
|
import { HydraniumDocumentBuilder } from './document-builder/document-builder.js';
|
|
19
|
-
import {
|
|
19
|
+
import { DefaultModelService } from './model-service/model-service.js';
|
|
20
20
|
import { HydraniumScopeComputation } from './scope/hydranium-scope-computation.js';
|
|
21
21
|
import { HydraniumScopeProvider } from './scope/hydranium-scope-provider.js';
|
|
22
22
|
import { processEnv } from '../util/environment.js';
|
|
23
|
-
import {
|
|
23
|
+
import { DefaultTransferEncoder } from './transfer/transfer-encoder.js';
|
|
24
24
|
import { HydraniumWorkspaceManager } from './workspace/hydranium-workspace-manager.js';
|
|
25
25
|
|
|
26
26
|
/**
|
|
@@ -73,7 +73,14 @@ const EXPECTED_SHARED_SLOTS: readonly ExpectedSlot[] = [
|
|
|
73
73
|
side: 'shared',
|
|
74
74
|
path: 'workspace.BuildPipelineIntegration',
|
|
75
75
|
get: services => nested(services, 'workspace', 'BuildPipelineIntegration')
|
|
76
|
-
}
|
|
76
|
+
},
|
|
77
|
+
// Unbound, this fails where the cause is least visible: the render pass runs
|
|
78
|
+
// inside the `Validated` phase, so a `TypeError` there propagates out of
|
|
79
|
+
// `notifyDocumentPhase` and the document reaches `Validated` with Langium's
|
|
80
|
+
// publisher never invoked — the client receives no diagnostics for the file
|
|
81
|
+
// and the stack names the document builder.
|
|
82
|
+
{ side: 'shared', path: 'MessageRenderer', get: services => nested(services, 'MessageRenderer') },
|
|
83
|
+
{ side: 'shared', path: 'ServerLocale', get: services => nested(services, 'ServerLocale') }
|
|
77
84
|
];
|
|
78
85
|
|
|
79
86
|
/**
|
|
@@ -199,17 +206,17 @@ const STRICT_SHARED_SLOTS: readonly StrictSlot[] = [
|
|
|
199
206
|
{
|
|
200
207
|
side: 'shared',
|
|
201
208
|
path: 'workspace.AstDocumentManager',
|
|
202
|
-
base:
|
|
209
|
+
base: DefaultAstDocumentManager,
|
|
203
210
|
get: s => nested(s, 'workspace', 'AstDocumentManager')
|
|
204
211
|
},
|
|
205
212
|
{
|
|
206
213
|
side: 'shared',
|
|
207
214
|
path: 'workspace.BuildPipelineIntegration',
|
|
208
|
-
base:
|
|
215
|
+
base: DefaultBuildPipelineIntegration,
|
|
209
216
|
get: s => nested(s, 'workspace', 'BuildPipelineIntegration')
|
|
210
217
|
},
|
|
211
|
-
{ side: 'shared', path: 'model.TransferEncoder', base:
|
|
212
|
-
{ side: 'shared', path: 'model.ModelService', base:
|
|
218
|
+
{ side: 'shared', path: 'model.TransferEncoder', base: DefaultTransferEncoder, get: s => nested(s, 'model', 'TransferEncoder') },
|
|
219
|
+
{ side: 'shared', path: 'model.ModelService', base: DefaultModelService, get: s => nested(s, 'model', 'ModelService') }
|
|
213
220
|
];
|
|
214
221
|
|
|
215
222
|
const STRICT_LANGUAGE_SLOTS: readonly StrictSlot[] = [
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { type ConfigurationInitializedParams, DefaultConfigurationProvider } from '@hydranium/langium';
|
|
11
|
+
import type { ConfigurationItem, DidChangeConfigurationRegistrationOptions } from 'vscode-languageserver';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Configuration provider that asks the client about a section the first time
|
|
15
|
+
* something reads it.
|
|
16
|
+
*
|
|
17
|
+
* Langium's base requests one section per registered language, named after the
|
|
18
|
+
* language id, and never asks again. A read of any other section — including
|
|
19
|
+
* whatever `lsp.configurationRoot` is bound to — then resolves `undefined` with
|
|
20
|
+
* no request on the wire, which is indistinguishable from a setting the user has
|
|
21
|
+
* not set.
|
|
22
|
+
*
|
|
23
|
+
* Asking on first read rather than from a list collected up front: a reader
|
|
24
|
+
* names its section during DI, after `initialized` has taken its one-shot pass,
|
|
25
|
+
* so a list would have to be complete before the server initialized.
|
|
26
|
+
*/
|
|
27
|
+
export class HydraniumConfigurationProvider extends DefaultConfigurationProvider {
|
|
28
|
+
/**
|
|
29
|
+
* The client-facing hooks, captured because they arrive once as arguments to
|
|
30
|
+
* {@link initialized} and are needed on every later read. Absent in a
|
|
31
|
+
* headless composition, which has no connection to ask.
|
|
32
|
+
*/
|
|
33
|
+
protected registerForSection?: (params: DidChangeConfigurationRegistrationOptions) => void;
|
|
34
|
+
protected fetchSections?: (items: ConfigurationItem[]) => Promise<unknown>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Sections already dealt with, holding the in-flight work so concurrent
|
|
38
|
+
* readers share one round trip. Entries stay after they settle: they record
|
|
39
|
+
* that the section was HANDLED, and without that an unset section is
|
|
40
|
+
* re-requested on every read, "unset" and "not fetched" being the same
|
|
41
|
+
* absence in the store.
|
|
42
|
+
*
|
|
43
|
+
* A section the client's capabilities ruled out counts as handled, having
|
|
44
|
+
* been settled by asking nothing.
|
|
45
|
+
*/
|
|
46
|
+
protected readonly requestedSections = new Map<string, Promise<void>>();
|
|
47
|
+
|
|
48
|
+
override async initialized(params: ConfigurationInitializedParams): Promise<void> {
|
|
49
|
+
this.registerForSection = params.register;
|
|
50
|
+
this.fetchSections = params.fetchConfiguration;
|
|
51
|
+
await super.initialized(params);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The `ready` await precedes the emptiness check: the base resolves it at the
|
|
56
|
+
* end of its own initial fetch, so testing the store first calls every
|
|
57
|
+
* language section missing and re-requests what is already on its way.
|
|
58
|
+
*/
|
|
59
|
+
override async getConfiguration(language: string, configuration: string): Promise<unknown> {
|
|
60
|
+
await this.ready;
|
|
61
|
+
const section = this.toSectionName(language);
|
|
62
|
+
if (this.settings[section] === undefined) {
|
|
63
|
+
await this.requestSection(section);
|
|
64
|
+
}
|
|
65
|
+
return super.getConfiguration(language, configuration);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Ask about `section` once, sharing the request with any concurrent reader. */
|
|
69
|
+
protected requestSection(section: string): Promise<void> {
|
|
70
|
+
const existing = this.requestedSections.get(section);
|
|
71
|
+
if (existing) {
|
|
72
|
+
return existing;
|
|
73
|
+
}
|
|
74
|
+
const request = this.doRequestSection(section);
|
|
75
|
+
this.requestedSections.set(section, request);
|
|
76
|
+
return request;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The hooks are gated on the client having declared `workspace.configuration`.
|
|
81
|
+
* A connection binds them whatever the client said, so their presence is not
|
|
82
|
+
* consent: calling them sends a `client/registerCapability` and a
|
|
83
|
+
* `workspace/configuration` to a client that advertised support for neither,
|
|
84
|
+
* which is entitled to answer either with a method-not-found and may drop the
|
|
85
|
+
* connection.
|
|
86
|
+
*
|
|
87
|
+
* That flag is exact for the fetch and KNOWINGLY coarse for the registration,
|
|
88
|
+
* which the spec governs by
|
|
89
|
+
* `workspace.didChangeConfiguration.dynamicRegistration`. The base registers
|
|
90
|
+
* under `workspace.configuration` alone before any of this runs, so gating
|
|
91
|
+
* the registration on the narrower flag here would withhold a request whose
|
|
92
|
+
* twin has already gone out, sparing the client nothing. A client declaring
|
|
93
|
+
* `workspace.configuration` without dynamic registration is still registered
|
|
94
|
+
* for, by the base and by this; closing that means changing the base's pass,
|
|
95
|
+
* not this gate.
|
|
96
|
+
*
|
|
97
|
+
* Registering before fetching, so an edit landing between the two arrives as
|
|
98
|
+
* a notification instead of being lost; the reverse order drops it.
|
|
99
|
+
*
|
|
100
|
+
* A `null` result is left out of the store rather than written, so the
|
|
101
|
+
* section reads as unset, and no `onConfigurationSectionUpdate` fires — the
|
|
102
|
+
* value goes to the caller that asked for it, and an event would announce a
|
|
103
|
+
* change that is only this provider catching up.
|
|
104
|
+
*/
|
|
105
|
+
protected async doRequestSection(section: string): Promise<void> {
|
|
106
|
+
if (!this.workspaceConfig) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
this.registerForSection?.({ section });
|
|
110
|
+
if (!this.fetchSections) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const fetched = await this.fetchSections([{ section }]);
|
|
114
|
+
const value = Array.isArray(fetched) ? fetched[0] : fetched;
|
|
115
|
+
if (value !== null && value !== undefined) {
|
|
116
|
+
this.updateSectionConfiguration(section, value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -92,14 +92,17 @@ export class LspLogger extends AbstractLogger implements Logger {
|
|
|
92
92
|
// a Node concern; skip it cleanly there.
|
|
93
93
|
const env = processEnv();
|
|
94
94
|
if (env) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
95
|
+
// The file-tee target FIRST: the other order writes the level
|
|
96
|
+
// announcement before the sink exists, losing the one line that says
|
|
97
|
+
// which threshold the rest of the file was written at.
|
|
99
98
|
const envLogFile = env[DEFAULT_LOG_FILE_ENV];
|
|
100
99
|
if (envLogFile) {
|
|
101
100
|
setLogFilePath(envLogFile);
|
|
102
101
|
}
|
|
102
|
+
const envLevel = parseLogLevel(env[DEFAULT_LOG_LEVEL_ENV]);
|
|
103
|
+
if (envLevel) {
|
|
104
|
+
this.applyLevel(envLevel, DEFAULT_LOG_LEVEL_ENV);
|
|
105
|
+
}
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
// Apply the configured threshold from the root logger only — `derive`
|
|
@@ -109,16 +112,48 @@ export class LspLogger extends AbstractLogger implements Logger {
|
|
|
109
112
|
if (options.logThreshold !== undefined) {
|
|
110
113
|
const level = ObservableValue.from(options.logThreshold);
|
|
111
114
|
if (level.value) {
|
|
112
|
-
|
|
115
|
+
this.applyLevel(level.value, 'setting');
|
|
113
116
|
}
|
|
114
117
|
level.onChange(next => {
|
|
115
118
|
if (next) {
|
|
116
|
-
|
|
119
|
+
this.applyLevel(next, 'setting');
|
|
117
120
|
}
|
|
118
121
|
});
|
|
119
122
|
}
|
|
120
123
|
}
|
|
121
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Set the process-wide threshold and announce the transition under whichever
|
|
127
|
+
* of the two thresholds can carry the notice, so a reader sees every change
|
|
128
|
+
* and its absence means the threshold never moved.
|
|
129
|
+
*
|
|
130
|
+
* `error` and `off` admit no notice of their own — `off` nothing at all, and
|
|
131
|
+
* an error-severity line is read as a fault, which a client that marks its
|
|
132
|
+
* log as having seen an error keeps for the session. Entering either is
|
|
133
|
+
* therefore announced BEFORE the switch, under the outgoing threshold; every
|
|
134
|
+
* other change is announced after it, at the level being entered. Leaving
|
|
135
|
+
* `error` or `off` for one of those two says nothing, both thresholds being
|
|
136
|
+
* unable to carry it.
|
|
137
|
+
*
|
|
138
|
+
* Only on a real transition: the bound setting re-fires for configuration
|
|
139
|
+
* changes that leave the value alone.
|
|
140
|
+
*/
|
|
141
|
+
protected applyLevel(next: LogThreshold, source: string): void {
|
|
142
|
+
const previous = Logger.getLevel();
|
|
143
|
+
if (previous === next) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const message = `Log level ${previous} → ${next} (${source})`;
|
|
147
|
+
const announceFirst = next === 'error' || next === 'off';
|
|
148
|
+
if (announceFirst && previous !== 'error') {
|
|
149
|
+
this.logAt(previous, message);
|
|
150
|
+
}
|
|
151
|
+
Logger.setLevel(next);
|
|
152
|
+
if (!announceFirst) {
|
|
153
|
+
this.logAt(next, message);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
122
157
|
/**
|
|
123
158
|
* Route logger timing and timestamps through the injected framework
|
|
124
159
|
* {@link Clock} (the dedicated `shared.Clock` slot) so server-side log time
|
|
@@ -65,6 +65,19 @@ export interface BuildPipelineIntegrationOptions {
|
|
|
65
65
|
readonly integrityPriority?: number;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* The build-pipeline slot. Empty because nothing calls this service — it wires
|
|
70
|
+
* itself in its constructor and every seam it offers is `protected`.
|
|
71
|
+
*
|
|
72
|
+
* Typing the slot by the class instead would stop an adopter's subclass
|
|
73
|
+
* satisfying it, since a class carries its `protected` members into the check
|
|
74
|
+
* and they compare by declaration rather than by shape. The cost of the empty
|
|
75
|
+
* shape is that any non-null value satisfies it, so a mis-bound slot compiles
|
|
76
|
+
* and the framework's phase listeners are then never attached.
|
|
77
|
+
*/
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type -- the slot has no public member to name
|
|
79
|
+
export interface BuildPipelineIntegration {}
|
|
80
|
+
|
|
68
81
|
/**
|
|
69
82
|
* Wires the framework's build-time features (integrity rules, AST
|
|
70
83
|
* enrichment) into Langium's document-build pipeline. The single shared
|
|
@@ -90,7 +103,7 @@ export interface BuildPipelineIntegrationOptions {
|
|
|
90
103
|
* Eagerly constructed (via `DEFAULT_EAGER_SERVICES`) so the listeners are
|
|
91
104
|
* attached before the first build cycle.
|
|
92
105
|
*/
|
|
93
|
-
export class BuildPipelineIntegration {
|
|
106
|
+
export class DefaultBuildPipelineIntegration implements BuildPipelineIntegration {
|
|
94
107
|
constructor(
|
|
95
108
|
protected readonly services: ServerSharedServices,
|
|
96
109
|
options: BuildPipelineIntegrationOptions = {}
|
|
@@ -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
|
+
import { type URI } from '@hydranium/langium';
|
|
11
|
+
|
|
12
|
+
/** What a build is about, handed to `HydraniumDocumentBuilder.createBuildSession`. */
|
|
13
|
+
export interface BuildSessionContext {
|
|
14
|
+
/**
|
|
15
|
+
* Which entry point opened the build: `'build'` is the workspace-wide pass
|
|
16
|
+
* (initialization), `'update'` an incremental rebuild. A subclass deriving
|
|
17
|
+
* cascade state skips the work for `'build'`, where every document is in the
|
|
18
|
+
* set already and `shouldRelink` is never consulted.
|
|
19
|
+
*/
|
|
20
|
+
readonly kind: 'build' | 'update';
|
|
21
|
+
/** Formatted description of what caused the build — a URI, a count summary. */
|
|
22
|
+
readonly trigger: string;
|
|
23
|
+
/** True when {@link trigger} already states the document count. */
|
|
24
|
+
readonly triggerCountsDocs: boolean;
|
|
25
|
+
/** URIs the build was asked to rebuild, already flattened and canonicalized. */
|
|
26
|
+
readonly changed: readonly URI[];
|
|
27
|
+
/** URIs the build was asked to delete, already expanded by `collectDeletedURIs`. Empty for a `build` call. */
|
|
28
|
+
readonly deleted: readonly URI[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* One rebuild, treated as a correlated unit.
|
|
33
|
+
*
|
|
34
|
+
* Everything a build phase needs to know about the build it belongs to — what
|
|
35
|
+
* triggered it, when it started, whether it was cancelled, and the log lines it
|
|
36
|
+
* has produced so far. A `HydraniumDocumentBuilder` opens one per `update` /
|
|
37
|
+
* `build` call and holds it for the duration, so a per-phase hook can reach
|
|
38
|
+
* build-wide facts that no per-line formatting hook could.
|
|
39
|
+
*
|
|
40
|
+
* **Identity is the object, not {@link traceId}.** `Tracer.time` short-circuits
|
|
41
|
+
* when its log level is suppressed and never calls `captureId`, so `traceId` is
|
|
42
|
+
* `undefined` on every build in a production configuration that logs above
|
|
43
|
+
* `debug`. A teardown guard keyed on the id would then compare `undefined`
|
|
44
|
+
* against `undefined` and let a preempted build clear its successor's state.
|
|
45
|
+
* Reference equality against `HydraniumDocumentBuilder.activeSession` holds in
|
|
46
|
+
* both configurations, which is why nothing here is keyed on the id.
|
|
47
|
+
*
|
|
48
|
+
* Adopters carrying build-scoped state of their own subclass this and return it
|
|
49
|
+
* from `HydraniumDocumentBuilder.createBuildSession`, narrowing with an
|
|
50
|
+
* `instanceof` where they read it back. That buys them the framework's
|
|
51
|
+
* preemption-correct teardown rather than a second copy of it.
|
|
52
|
+
*/
|
|
53
|
+
export class BuildSession {
|
|
54
|
+
/**
|
|
55
|
+
* `Tracer.time` correlation id, so a phase line and the build line share a
|
|
56
|
+
* `#N`. `undefined` until the timer starts, and permanently `undefined` when
|
|
57
|
+
* the timing line is suppressed.
|
|
58
|
+
*/
|
|
59
|
+
traceId?: number;
|
|
60
|
+
/** Set when the build ended in `OperationCancelled`, so the successor can tag itself "cancels #N". */
|
|
61
|
+
cancelled = false;
|
|
62
|
+
/** Phase-reached lines produced so far; the first one measures from {@link startMs} rather than from the previous phase. */
|
|
63
|
+
phasesLogged = 0;
|
|
64
|
+
/** Lines held back by {@link detailThresholdMs}; flushed or dropped when the build ends. */
|
|
65
|
+
readonly bufferedLines: string[] = [];
|
|
66
|
+
|
|
67
|
+
constructor(
|
|
68
|
+
/** Millisecond timestamp the build started at, on the same clock as `performance.now()`. */
|
|
69
|
+
readonly startMs: number,
|
|
70
|
+
/** What caused this build — a formatted URI, a count summary, or an adopter label. */
|
|
71
|
+
readonly trigger: string,
|
|
72
|
+
/** True when {@link trigger} already states the document count, so a phase line must not repeat it. */
|
|
73
|
+
readonly triggerCountsDocs: boolean,
|
|
74
|
+
/**
|
|
75
|
+
* Build duration at or above which the buffered lines are emitted; `0`
|
|
76
|
+
* disables buffering entirely. Captured per session so the flush decision
|
|
77
|
+
* uses the same value the buffering decision did, even if the underlying
|
|
78
|
+
* observable changed mid-build.
|
|
79
|
+
*/
|
|
80
|
+
readonly detailThresholdMs: number
|
|
81
|
+
) {}
|
|
82
|
+
|
|
83
|
+
/** True while lines are being held rather than emitted as they are produced. */
|
|
84
|
+
get buffers(): boolean {
|
|
85
|
+
return this.detailThresholdMs > 0;
|
|
86
|
+
}
|
|
87
|
+
}
|