@hydranium/glsp-server 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.
Files changed (208) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/lib/browser/index.d.ts +10 -0
  4. package/lib/browser/index.d.ts.map +1 -0
  5. package/lib/browser/index.js +15 -0
  6. package/lib/browser/index.js.map +1 -0
  7. package/lib/browser/start-glsp-server-in-worker.d.ts +121 -0
  8. package/lib/browser/start-glsp-server-in-worker.d.ts.map +1 -0
  9. package/lib/browser/start-glsp-server-in-worker.js +102 -0
  10. package/lib/browser/start-glsp-server-in-worker.js.map +1 -0
  11. package/lib/command/hydranium-glsp-recording-command.d.ts +132 -0
  12. package/lib/command/hydranium-glsp-recording-command.d.ts.map +1 -0
  13. package/lib/command/hydranium-glsp-recording-command.js +159 -0
  14. package/lib/command/hydranium-glsp-recording-command.js.map +1 -0
  15. package/lib/command/index.d.ts +10 -0
  16. package/lib/command/index.d.ts.map +1 -0
  17. package/lib/command/index.js +10 -0
  18. package/lib/command/index.js.map +1 -0
  19. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.d.ts +61 -0
  20. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.d.ts.map +1 -0
  21. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.js +93 -0
  22. package/lib/computed-bounds/hydranium-glsp-computed-bounds-action-handler.js.map +1 -0
  23. package/lib/computed-bounds/index.d.ts +10 -0
  24. package/lib/computed-bounds/index.d.ts.map +1 -0
  25. package/lib/computed-bounds/index.js +10 -0
  26. package/lib/computed-bounds/index.js.map +1 -0
  27. package/lib/dispatcher/index.d.ts +10 -0
  28. package/lib/dispatcher/index.d.ts.map +1 -0
  29. package/lib/dispatcher/index.js +10 -0
  30. package/lib/dispatcher/index.js.map +1 -0
  31. package/lib/dispatcher/server-action-dispatcher.d.ts +52 -0
  32. package/lib/dispatcher/server-action-dispatcher.d.ts.map +1 -0
  33. package/lib/dispatcher/server-action-dispatcher.js +79 -0
  34. package/lib/dispatcher/server-action-dispatcher.js.map +1 -0
  35. package/lib/index.d.ts +19 -0
  36. package/lib/index.d.ts.map +1 -0
  37. package/lib/index.js +20 -0
  38. package/lib/index.js.map +1 -0
  39. package/lib/launcher/abstract-hydranium-glsp-diagram-module.d.ts +75 -0
  40. package/lib/launcher/abstract-hydranium-glsp-diagram-module.d.ts.map +1 -0
  41. package/lib/launcher/abstract-hydranium-glsp-diagram-module.js +100 -0
  42. package/lib/launcher/abstract-hydranium-glsp-diagram-module.js.map +1 -0
  43. package/lib/launcher/glsp-app-module.d.ts +87 -0
  44. package/lib/launcher/glsp-app-module.d.ts.map +1 -0
  45. package/lib/launcher/glsp-app-module.js +71 -0
  46. package/lib/launcher/glsp-app-module.js.map +1 -0
  47. package/lib/launcher/glsp-framework-overrides.d.ts +32 -0
  48. package/lib/launcher/glsp-framework-overrides.d.ts.map +1 -0
  49. package/lib/launcher/glsp-framework-overrides.js +49 -0
  50. package/lib/launcher/glsp-framework-overrides.js.map +1 -0
  51. package/lib/launcher/glsp-server-overrides.d.ts +39 -0
  52. package/lib/launcher/glsp-server-overrides.d.ts.map +1 -0
  53. package/lib/launcher/glsp-server-overrides.js +45 -0
  54. package/lib/launcher/glsp-server-overrides.js.map +1 -0
  55. package/lib/launcher/hydranium-glsp-server.d.ts +62 -0
  56. package/lib/launcher/hydranium-glsp-server.d.ts.map +1 -0
  57. package/lib/launcher/hydranium-glsp-server.js +101 -0
  58. package/lib/launcher/hydranium-glsp-server.js.map +1 -0
  59. package/lib/launcher/index.d.ts +12 -0
  60. package/lib/launcher/index.d.ts.map +1 -0
  61. package/lib/launcher/index.js +22 -0
  62. package/lib/launcher/index.js.map +1 -0
  63. package/lib/logging/glsp-client-logger.d.ts +110 -0
  64. package/lib/logging/glsp-client-logger.d.ts.map +1 -0
  65. package/lib/logging/glsp-client-logger.js +151 -0
  66. package/lib/logging/glsp-client-logger.js.map +1 -0
  67. package/lib/logging/index.d.ts +10 -0
  68. package/lib/logging/index.d.ts.map +1 -0
  69. package/lib/logging/index.js +10 -0
  70. package/lib/logging/index.js.map +1 -0
  71. package/lib/node/index.d.ts +10 -0
  72. package/lib/node/index.d.ts.map +1 -0
  73. package/lib/node/index.js +13 -0
  74. package/lib/node/index.js.map +1 -0
  75. package/lib/node/start-glsp-server.d.ts +122 -0
  76. package/lib/node/start-glsp-server.d.ts.map +1 -0
  77. package/lib/node/start-glsp-server.js +103 -0
  78. package/lib/node/start-glsp-server.js.map +1 -0
  79. package/lib/state/abstract-hydranium-glsp-state.d.ts +333 -0
  80. package/lib/state/abstract-hydranium-glsp-state.d.ts.map +1 -0
  81. package/lib/state/abstract-hydranium-glsp-state.js +451 -0
  82. package/lib/state/abstract-hydranium-glsp-state.js.map +1 -0
  83. package/lib/state/full-text-hydranium-glsp-state.d.ts +43 -0
  84. package/lib/state/full-text-hydranium-glsp-state.d.ts.map +1 -0
  85. package/lib/state/full-text-hydranium-glsp-state.js +60 -0
  86. package/lib/state/full-text-hydranium-glsp-state.js.map +1 -0
  87. package/lib/state/hydranium-glsp-index.d.ts +220 -0
  88. package/lib/state/hydranium-glsp-index.d.ts.map +1 -0
  89. package/lib/state/hydranium-glsp-index.js +320 -0
  90. package/lib/state/hydranium-glsp-index.js.map +1 -0
  91. package/lib/state/hydranium-shared-core-services.d.ts +90 -0
  92. package/lib/state/hydranium-shared-core-services.d.ts.map +1 -0
  93. package/lib/state/hydranium-shared-core-services.js +89 -0
  94. package/lib/state/hydranium-shared-core-services.js.map +1 -0
  95. package/lib/state/index.d.ts +17 -0
  96. package/lib/state/index.d.ts.map +1 -0
  97. package/lib/state/index.js +17 -0
  98. package/lib/state/index.js.map +1 -0
  99. package/lib/state/model-ready-timeout-error.d.ts +28 -0
  100. package/lib/state/model-ready-timeout-error.d.ts.map +1 -0
  101. package/lib/state/model-ready-timeout-error.js +36 -0
  102. package/lib/state/model-ready-timeout-error.js.map +1 -0
  103. package/lib/state/reconcile-source-model-write.d.ts +43 -0
  104. package/lib/state/reconcile-source-model-write.d.ts.map +1 -0
  105. package/lib/state/reconcile-source-model-write.js +47 -0
  106. package/lib/state/reconcile-source-model-write.js.map +1 -0
  107. package/lib/state/reconciling-multi-document-glsp-state.d.ts +164 -0
  108. package/lib/state/reconciling-multi-document-glsp-state.d.ts.map +1 -0
  109. package/lib/state/reconciling-multi-document-glsp-state.js +222 -0
  110. package/lib/state/reconciling-multi-document-glsp-state.js.map +1 -0
  111. package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts +91 -0
  112. package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts.map +1 -0
  113. package/lib/state/reconciling-transfer-hydranium-glsp-state.js +129 -0
  114. package/lib/state/reconciling-transfer-hydranium-glsp-state.js.map +1 -0
  115. package/lib/storage/hydranium-glsp-storage.d.ts +348 -0
  116. package/lib/storage/hydranium-glsp-storage.d.ts.map +1 -0
  117. package/lib/storage/hydranium-glsp-storage.js +634 -0
  118. package/lib/storage/hydranium-glsp-storage.js.map +1 -0
  119. package/lib/storage/index.d.ts +11 -0
  120. package/lib/storage/index.d.ts.map +1 -0
  121. package/lib/storage/index.js +11 -0
  122. package/lib/storage/index.js.map +1 -0
  123. package/lib/storage/save-conflict-policy.d.ts +56 -0
  124. package/lib/storage/save-conflict-policy.d.ts.map +1 -0
  125. package/lib/storage/save-conflict-policy.js +19 -0
  126. package/lib/storage/save-conflict-policy.js.map +1 -0
  127. package/lib/submission/hydranium-glsp-submission-handler.d.ts +109 -0
  128. package/lib/submission/hydranium-glsp-submission-handler.d.ts.map +1 -0
  129. package/lib/submission/hydranium-glsp-submission-handler.js +157 -0
  130. package/lib/submission/hydranium-glsp-submission-handler.js.map +1 -0
  131. package/lib/submission/index.d.ts +10 -0
  132. package/lib/submission/index.d.ts.map +1 -0
  133. package/lib/submission/index.js +10 -0
  134. package/lib/submission/index.js.map +1 -0
  135. package/lib/testing/glsp-harness.d.ts +170 -0
  136. package/lib/testing/glsp-harness.d.ts.map +1 -0
  137. package/lib/testing/glsp-harness.js +231 -0
  138. package/lib/testing/glsp-harness.js.map +1 -0
  139. package/lib/testing/index.d.ts +11 -0
  140. package/lib/testing/index.d.ts.map +1 -0
  141. package/lib/testing/index.js +13 -0
  142. package/lib/testing/index.js.map +1 -0
  143. package/lib/testing/make-noop-glsp-logger.d.ts +42 -0
  144. package/lib/testing/make-noop-glsp-logger.d.ts.map +1 -0
  145. package/lib/testing/make-noop-glsp-logger.js +47 -0
  146. package/lib/testing/make-noop-glsp-logger.js.map +1 -0
  147. package/lib/util/index.d.ts +10 -0
  148. package/lib/util/index.d.ts.map +1 -0
  149. package/lib/util/index.js +10 -0
  150. package/lib/util/index.js.map +1 -0
  151. package/lib/util/service-identifier.d.ts +46 -0
  152. package/lib/util/service-identifier.d.ts.map +1 -0
  153. package/lib/util/service-identifier.js +47 -0
  154. package/lib/util/service-identifier.js.map +1 -0
  155. package/lib/validation/diagnostic-markers.d.ts +48 -0
  156. package/lib/validation/diagnostic-markers.d.ts.map +1 -0
  157. package/lib/validation/diagnostic-markers.js +68 -0
  158. package/lib/validation/diagnostic-markers.js.map +1 -0
  159. package/lib/validation/hydranium-glsp-model-validator.d.ts +51 -0
  160. package/lib/validation/hydranium-glsp-model-validator.d.ts.map +1 -0
  161. package/lib/validation/hydranium-glsp-model-validator.js +118 -0
  162. package/lib/validation/hydranium-glsp-model-validator.js.map +1 -0
  163. package/lib/validation/index.d.ts +11 -0
  164. package/lib/validation/index.d.ts.map +1 -0
  165. package/lib/validation/index.js +11 -0
  166. package/lib/validation/index.js.map +1 -0
  167. package/package.json +111 -0
  168. package/src/browser/index.ts +15 -0
  169. package/src/browser/start-glsp-server-in-worker.ts +202 -0
  170. package/src/command/hydranium-glsp-recording-command.ts +183 -0
  171. package/src/command/index.ts +10 -0
  172. package/src/computed-bounds/hydranium-glsp-computed-bounds-action-handler.ts +103 -0
  173. package/src/computed-bounds/index.ts +10 -0
  174. package/src/dispatcher/index.ts +10 -0
  175. package/src/dispatcher/server-action-dispatcher.ts +72 -0
  176. package/src/index.ts +20 -0
  177. package/src/launcher/abstract-hydranium-glsp-diagram-module.ts +117 -0
  178. package/src/launcher/glsp-app-module.ts +106 -0
  179. package/src/launcher/glsp-framework-overrides.ts +51 -0
  180. package/src/launcher/glsp-server-overrides.ts +46 -0
  181. package/src/launcher/hydranium-glsp-server.ts +104 -0
  182. package/src/launcher/index.ts +22 -0
  183. package/src/logging/glsp-client-logger.ts +184 -0
  184. package/src/logging/index.ts +10 -0
  185. package/src/node/index.ts +13 -0
  186. package/src/node/start-glsp-server.ts +214 -0
  187. package/src/state/abstract-hydranium-glsp-state.ts +503 -0
  188. package/src/state/full-text-hydranium-glsp-state.ts +63 -0
  189. package/src/state/hydranium-glsp-index.ts +337 -0
  190. package/src/state/hydranium-shared-core-services.ts +92 -0
  191. package/src/state/index.ts +17 -0
  192. package/src/state/model-ready-timeout-error.ts +33 -0
  193. package/src/state/reconcile-source-model-write.ts +77 -0
  194. package/src/state/reconciling-multi-document-glsp-state.ts +248 -0
  195. package/src/state/reconciling-transfer-hydranium-glsp-state.ts +132 -0
  196. package/src/storage/hydranium-glsp-storage.ts +660 -0
  197. package/src/storage/index.ts +11 -0
  198. package/src/storage/save-conflict-policy.ts +52 -0
  199. package/src/submission/hydranium-glsp-submission-handler.ts +170 -0
  200. package/src/submission/index.ts +10 -0
  201. package/src/testing/glsp-harness.ts +422 -0
  202. package/src/testing/index.ts +14 -0
  203. package/src/testing/make-noop-glsp-logger.ts +66 -0
  204. package/src/util/index.ts +10 -0
  205. package/src/util/service-identifier.ts +49 -0
  206. package/src/validation/diagnostic-markers.ts +93 -0
  207. package/src/validation/hydranium-glsp-model-validator.ts +105 -0
  208. package/src/validation/index.ts +11 -0
@@ -0,0 +1,66 @@
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 Logger as GlspLogger, NullLogger } from '@eclipse-glsp/server';
11
+
12
+ /**
13
+ * No-op GLSP {@link GlspLogger} for tests — discards every message. GLSP's
14
+ * `Logger` is an abstract class declaring `logLevel` / `caller` alongside the
15
+ * emit methods, so an object literal never structurally satisfies it and each
16
+ * test otherwise writes `{ info, warn, error, debug } as unknown as Logger`.
17
+ * This returns GLSP's shipped {@link NullLogger}, so call sites fill a
18
+ * `GlspLogger` slot cast-free — the GLSP-server sibling of `makeNoopLogger`.
19
+ */
20
+ export function makeNoopGlspLogger(): GlspLogger {
21
+ return new NullLogger();
22
+ }
23
+
24
+ /** GLSP emit level a {@link CapturedGlspLine} was recorded at. */
25
+ export type GlspLogLevel = 'info' | 'warn' | 'error' | 'debug';
26
+
27
+ /** A captured GLSP emission: level, rendered message, and trailing params. */
28
+ export interface CapturedGlspLine {
29
+ readonly level: GlspLogLevel;
30
+ readonly message: string;
31
+ readonly params: readonly unknown[];
32
+ }
33
+
34
+ /** A GLSP {@link GlspLogger} whose emitted lines are captured for assertions. */
35
+ export interface CapturingGlspLogger {
36
+ readonly logger: GlspLogger;
37
+ /** Every line emitted through the logger, in order. */
38
+ readonly lines: CapturedGlspLine[];
39
+ }
40
+
41
+ /**
42
+ * Capturing GLSP {@link GlspLogger} for tests that assert on emitted output
43
+ * (e.g. an error surfaced through the logger). Backed by a {@link NullLogger}
44
+ * subclass so it is a complete `Logger`; each emit records into
45
+ * {@link CapturingGlspLogger.lines}. Filter by `level` to assert on one tier —
46
+ * `lines.filter(line => line.level === 'error')`. Prefer this over hand-rolling
47
+ * an `{ error } as unknown as Logger` stub.
48
+ */
49
+ export function makeCapturingGlspLogger(): CapturingGlspLogger {
50
+ const lines: CapturedGlspLine[] = [];
51
+ class Capturing extends NullLogger {
52
+ override info(message: string, ...params: unknown[]): void {
53
+ lines.push({ level: 'info', message, params });
54
+ }
55
+ override warn(message: string, ...params: unknown[]): void {
56
+ lines.push({ level: 'warn', message, params });
57
+ }
58
+ override error(message: string, ...params: unknown[]): void {
59
+ lines.push({ level: 'error', message, params });
60
+ }
61
+ override debug(message: string, ...params: unknown[]): void {
62
+ lines.push({ level: 'debug', message, params });
63
+ }
64
+ }
65
+ return { logger: new Capturing(), lines };
66
+ }
@@ -0,0 +1,10 @@
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
+ export * from './service-identifier.js';
@@ -0,0 +1,49 @@
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 { interfaces } from 'inversify';
11
+
12
+ /**
13
+ * Create an Inversify service identifier for type `T`.
14
+ *
15
+ * Returns a fresh Symbol cast to Inversify's typed
16
+ * {@link interfaces.ServiceIdentifier} shape. The cast is necessary
17
+ * because TypeScript cannot infer the generic parameter from a Symbol
18
+ * literal alone — Symbols carry no type information at the value level,
19
+ * so `bind` and `@inject` would receive `any` without it. The cast lets
20
+ * Inversify type-check the bound value at `bind(...).toConstantValue(x)`
21
+ * sites and infer the field type at `@inject(...)` declarations.
22
+ *
23
+ * **Why a Symbol + cast, not a class or string token.**
24
+ *
25
+ * - **Class-as-token** doesn't work for interface-typed services —
26
+ * interfaces have no runtime value to bind against. Using a concrete
27
+ * class as the token would force adopters to extend that specific
28
+ * class rather than implement the interface.
29
+ * - **String tokens** carry cross-package collision risk; two packages
30
+ * that independently bind the same name would clash in a shared
31
+ * container.
32
+ * - **`Symbol.for(name)`** uses Node's cross-realm registry — that sharing
33
+ * is rarely needed, and it makes module-reload behaviour a foot-gun.
34
+ *
35
+ * Fresh `Symbol(description)` per token is the right primitive; this
36
+ * helper centralises the cast so consumers don't repeat the
37
+ * `as interfaces.ServiceIdentifier<T>` boilerplate at every binding
38
+ * site declaration.
39
+ *
40
+ * Only the GLSP head uses Inversify, which is why this lives here; LSP /
41
+ * data-server / `@hydranium/core` use Langium's plain constructor-arg DI
42
+ * through the typed services tree, where binding tokens aren't needed. Adopters
43
+ * mint a token per service they expose to their `@injectable()` GLSP components;
44
+ * the framework's own are grouped in `HydraniumTypes` so token identity
45
+ * stays decoupled from the implementation class name.
46
+ */
47
+ export function serviceIdentifier<T>(description: string): interfaces.ServiceIdentifier<T> {
48
+ return Symbol(description) as interfaces.ServiceIdentifier<T>;
49
+ }
@@ -0,0 +1,93 @@
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 Marker, MarkerKind } from '@eclipse-glsp/protocol';
11
+ import { type AstNode } from '@hydranium/langium';
12
+ import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-types';
13
+ import { type TransferLspDiagnostic } from '@hydranium/core';
14
+
15
+ /**
16
+ * The model-shaped lookups the diagnostic→marker translation needs, injected so
17
+ * the pure mapping can be unit-tested without GLSP/Langium DI. The validator
18
+ * wires them to the live services.
19
+ */
20
+ export interface DiagnosticMarkerLookups {
21
+ /**
22
+ * Resolve a diagnostic's `element` AST-path (as produced by
23
+ * `AstNodeLocator.getAstNodePath`) to its node in the current source root,
24
+ * or `undefined` if it no longer resolves.
25
+ */
26
+ resolveElement(elementPath: string): AstNode | undefined;
27
+ /**
28
+ * The GModel ids that currently render `node` on this diagram. Usually one,
29
+ * but a single AST element can be drawn as several nodes, and then every
30
+ * representing node is marked. Empty when `node` is not drawn here.
31
+ */
32
+ renderedIdsFor(node: AstNode): readonly string[];
33
+ }
34
+
35
+ /**
36
+ * Translate the current document's LSP diagnostics into GLSP {@link Marker}s
37
+ * for the diagram.
38
+ *
39
+ * Each diagnostic's `element` path is resolved to an AST node; if that node is
40
+ * not drawn on this diagram, the mapping walks up the `$container` chain to the
41
+ * nearest ancestor that is, and marks every GModel node representing it — so a
42
+ * deeply nested error still surfaces, on the innermost drawn container, at each
43
+ * of its occurrences. A diagnostic whose element — and every ancestor — is
44
+ * absent from this diagram is dropped: the model may span several diagrams, and
45
+ * the text editor / Problems view still report it.
46
+ *
47
+ * The full set is recomputed on every call; callers dispatch it under a single
48
+ * `MarkersReason` so a fresh `SetMarkersAction` replaces the previous markers
49
+ * (an empty result clears them when all errors are fixed).
50
+ */
51
+ export function diagnosticsToMarkers(diagnostics: readonly TransferLspDiagnostic[], lookups: DiagnosticMarkerLookups): Marker[] {
52
+ const markers: Marker[] = [];
53
+ for (const diagnostic of diagnostics) {
54
+ const kind = markerKind(diagnostic.severity);
55
+ // LSP 3.18 allows a `MarkupContent` message; GLSP markers are plain text.
56
+ const message = Diagnostic.getMessageString(diagnostic);
57
+ for (const elementId of renderedElementIds(diagnostic.element, lookups)) {
58
+ markers.push({ elementId, kind, label: message, description: message });
59
+ }
60
+ }
61
+ return markers;
62
+ }
63
+
64
+ /**
65
+ * The ids of the elements drawn on this diagram for the diagnostic's target,
66
+ * starting at the node it points at and walking up its containers; empty when
67
+ * the path does not resolve or nothing on the chain is rendered.
68
+ */
69
+ function renderedElementIds(elementPath: string, lookups: DiagnosticMarkerLookups): readonly string[] {
70
+ let node: AstNode | undefined = lookups.resolveElement(elementPath);
71
+ while (node !== undefined) {
72
+ const ids = lookups.renderedIdsFor(node);
73
+ if (ids.length > 0) {
74
+ return ids;
75
+ }
76
+ node = node.$container;
77
+ }
78
+ return [];
79
+ }
80
+
81
+ /** Map an LSP {@link DiagnosticSeverity} to a GLSP {@link MarkerKind}; unset severity is treated as an error. */
82
+ function markerKind(severity: DiagnosticSeverity | undefined): string {
83
+ switch (severity) {
84
+ case DiagnosticSeverity.Warning:
85
+ return MarkerKind.WARNING;
86
+ case DiagnosticSeverity.Information:
87
+ case DiagnosticSeverity.Hint:
88
+ return MarkerKind.INFO;
89
+ case DiagnosticSeverity.Error:
90
+ default:
91
+ return MarkerKind.ERROR;
92
+ }
93
+ }
@@ -0,0 +1,105 @@
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 Marker } from '@eclipse-glsp/protocol';
11
+ import { type GModelElement, ModelState, type ModelValidator } from '@eclipse-glsp/server';
12
+ import { inject, injectable } from 'inversify';
13
+ import { type AstNode } from '@hydranium/langium';
14
+ import { URI } from '@hydranium/langium';
15
+ import { type ServerSharedServices, type TransferLspDiagnostic } from '@hydranium/core';
16
+ import { type AbstractHydraniumGlspState } from '../state/abstract-hydranium-glsp-state.js';
17
+ import { HydraniumTypes } from '../state/hydranium-shared-core-services.js';
18
+ import { type DiagnosticMarkerLookups, diagnosticsToMarkers } from './diagnostic-markers.js';
19
+
20
+ /**
21
+ * Framework {@link ModelValidator} that surfaces the model server's LSP
22
+ * diagnostics as GLSP diagram markers. Bound per diagram via
23
+ * `DiagramModule.bindModelValidator()`; the framework submission handler also
24
+ * pushes its output as a `SetMarkersAction` after every model submit so
25
+ * markers refresh live (the GLSP client otherwise only re-requests markers on
26
+ * the tool-palette validate command).
27
+ *
28
+ * The translation itself ({@link diagnosticsToMarkers}) is pure; this class
29
+ * supplies its model-shaped lookups from the live services: the per-language
30
+ * `AstNodeLocator` resolves a diagnostic's `element` path back to its AST node,
31
+ * and the GModel index assigns the node's diagram id and reports whether that
32
+ * id is currently drawn.
33
+ *
34
+ * **Cross-document.** A diagram routinely renders elements defined in *other*
35
+ * documents, and their validation errors are published on those documents, not
36
+ * on the diagram's own. The validator therefore scans the diagnostics of every
37
+ * document the index reports as contributing a rendered element (the index's
38
+ * `renderedDocumentUris`: the diagram's own document plus those reached via
39
+ * reference projections) and lets the index's rendered-check keep only those
40
+ * that map to an element drawn on this diagram; the rest fall away. The
41
+ * `elements` argument of {@link validate} is ignored — the diagnostic set is
42
+ * the source of truth, and the default `RequestMarkersAction` requests the
43
+ * whole diagram anyway.
44
+ */
45
+ @injectable()
46
+ export class HydraniumGlspModelValidator<TRoot extends AstNode = AstNode> implements ModelValidator {
47
+ @inject(HydraniumTypes.SharedCoreServices) protected readonly sharedServices!: ServerSharedServices;
48
+ @inject(ModelState) protected readonly modelState!: AbstractHydraniumGlspState<TRoot>;
49
+
50
+ validate(_elements: GModelElement[], _reason?: string): Marker[] {
51
+ return this.markers();
52
+ }
53
+
54
+ /**
55
+ * Markers for everything drawn on this diagram, gathered from the
56
+ * diagnostics of every document the index reports as contributing a
57
+ * rendered element. Empty before the source model is loaded, and for any
58
+ * diagnostic whose element is not rendered here.
59
+ */
60
+ markers(): Marker[] {
61
+ // `sourceUri` is a definite-assignment field — undefined until the
62
+ // storage load flow calls `setSourceRoot`. Guard so a marker request that
63
+ // races initial load returns empty rather than scanning an empty index.
64
+ if (!this.modelState.sourceUri) {
65
+ return [];
66
+ }
67
+ const index = this.modelState.index;
68
+ const markers: Marker[] = [];
69
+ // Scan only the documents that contribute rendered elements, not every
70
+ // loaded document. The rendered-check below still keeps only what is
71
+ // drawn here.
72
+ for (const uri of index.renderedDocumentUris()) {
73
+ const document = this.sharedServices.workspace.LangiumDocuments.getDocument(URI.parse(uri));
74
+ if (!document) {
75
+ continue;
76
+ }
77
+ // Diagnostics are produced by the framework's `HydraniumDocumentValidator`,
78
+ // which emits `TransferLspDiagnostic` (LSP `Diagnostic` + `element` path).
79
+ // Parser/lexer diagnostics carry no `element`; the lookup drops them.
80
+ const diagnostics = document.diagnostics as TransferLspDiagnostic[] | undefined;
81
+ if (!diagnostics || diagnostics.length === 0) {
82
+ continue;
83
+ }
84
+ const root = document.parseResult.value;
85
+ const locator = this.sharedServices.ServiceRegistry.getServices(document.uri).workspace.AstNodeLocator;
86
+ const lookups: DiagnosticMarkerLookups = {
87
+ resolveElement: path => (path ? locator.getAstNode(root, path) : undefined),
88
+ renderedIdsFor: astNode => {
89
+ // A node renders as: every GModel element explicitly registered as
90
+ // representing it (reference projections, possibly several), plus its
91
+ // own stable id, for an element keyed directly by it. Keep only the
92
+ // ids actually drawn on this diagram.
93
+ const candidates = new Set(index.findElementIds(astNode));
94
+ const stableId = index.findId(astNode);
95
+ if (stableId !== undefined) {
96
+ candidates.add(stableId);
97
+ }
98
+ return [...candidates].filter(id => index.find(id) !== undefined);
99
+ }
100
+ };
101
+ markers.push(...diagnosticsToMarkers(diagnostics, lookups));
102
+ }
103
+ return markers;
104
+ }
105
+ }
@@ -0,0 +1,11 @@
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
+ export * from './diagnostic-markers.js';
11
+ export * from './hydranium-glsp-model-validator.js';