@hydranium/core 1.0.0-next.11 → 1.0.0-next.111

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 (415) hide show
  1. package/lib/documents/ast-document-manager.d.ts +75 -16
  2. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  3. package/lib/documents/ast-document-manager.js +57 -19
  4. package/lib/documents/ast-document-manager.js.map +1 -1
  5. package/lib/documents/client-ids.d.ts +5 -19
  6. package/lib/documents/client-ids.d.ts.map +1 -1
  7. package/lib/documents/client-ids.js +5 -19
  8. package/lib/documents/client-ids.js.map +1 -1
  9. package/lib/documents/hydranium-text-documents.d.ts +124 -36
  10. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  11. package/lib/documents/hydranium-text-documents.js +191 -51
  12. package/lib/documents/hydranium-text-documents.js.map +1 -1
  13. package/lib/documents/language-client-text-shadow.d.ts +41 -9
  14. package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
  15. package/lib/documents/language-client-text-shadow.js +56 -11
  16. package/lib/documents/language-client-text-shadow.js.map +1 -1
  17. package/lib/documents/self-save-registry.d.ts +37 -6
  18. package/lib/documents/self-save-registry.d.ts.map +1 -1
  19. package/lib/documents/self-save-registry.js +28 -15
  20. package/lib/documents/self-save-registry.js.map +1 -1
  21. package/lib/index.d.ts +3 -0
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +6 -0
  24. package/lib/index.js.map +1 -1
  25. package/lib/langium/ast-extension/ast-node-builder.d.ts +32 -9
  26. package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
  27. package/lib/langium/ast-extension/ast-node-builder.js +60 -22
  28. package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
  29. package/lib/langium/bootstrap.d.ts.map +1 -1
  30. package/lib/langium/bootstrap.js +16 -9
  31. package/lib/langium/bootstrap.js.map +1 -1
  32. package/lib/langium/config/configuration-provider.d.ts +81 -0
  33. package/lib/langium/config/configuration-provider.d.ts.map +1 -0
  34. package/lib/langium/config/configuration-provider.js +112 -0
  35. package/lib/langium/config/configuration-provider.js.map +1 -0
  36. package/lib/langium/config/index.d.ts +1 -0
  37. package/lib/langium/config/index.d.ts.map +1 -1
  38. package/lib/langium/config/index.js +1 -0
  39. package/lib/langium/config/index.js.map +1 -1
  40. package/lib/langium/diagnostics/lsp-logger.d.ts +17 -0
  41. package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
  42. package/lib/langium/diagnostics/lsp-logger.js +40 -6
  43. package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
  44. package/lib/langium/document-builder/build-pipeline-integration.d.ts +21 -1
  45. package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
  46. package/lib/langium/document-builder/build-pipeline-integration.js +32 -5
  47. package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
  48. package/lib/langium/document-builder/build-session.d.ts +93 -0
  49. package/lib/langium/document-builder/build-session.d.ts.map +1 -0
  50. package/lib/langium/document-builder/build-session.js +72 -0
  51. package/lib/langium/document-builder/build-session.js.map +1 -0
  52. package/lib/langium/document-builder/document-builder.d.ts +150 -7
  53. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  54. package/lib/langium/document-builder/document-builder.js +298 -11
  55. package/lib/langium/document-builder/document-builder.js.map +1 -1
  56. package/lib/langium/document-builder/index.d.ts +1 -0
  57. package/lib/langium/document-builder/index.d.ts.map +1 -1
  58. package/lib/langium/document-builder/index.js +1 -0
  59. package/lib/langium/document-builder/index.js.map +1 -1
  60. package/lib/langium/integration-services.d.ts +45 -1
  61. package/lib/langium/integration-services.d.ts.map +1 -1
  62. package/lib/langium/integration-services.js +10 -5
  63. package/lib/langium/integration-services.js.map +1 -1
  64. package/lib/langium/integrity/integrity-service.d.ts +16 -0
  65. package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
  66. package/lib/langium/integrity/integrity-service.js +110 -2
  67. package/lib/langium/integrity/integrity-service.js.map +1 -1
  68. package/lib/langium/keys/containment.d.ts +64 -0
  69. package/lib/langium/keys/containment.d.ts.map +1 -0
  70. package/lib/langium/keys/containment.js +53 -0
  71. package/lib/langium/keys/containment.js.map +1 -0
  72. package/lib/langium/keys/index.d.ts +1 -0
  73. package/lib/langium/keys/index.d.ts.map +1 -1
  74. package/lib/langium/keys/index.js +1 -0
  75. package/lib/langium/keys/index.js.map +1 -1
  76. package/lib/langium/keys/name-based-key-provider.d.ts +4 -0
  77. package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
  78. package/lib/langium/keys/name-based-key-provider.js +4 -0
  79. package/lib/langium/keys/name-based-key-provider.js.map +1 -1
  80. package/lib/langium/language-module.d.ts +25 -0
  81. package/lib/langium/language-module.d.ts.map +1 -1
  82. package/lib/langium/language-module.js +14 -0
  83. package/lib/langium/language-module.js.map +1 -1
  84. package/lib/langium/model-service/model-service.d.ts +161 -10
  85. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  86. package/lib/langium/model-service/model-service.js +174 -95
  87. package/lib/langium/model-service/model-service.js.map +1 -1
  88. package/lib/langium/module.d.ts +84 -15
  89. package/lib/langium/module.d.ts.map +1 -1
  90. package/lib/langium/module.js +26 -12
  91. package/lib/langium/module.js.map +1 -1
  92. package/lib/langium/naming/name-provider.d.ts +25 -4
  93. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  94. package/lib/langium/naming/name-provider.js +2 -1
  95. package/lib/langium/naming/name-provider.js.map +1 -1
  96. package/lib/langium/naming/name-separator-validation.d.ts +23 -0
  97. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  98. package/lib/langium/naming/name-separator-validation.js +30 -1
  99. package/lib/langium/naming/name-separator-validation.js.map +1 -1
  100. package/lib/langium/residency/cst-residency-service.d.ts +31 -11
  101. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  102. package/lib/langium/residency/cst-residency-service.js +13 -56
  103. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  104. package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
  105. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  106. package/lib/langium/scope/hydranium-scope-provider.js +56 -20
  107. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  108. package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
  109. package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
  110. package/lib/langium/serialization/abstract-serializer.js +12 -21
  111. package/lib/langium/serialization/abstract-serializer.js.map +1 -1
  112. package/lib/langium/service-registry.d.ts +15 -1
  113. package/lib/langium/service-registry.d.ts.map +1 -1
  114. package/lib/langium/service-registry.js +16 -0
  115. package/lib/langium/service-registry.js.map +1 -1
  116. package/lib/langium/shared-services.d.ts +15 -2
  117. package/lib/langium/shared-services.d.ts.map +1 -1
  118. package/lib/langium/shared-services.js.map +1 -1
  119. package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
  120. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  121. package/lib/langium/transfer/transfer-encoder.js +38 -16
  122. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  123. package/lib/langium/trivia/comment-preserver.d.ts +423 -0
  124. package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
  125. package/lib/langium/trivia/comment-preserver.js +906 -0
  126. package/lib/langium/trivia/comment-preserver.js.map +1 -0
  127. package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
  128. package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
  129. package/lib/langium/trivia/document-ending-preserver.js +48 -0
  130. package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
  131. package/lib/langium/trivia/index.d.ts +14 -0
  132. package/lib/langium/trivia/index.d.ts.map +1 -0
  133. package/lib/langium/trivia/index.js +14 -0
  134. package/lib/langium/trivia/index.js.map +1 -0
  135. package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
  136. package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
  137. package/lib/langium/trivia/trivia-contribution.js +10 -0
  138. package/lib/langium/trivia/trivia-contribution.js.map +1 -0
  139. package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
  140. package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
  141. package/lib/langium/trivia/trivia-preserver.js +10 -0
  142. package/lib/langium/trivia/trivia-preserver.js.map +1 -0
  143. package/lib/langium/trivia/trivia-service.d.ts +70 -0
  144. package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
  145. package/lib/langium/trivia/trivia-service.js +56 -0
  146. package/lib/langium/trivia/trivia-service.js.map +1 -0
  147. package/lib/langium/validation/document-validator.d.ts +294 -10
  148. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  149. package/lib/langium/validation/document-validator.js +448 -5
  150. package/lib/langium/validation/document-validator.js.map +1 -1
  151. package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
  152. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  153. package/lib/langium/workspace/document-uri-policy.js +3 -4
  154. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  155. package/lib/langium/workspace/file-system-provider.d.ts +9 -1
  156. package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
  157. package/lib/langium/workspace/file-system-provider.js +40 -7
  158. package/lib/langium/workspace/file-system-provider.js.map +1 -1
  159. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  160. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  161. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  162. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  163. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
  164. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  165. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  166. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  167. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
  168. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  169. package/lib/langium/workspace/in-memory-file-system-provider.js +47 -9
  170. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  171. package/lib/langium/workspace/initialize-workspace.d.ts +23 -2
  172. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  173. package/lib/langium/workspace/initialize-workspace.js +13 -5
  174. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  175. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  176. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  177. package/lib/langium/workspace/langium-documents.js +83 -28
  178. package/lib/langium/workspace/langium-documents.js.map +1 -1
  179. package/lib/langium/workspace/virtual-document.d.ts +18 -3
  180. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  181. package/lib/langium/workspace/virtual-document.js +19 -2
  182. package/lib/langium/workspace/virtual-document.js.map +1 -1
  183. package/lib/locale/index.d.ts +10 -0
  184. package/lib/locale/index.d.ts.map +1 -0
  185. package/lib/locale/index.js +10 -0
  186. package/lib/locale/index.js.map +1 -0
  187. package/lib/locale/server-locale.d.ts +73 -0
  188. package/lib/locale/server-locale.d.ts.map +1 -0
  189. package/lib/locale/server-locale.js +61 -0
  190. package/lib/locale/server-locale.js.map +1 -0
  191. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  192. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  193. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  194. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  195. package/lib/lsp/connection-features.d.ts +23 -0
  196. package/lib/lsp/connection-features.d.ts.map +1 -0
  197. package/lib/lsp/connection-features.js +60 -0
  198. package/lib/lsp/connection-features.js.map +1 -0
  199. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  200. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  201. package/lib/lsp/diagnostics-connection.js +53 -0
  202. package/lib/lsp/diagnostics-connection.js.map +1 -0
  203. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  204. package/lib/lsp/hydranium-document-update-handler.js +10 -1
  205. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  206. package/lib/lsp/index.d.ts +3 -1
  207. package/lib/lsp/index.d.ts.map +1 -1
  208. package/lib/lsp/index.js +3 -1
  209. package/lib/lsp/index.js.map +1 -1
  210. package/lib/lsp/lsp-latency.d.ts +39 -0
  211. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  212. package/lib/lsp/lsp-latency.js +58 -0
  213. package/lib/lsp/lsp-latency.js.map +1 -0
  214. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  215. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  216. package/lib/lsp/semantic-token-provider.js +55 -8
  217. package/lib/lsp/semantic-token-provider.js.map +1 -1
  218. package/lib/lsp/start-language-server.d.ts +5 -3
  219. package/lib/lsp/start-language-server.d.ts.map +1 -1
  220. package/lib/lsp/start-language-server.js +10 -3
  221. package/lib/lsp/start-language-server.js.map +1 -1
  222. package/lib/messages/carriers.d.ts +54 -0
  223. package/lib/messages/carriers.d.ts.map +1 -0
  224. package/lib/messages/carriers.js +62 -0
  225. package/lib/messages/carriers.js.map +1 -0
  226. package/lib/messages/index.d.ts +25 -0
  227. package/lib/messages/index.d.ts.map +1 -0
  228. package/lib/messages/index.js +25 -0
  229. package/lib/messages/index.js.map +1 -0
  230. package/lib/messages/renderer.d.ts +136 -0
  231. package/lib/messages/renderer.d.ts.map +1 -0
  232. package/lib/messages/renderer.js +159 -0
  233. package/lib/messages/renderer.js.map +1 -0
  234. package/lib/node/heap-ceiling.d.ts +61 -0
  235. package/lib/node/heap-ceiling.d.ts.map +1 -0
  236. package/lib/node/heap-ceiling.js +72 -0
  237. package/lib/node/heap-ceiling.js.map +1 -0
  238. package/lib/node/index.d.ts +1 -0
  239. package/lib/node/index.d.ts.map +1 -1
  240. package/lib/node/index.js +1 -0
  241. package/lib/node/index.js.map +1 -1
  242. package/lib/node/latency-from-env.d.ts +1 -1
  243. package/lib/node/latency-from-env.js +1 -1
  244. package/lib/node/node-file-system-provider.d.ts +35 -1
  245. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  246. package/lib/node/node-file-system-provider.js +140 -45
  247. package/lib/node/node-file-system-provider.js.map +1 -1
  248. package/lib/node/profile-capture.d.ts +8 -2
  249. package/lib/node/profile-capture.d.ts.map +1 -1
  250. package/lib/node/profile-capture.js.map +1 -1
  251. package/lib/node/profiling-run.d.ts +2 -2
  252. package/lib/node/profiling-run.js +1 -1
  253. package/lib/node/profiling-run.js.map +1 -1
  254. package/lib/node/rename-over-open-readers.d.ts +52 -0
  255. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  256. package/lib/node/rename-over-open-readers.js +66 -0
  257. package/lib/node/rename-over-open-readers.js.map +1 -0
  258. package/lib/node/socket-launcher.d.ts +15 -0
  259. package/lib/node/socket-launcher.d.ts.map +1 -1
  260. package/lib/node/socket-launcher.js +3 -2
  261. package/lib/node/socket-launcher.js.map +1 -1
  262. package/lib/testing/fake-document.d.ts +16 -4
  263. package/lib/testing/fake-document.d.ts.map +1 -1
  264. package/lib/testing/fake-document.js +18 -3
  265. package/lib/testing/fake-document.js.map +1 -1
  266. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  267. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  268. package/lib/testing/make-noop-shared-services.js +28 -2
  269. package/lib/testing/make-noop-shared-services.js.map +1 -1
  270. package/lib/testing/make-test-services.d.ts +45 -9
  271. package/lib/testing/make-test-services.d.ts.map +1 -1
  272. package/lib/testing/make-test-services.js +19 -3
  273. package/lib/testing/make-test-services.js.map +1 -1
  274. package/lib/testing/node/index.d.ts +1 -0
  275. package/lib/testing/node/index.d.ts.map +1 -1
  276. package/lib/testing/node/index.js +1 -0
  277. package/lib/testing/node/index.js.map +1 -1
  278. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  279. package/lib/testing/node/lsp-server-connection.js +5 -6
  280. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  281. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  282. package/lib/testing/node/scratch-workspace.js +9 -4
  283. package/lib/testing/node/scratch-workspace.js.map +1 -1
  284. package/lib/testing/node/spawned-server.d.ts +14 -0
  285. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  286. package/lib/testing/node/spawned-server.js +33 -1
  287. package/lib/testing/node/spawned-server.js.map +1 -1
  288. package/lib/testing/node/unhandled-rejections.d.ts +20 -0
  289. package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
  290. package/lib/testing/node/unhandled-rejections.js +32 -0
  291. package/lib/testing/node/unhandled-rejections.js.map +1 -0
  292. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  293. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  294. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  295. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  296. package/lib/testing/playwright/index.d.ts +1 -0
  297. package/lib/testing/playwright/index.d.ts.map +1 -1
  298. package/lib/testing/playwright/index.js +1 -0
  299. package/lib/testing/playwright/index.js.map +1 -1
  300. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  301. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  302. package/lib/testing/playwright/server-log-capture.js +21 -4
  303. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  304. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  305. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  306. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  307. package/lib/testing/stub-ast-document-manager.d.ts +5 -5
  308. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  309. package/lib/testing/stub-ast-document-manager.js +4 -6
  310. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  311. package/lib/testing/stub-document-builder.d.ts +8 -0
  312. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  313. package/lib/testing/stub-document-builder.js +22 -3
  314. package/lib/testing/stub-document-builder.js.map +1 -1
  315. package/lib/testing/stub-langium-documents.d.ts +3 -2
  316. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  317. package/lib/testing/stub-langium-documents.js.map +1 -1
  318. package/lib/testing/stub-model-service.d.ts +5 -4
  319. package/lib/testing/stub-model-service.d.ts.map +1 -1
  320. package/lib/testing/stub-model-service.js +2 -2
  321. package/lib/testing/stub-model-service.js.map +1 -1
  322. package/lib/util/connection-liveness.d.ts +7 -6
  323. package/lib/util/connection-liveness.d.ts.map +1 -1
  324. package/lib/util/connection-liveness.js +35 -14
  325. package/lib/util/connection-liveness.js.map +1 -1
  326. package/package.json +17 -8
  327. package/src/documents/ast-document-manager.ts +129 -36
  328. package/src/documents/client-ids.ts +5 -22
  329. package/src/documents/hydranium-text-documents.ts +248 -75
  330. package/src/documents/language-client-text-shadow.ts +59 -11
  331. package/src/documents/self-save-registry.ts +52 -13
  332. package/src/index.ts +6 -0
  333. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  334. package/src/langium/bootstrap.ts +16 -9
  335. package/src/langium/config/configuration-provider.ts +119 -0
  336. package/src/langium/config/index.ts +1 -0
  337. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  338. package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
  339. package/src/langium/document-builder/build-session.ts +87 -0
  340. package/src/langium/document-builder/document-builder.ts +339 -11
  341. package/src/langium/document-builder/index.ts +1 -0
  342. package/src/langium/integration-services.ts +67 -6
  343. package/src/langium/integrity/integrity-service.ts +116 -2
  344. package/src/langium/keys/containment.ts +87 -0
  345. package/src/langium/keys/index.ts +1 -0
  346. package/src/langium/keys/name-based-key-provider.ts +4 -0
  347. package/src/langium/language-module.ts +38 -0
  348. package/src/langium/model-service/model-service.ts +264 -32
  349. package/src/langium/module.ts +106 -21
  350. package/src/langium/naming/name-provider.ts +27 -5
  351. package/src/langium/naming/name-separator-validation.ts +34 -5
  352. package/src/langium/residency/cst-residency-service.ts +36 -14
  353. package/src/langium/scope/hydranium-scope-provider.ts +60 -22
  354. package/src/langium/serialization/abstract-serializer.ts +13 -32
  355. package/src/langium/service-registry.ts +19 -2
  356. package/src/langium/shared-services.ts +19 -2
  357. package/src/langium/transfer/transfer-encoder.ts +85 -24
  358. package/src/langium/trivia/comment-preserver.ts +1100 -0
  359. package/src/langium/trivia/document-ending-preserver.ts +55 -0
  360. package/src/langium/trivia/index.ts +14 -0
  361. package/src/langium/trivia/trivia-contribution.ts +39 -0
  362. package/src/langium/trivia/trivia-preserver.ts +54 -0
  363. package/src/langium/trivia/trivia-service.ts +99 -0
  364. package/src/langium/validation/document-validator.ts +537 -11
  365. package/src/langium/workspace/document-uri-policy.ts +3 -4
  366. package/src/langium/workspace/file-system-provider.ts +48 -8
  367. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  368. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  369. package/src/langium/workspace/in-memory-file-system-provider.ts +53 -10
  370. package/src/langium/workspace/initialize-workspace.ts +35 -5
  371. package/src/langium/workspace/langium-documents.ts +106 -33
  372. package/src/langium/workspace/virtual-document.ts +21 -3
  373. package/src/locale/index.ts +10 -0
  374. package/src/locale/server-locale.ts +86 -0
  375. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  376. package/src/lsp/connection-features.ts +67 -0
  377. package/src/lsp/diagnostics-connection.ts +56 -0
  378. package/src/lsp/hydranium-document-update-handler.ts +10 -1
  379. package/src/lsp/index.ts +3 -1
  380. package/src/lsp/lsp-latency.ts +62 -0
  381. package/src/lsp/semantic-token-provider.ts +74 -7
  382. package/src/lsp/start-language-server.ts +10 -3
  383. package/src/messages/carriers.ts +78 -0
  384. package/src/messages/index.ts +36 -0
  385. package/src/messages/renderer.ts +206 -0
  386. package/src/node/heap-ceiling.ts +108 -0
  387. package/src/node/index.ts +1 -0
  388. package/src/node/latency-from-env.ts +1 -1
  389. package/src/node/node-file-system-provider.ts +154 -47
  390. package/src/node/profile-capture.ts +8 -2
  391. package/src/node/profiling-run.ts +3 -3
  392. package/src/node/rename-over-open-readers.ts +78 -0
  393. package/src/node/socket-launcher.ts +18 -2
  394. package/src/testing/fake-document.ts +24 -5
  395. package/src/testing/make-noop-shared-services.ts +50 -2
  396. package/src/testing/make-test-services.ts +74 -18
  397. package/src/testing/node/index.ts +1 -0
  398. package/src/testing/node/lsp-server-connection.ts +5 -6
  399. package/src/testing/node/scratch-workspace.ts +9 -4
  400. package/src/testing/node/spawned-server.ts +35 -1
  401. package/src/testing/node/unhandled-rejections.ts +31 -0
  402. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  403. package/src/testing/playwright/index.ts +1 -0
  404. package/src/testing/playwright/server-log-capture.ts +24 -5
  405. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  406. package/src/testing/stub-ast-document-manager.ts +10 -14
  407. package/src/testing/stub-document-builder.ts +32 -3
  408. package/src/testing/stub-langium-documents.ts +3 -2
  409. package/src/testing/stub-model-service.ts +7 -6
  410. package/src/util/connection-liveness.ts +37 -14
  411. package/lib/lsp/instrument-connection.d.ts +0 -36
  412. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  413. package/lib/lsp/instrument-connection.js +0 -62
  414. package/lib/lsp/instrument-connection.js.map +0 -1
  415. package/src/lsp/instrument-connection.ts +0 -66
@@ -0,0 +1,86 @@
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 { Locale, Tracer } from '@hydranium/protocol';
11
+ import { type LogNameOptions } from '../langium/diagnostics/logger.js';
12
+ import type { ServerSharedServicesMinimal } from '../langium/shared-services.js';
13
+
14
+ /** Construction options for {@link ServerLocale}. */
15
+ export type ServerLocaleOptions = LogNameOptions;
16
+
17
+ /**
18
+ * The locale contract the `ServerLocale` slot holds, implemented by
19
+ * {@link DefaultServerLocale}.
20
+ *
21
+ * An interface rather than the class, so the slot is compared STRUCTURALLY —
22
+ * a class-typed slot carries its `protected` members into every assignability
23
+ * check, compared nominally, which makes it unsatisfiable across two physical
24
+ * copies of this package and unreplaceable by an adopter's own declaration.
25
+ */
26
+ export interface ServerLocale {
27
+ /** The locale, or `undefined` when no init supplied one — the framework's English. */
28
+ readonly value: Locale | undefined;
29
+ accept(locale: Locale): void;
30
+ }
31
+
32
+ /**
33
+ * The locale the server was handed at init, for whoever needs to render in the
34
+ * reading user's language. Held apart from the message renderer that reads it,
35
+ * so replacing the renderer cannot drop locale handling.
36
+ *
37
+ * A plain string in and out. A consumer that must react to a change rather than
38
+ * read the current value wraps this itself — nothing does today, and both real
39
+ * hosts respawn the server on a display-language switch.
40
+ */
41
+ export class DefaultServerLocale implements ServerLocale {
42
+ protected readonly tracer: Tracer;
43
+ protected current: Locale | undefined;
44
+
45
+ constructor(services: ServerSharedServicesMinimal, options: ServerLocaleOptions = {}) {
46
+ this.tracer = services.Tracer.for(options.logName ?? 'ServerLocale').trace('instantiated');
47
+ }
48
+
49
+ /** The locale, or `undefined` when no init supplied one — which means the framework's English. */
50
+ get value(): Locale | undefined {
51
+ return this.current;
52
+ }
53
+
54
+ /**
55
+ * Take the locale an init declared.
56
+ *
57
+ * **One locale per process, and the reason the OTHER heads need none of
58
+ * their own.** The data and GLSP heads publish a port over the LSP
59
+ * connection and are reached by forwarding a socket to it, so they are the
60
+ * same process as the LSP head that was handed this locale — there is no
61
+ * topology in which one of them serves a second frontend. The Theia backend
62
+ * is the case that would break it, and it holds no locale precisely because
63
+ * it serves every frontend at once; nothing there writes here.
64
+ *
65
+ * The framework accepts a locale and never sources one, and neither
66
+ * validates nor normalises the tag: rejecting an unfamiliar one would be
67
+ * selecting a locale.
68
+ *
69
+ * Override to ignore the argument, which is how a host pins a locale it
70
+ * already knows but has no LSP client to declare.
71
+ *
72
+ * **The line is at `info`, which is a deliberate exception to the
73
+ * per-service default.** A locale is written once per process and it is the
74
+ * one setting that silently changes every user-facing sentence the server
75
+ * produces, so a reader trying to explain an unexpected language has nothing
76
+ * else to look at: the framework ships no catalogue, so "no entry for this
77
+ * code" and "no locale declared" both render the English and are
78
+ * indistinguishable in the output. At `debug` the line is below the default
79
+ * threshold and therefore absent from exactly the log someone would be
80
+ * reading.
81
+ */
82
+ accept(locale: Locale): void {
83
+ this.current = locale;
84
+ this.tracer.info(`rendering messages in locale '${locale}'`);
85
+ }
86
+ }
@@ -7,9 +7,16 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { type AstNodeDescription, type ReferenceInfo, type Stream } from '@hydranium/langium';
10
+ import { type AstNodeDescription, type LangiumDocument, type ReferenceInfo, type Stream } from '@hydranium/langium';
11
11
  import { DefaultCompletionProvider, type CompletionContext, type CompletionValueItem } from '@hydranium/langium/lsp';
12
- import type { CompletionItem, InsertReplaceEdit } from 'vscode-languageserver';
12
+ import {
13
+ CompletionList,
14
+ type CancellationToken,
15
+ type CompletionItem,
16
+ type CompletionParams,
17
+ type InsertReplaceEdit,
18
+ type Position
19
+ } from 'vscode-languageserver';
13
20
  import { type ReferenceCandidateProvider } from '../../langium/scope/reference-candidate-provider.js';
14
21
  import { type ServerLanguageServices } from '../../langium/language-module.js';
15
22
 
@@ -52,6 +59,14 @@ import { type ServerLanguageServices } from '../../langium/language-module.js';
52
59
  * elements. The provider owns that pipeline in one place, so the
53
60
  * text-editor dropdown and the protocol candidate picker stay in sync; this
54
61
  * class keeps only the LSP-specific {@link fillCompletionItem} upgrade.
62
+ *
63
+ * **A comment is not a completion site.** Langium's context builder reads the
64
+ * non-hidden token stream alone, so a cursor in a comment is indistinguishable
65
+ * from one in the whitespace between the tokens surrounding it: the grammar's
66
+ * follow-set goes out as keyword proposals, and none of them is filtered,
67
+ * because the characters the user typed belong to the hidden token and the
68
+ * prefix matched against is therefore empty. {@link isInsideComment} suppresses
69
+ * the request instead.
55
70
  */
56
71
  export class HydraniumCompletionProvider extends DefaultCompletionProvider {
57
72
  protected readonly candidateProvider: ReferenceCandidateProvider;
@@ -61,6 +76,71 @@ export class HydraniumCompletionProvider extends DefaultCompletionProvider {
61
76
  this.candidateProvider = services.references.CandidateProvider;
62
77
  }
63
78
 
79
+ /**
80
+ * Answer no proposals at all when the cursor is inside a comment.
81
+ *
82
+ * Suppressing the whole request is what makes the rule hold for every
83
+ * proposal kind at once. Filtering the built contexts instead has to catch
84
+ * each context kind the base builds separately, and a kind added upstream
85
+ * arrives unguarded.
86
+ *
87
+ * The list is marked incomplete so the client re-asks on the next keystroke
88
+ * and proposals resume the moment the cursor leaves the comment.
89
+ */
90
+ override async getCompletion(
91
+ document: LangiumDocument,
92
+ params: CompletionParams,
93
+ cancelToken?: CancellationToken
94
+ ): Promise<CompletionList | undefined> {
95
+ if (this.isInsideComment(document, params.position)) {
96
+ return CompletionList.create([], true);
97
+ }
98
+ return super.getCompletion(document, params, cancelToken);
99
+ }
100
+
101
+ /**
102
+ * Whether `position` falls in text a comment terminal claims.
103
+ *
104
+ * The two comment kinds part company at their far edge. A single-line
105
+ * comment runs to the line break, so a cursor at its end offset is still in
106
+ * prose and that offset counts as inside. A multi-line comment is
107
+ * terminated, so a cursor at its end offset has passed the terminator and is
108
+ * back in code. A cursor ON the opening delimiter is outside either way,
109
+ * that offset being a legal position for whatever token the comment
110
+ * displaced.
111
+ *
112
+ * The hidden token stream decides rather than the CST. A leaf lookup by
113
+ * offset misses the position a cursor reaches by typing prose into a
114
+ * trailing comment, because that comment ends AT the line break and
115
+ * whitespace is discarded before a CST node is built over it — so the
116
+ * lookup answers nothing exactly where proposals are least wanted.
117
+ *
118
+ * An adopter whose comment syntax carries completable content — a
119
+ * documentation tag naming a type — narrows this to return `false` over that
120
+ * region rather than replacing {@link getCompletion}.
121
+ */
122
+ protected isInsideComment(document: LangiumDocument, position: Position): boolean {
123
+ const text = document.textDocument.getText();
124
+ const offset = document.textDocument.offsetAt(position);
125
+ const multilineCommentRules = this.grammarConfig.multilineCommentRules;
126
+ for (const token of this.lexer.tokenize(text).hidden) {
127
+ if (token.startOffset >= offset) {
128
+ // Hidden tokens arrive in document order, so no later one reaches back over the cursor.
129
+ return false;
130
+ }
131
+ // `endOffset` is optional on the token type and the image is not, so
132
+ // the width comes from the image and needs no absent-value branch.
133
+ const endOffset = token.startOffset + token.image.length;
134
+ if (offset < endOffset) {
135
+ return true;
136
+ }
137
+ if (offset === endOffset && !multilineCommentRules.includes(token.tokenType.name)) {
138
+ return true;
139
+ }
140
+ }
141
+ return false;
142
+ }
143
+
64
144
  /**
65
145
  * Delegate cross-reference candidate gathering to the candidate provider
66
146
  * so the LSP completion dropdown applies the same filter / canonical
@@ -0,0 +1,67 @@
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 { LogMessageNotification, MessageType, type Features, type RemoteConsole } from 'vscode-languageserver';
11
+ import { isConnectionGoneError } from '../util/connection-liveness.js';
12
+
13
+ /**
14
+ * Add Hydranium's connection-liveness handling to a language-server
15
+ * connection's console. The upstream `RemoteConsole` catches every failed log
16
+ * notification and prints `Sending log message failed`, so every ordinary
17
+ * teardown reports a failure; this console drops only a failure that
18
+ * {@link isConnectionGoneError} recognises and re-raises any other as an
19
+ * unhandled rejection.
20
+ *
21
+ * Supply it where the connection is created. `createConnection` builds the
22
+ * console from these features, so a connection that already exists keeps the
23
+ * upstream console for its whole lifetime.
24
+ */
25
+ export function withHydraniumLspFeatures<T extends Features>(features: T): T {
26
+ return {
27
+ ...features,
28
+ console: (Base: new () => RemoteConsole) =>
29
+ class extends Base {
30
+ override error(message: string): void {
31
+ this.sendLogMessage(MessageType.Error, message);
32
+ }
33
+
34
+ override warn(message: string): void {
35
+ this.sendLogMessage(MessageType.Warning, message);
36
+ }
37
+
38
+ override info(message: string): void {
39
+ this.sendLogMessage(MessageType.Info, message);
40
+ }
41
+
42
+ override log(message: string): void {
43
+ this.sendLogMessage(MessageType.Log, message);
44
+ }
45
+
46
+ override debug(message: string): void {
47
+ this.sendLogMessage(MessageType.Debug, message);
48
+ }
49
+
50
+ private sendLogMessage(type: MessageType, message: string): void {
51
+ try {
52
+ void this.connection.sendNotification(LogMessageNotification.type, { type, message }).catch(error => {
53
+ this.handleLogSendFailure(error);
54
+ });
55
+ } catch (error) {
56
+ this.handleLogSendFailure(error);
57
+ }
58
+ }
59
+
60
+ private handleLogSendFailure(error: unknown): void {
61
+ if (!isConnectionGoneError(error)) {
62
+ void Promise.reject(error);
63
+ }
64
+ }
65
+ }
66
+ } as T;
67
+ }
@@ -0,0 +1,56 @@
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 { Connection } from 'vscode-languageserver';
11
+ import { isConnectionGoneError } from '../util/connection-liveness.js';
12
+
13
+ const wrappedConnections = new WeakSet<object>();
14
+
15
+ /**
16
+ * Protect Langium's fire-and-forget diagnostics publish from the expected
17
+ * failure caused by a peer disconnecting during teardown.
18
+ *
19
+ * A publish issued after the connection is disposed throws synchronously
20
+ * instead of rejecting, and Langium's publisher is a document-phase listener,
21
+ * so an unguarded throw fails the build that validated the document. The
22
+ * `try` answers that case with a resolved promise; a guard on the promise
23
+ * alone never sees it.
24
+ *
25
+ * Otherwise the original promise is returned unchanged. The detached catch is
26
+ * deliberate: it marks the original promise as observed, suppresses only the
27
+ * expected connection-liveness error, and rethrows unexpected failures so they
28
+ * stay visible as unhandled rejections, as an unguarded fire-and-forget send
29
+ * would leave them.
30
+ */
31
+ export function guardDiagnosticsConnection(connection: Connection): void {
32
+ if (wrappedConnections.has(connection)) {
33
+ return;
34
+ }
35
+ wrappedConnections.add(connection);
36
+
37
+ const sendDiagnostics = connection.sendDiagnostics.bind(connection);
38
+ connection.sendDiagnostics = params => {
39
+ let result: Promise<void>;
40
+ try {
41
+ result = sendDiagnostics(params);
42
+ } catch (error: unknown) {
43
+ if (isConnectionGoneError(error)) {
44
+ return Promise.resolve();
45
+ }
46
+ throw error;
47
+ }
48
+ void result.catch(error => {
49
+ if (isConnectionGoneError(error)) {
50
+ return;
51
+ }
52
+ throw error;
53
+ });
54
+ return result;
55
+ };
56
+ }
@@ -16,6 +16,7 @@ import { LANGUAGE_CLIENT_ID } from '../documents/client-ids.js';
16
16
  import { type SelfSaveRegistry } from '../documents/self-save-registry.js';
17
17
  import { type WritableFileSystemProvider } from '../documents/ast-document-manager.js';
18
18
  import { type HydraniumTextDocuments } from '../documents/hydranium-text-documents.js';
19
+ import { isConnectionGoneError } from '../util/connection-liveness.js';
19
20
  import { type DidChangeWatchedFilesParams, type FileEvent, FileChangeType, type TextDocumentChangeEvent } from 'vscode-languageserver';
20
21
  import { type TextDocument } from 'vscode-languageserver-textdocument';
21
22
 
@@ -257,7 +258,7 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
257
258
  }
258
259
  const uri = UriUtils.toUri(change.uri);
259
260
  const mtimeMs = await this.fileSystemProvider.mtimeMs?.(uri);
260
- if (mtimeMs !== undefined && this.selfSaveRegistry.matches(uri.fsPath, mtimeMs)) {
261
+ if (mtimeMs !== undefined && this.selfSaveRegistry.isRegistered(uri.fsPath, mtimeMs)) {
261
262
  return undefined;
262
263
  }
263
264
  return change;
@@ -338,6 +339,14 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
338
339
  })
339
340
  )
340
341
  .catch(err => {
342
+ // A deferred rebuild can finish after the LSP peer has gone away.
343
+ // Unless the head was started through `startLanguageServer`, whose
344
+ // guard absorbs it, the diagnostics publish then fails this build:
345
+ // a routine teardown race, not a failed workspace update. Keep
346
+ // genuine update failures visible.
347
+ if (isConnectionGoneError(err)) {
348
+ return;
349
+ }
341
350
  const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
342
351
  this.logger.error(`Workspace initialization failed. Could not perform document update. ${detail}`);
343
352
  });
package/src/lsp/index.ts CHANGED
@@ -8,7 +8,9 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  export * from './completion/hydranium-completion-provider.js';
11
- export * from './instrument-connection.js';
11
+ export * from './lsp-latency.js';
12
+ export * from './connection-features.js';
13
+ export * from './diagnostics-connection.js';
12
14
  export * from './semantic-token-provider.js';
13
15
  export * from './language-module.js';
14
16
  export * from './shared-module.js';
@@ -0,0 +1,62 @@
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 { LatencyCollector } from '@hydranium/protocol';
11
+ import type { ConnectionOptions, Message } from 'vscode-jsonrpc';
12
+
13
+ /**
14
+ * Connection options that time every inbound LSP message into `latency` under
15
+ * its method name — the LSP analog of the data-server RPC chokepoint.
16
+ *
17
+ * Pass the result to `createConnection` where the head builds its connection:
18
+ * `createConnection(ProposedFeatures.all, lspLatencyOptions(latency))`. The
19
+ * transport is untouched, so a head keeps whatever `--stdio` / `--socket`
20
+ * selection its arguments make.
21
+ *
22
+ * **`messageStrategy` is the seam because it is the only one the handlers
23
+ * actually pass through.** `vscode-languageserver` builds its `Connection` as an
24
+ * object whose `onHover`, `onCompletion` and every other named helper register
25
+ * against the inner protocol connection captured in their closure — not against
26
+ * the returned object's own `onRequest`. Langium registers exclusively through
27
+ * those helpers, so decorating the `Connection` reaches none of them and reports
28
+ * an empty LSP half no matter how much traffic ran. `handleMessage` sits on the
29
+ * dispatch path itself, which every request and notification crosses, so it also
30
+ * catches `initialize` — special-cased into `onInitialize` and invisible to any
31
+ * `onRequest` decoration.
32
+ *
33
+ * What it measures is **dispatch plus handler**, not handler alone: the wait a
34
+ * client actually experiences, including time queued behind other work.
35
+ *
36
+ * When `latency` is `undefined` the seam is off and this returns `undefined`, so
37
+ * a head can pass the result of an env-gated factory (`latencyFromEnv()`)
38
+ * straight through and pay nothing when timing is not enabled.
39
+ */
40
+ export function lspLatencyOptions(latency: LatencyCollector | undefined): ConnectionOptions | undefined {
41
+ if (!latency) {
42
+ return undefined;
43
+ }
44
+ return {
45
+ messageStrategy: {
46
+ handleMessage: (message: Message, next: (message: Message) => void | Promise<void>) => {
47
+ // Responses to requests the SERVER sent carry no method and are not
48
+ // work this head performed; timing them would report the client's
49
+ // latency under a name this collector cannot supply.
50
+ if (!hasMethod(message)) {
51
+ return next(message);
52
+ }
53
+ return latency.time(message.method, () => next(message));
54
+ }
55
+ }
56
+ };
57
+ }
58
+
59
+ /** Narrow to the messages that name an operation — requests and notifications. */
60
+ function hasMethod(message: Message): message is Message & { method: string } {
61
+ return typeof (message as { method?: unknown }).method === 'string';
62
+ }
@@ -14,7 +14,9 @@ import {
14
14
  CstUtils,
15
15
  GrammarAST,
16
16
  interruptAndCheck,
17
+ isLeafCstNode,
17
18
  type LangiumDocument,
19
+ type LeafCstNode,
18
20
  type ReferenceInfo
19
21
  } from '@hydranium/langium';
20
22
  import { AbstractSemanticTokenProvider, type SemanticTokenAcceptor } from '@hydranium/langium/lsp';
@@ -54,6 +56,23 @@ export interface HydraniumSemanticTokenProviderOptions {
54
56
  * render in the editor's default foreground.
55
57
  */
56
58
  readonly highlightKeywords?: boolean;
59
+
60
+ /**
61
+ * Also emit a `comment` token for every comment leaf of the CST.
62
+ *
63
+ * OFF by default for the same reason as {@link highlightKeywords}, and the
64
+ * loss is larger: a TextMate grammar distinguishes `comment.line` from
65
+ * `comment.block` and scopes the delimiters separately, where a semantic
66
+ * token carries one flat `comment` that overrides all of them. It exists
67
+ * for the host that ships no client-side grammar, where a comment otherwise
68
+ * renders in the editor's default foreground.
69
+ *
70
+ * What counts as a comment is every hidden terminal the lexer retains,
71
+ * which is every hidden terminal whose pattern is not whitespace. A grammar
72
+ * whose hidden non-whitespace terminal means something other than a comment
73
+ * is miscoloured, and this option is the place to say no.
74
+ */
75
+ readonly highlightComments?: boolean;
57
76
  }
58
77
 
59
78
  /**
@@ -133,7 +152,9 @@ export abstract class AbstractHydraniumSemanticTokenProvider extends AbstractSem
133
152
  cancelToken: CancellationToken
134
153
  ): Promise<void> {
135
154
  await super.computeHighlighting(document, acceptor, cancelToken);
136
- if (this.options.highlightKeywords !== true) {
155
+ const keywords = this.options.highlightKeywords === true;
156
+ const comments = this.options.highlightComments === true;
157
+ if (!keywords && !comments) {
137
158
  return;
138
159
  }
139
160
  const root = document.parseResult.value.$cstNode;
@@ -142,14 +163,60 @@ export abstract class AbstractHydraniumSemanticTokenProvider extends AbstractSem
142
163
  }
143
164
  for (const cst of CstUtils.streamCst(root)) {
144
165
  await interruptAndCheck(cancelToken);
145
- // The grammar source is the whole filter. A keyword is always a leaf,
146
- // and the hidden leaves a comment or a run of whitespace contributes
147
- // are sourced from a terminal rule rather than a `Keyword` — so
148
- // neither a composite-node check nor a hidden-token check adds
149
- // anything here.
150
- if (GrammarAST.isKeyword(cst.grammarSource)) {
166
+ // A keyword is always a leaf with a `Keyword` grammar source, so that
167
+ // check alone separates it from everything else in the walk.
168
+ if (keywords && GrammarAST.isKeyword(cst.grammarSource)) {
151
169
  acceptor({ cst, type: SemanticTokenTypes.keyword });
152
170
  }
171
+ // A comment leaf cannot be recognised the same way — the CST builder
172
+ // adds hidden nodes from the token alone, so `grammarSource` is
173
+ // undefined, which is also why the keyword filter never matches one.
174
+ //
175
+ // `hidden` alone IS the comment test, and nothing narrower is needed:
176
+ // the token builder groups a hidden terminal as `Lexer.SKIPPED` when
177
+ // its pattern matches whitespace and as `hidden` otherwise, so the
178
+ // lexer discards whitespace before a CST node is ever built and every
179
+ // hidden leaf that survives is a comment. Classifying the terminal
180
+ // again here would be a second guard for the same property, which no
181
+ // test could then distinguish. An adopter whose token builder RETAINS
182
+ // whitespace breaks that equivalence and would colour its gaps.
183
+ if (comments && isLeafCstNode(cst) && cst.hidden) {
184
+ this.acceptComment(cst, acceptor);
185
+ }
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Emit a comment as ONE TOKEN PER LINE, never as one multi-line range.
191
+ *
192
+ * A block comment is the first token this provider emits that can span
193
+ * lines, and the upstream encoder mishandles that for a client which does
194
+ * not advertise `multilineTokenSupport` — the split path computes the first
195
+ * line's length as `nextLineOffset - range.start.character - 1`, subtracting
196
+ * a COLUMN from a document OFFSET, so the length is right only for a token
197
+ * starting on line 0 and overruns its line everywhere else. No client in
198
+ * this repository advertises that capability, so the broken path is the one
199
+ * taken. Splitting here means every range handed over starts and ends on one
200
+ * line and the arithmetic is never reached.
201
+ *
202
+ * Lines come from the leaf's own text rather than from the document, so
203
+ * nothing has to re-derive where the comment ends.
204
+ */
205
+ protected acceptComment(cst: LeafCstNode, acceptor: SemanticTokenAcceptor): void {
206
+ const lines = cst.text.split('\n');
207
+ for (let index = 0; index < lines.length; index++) {
208
+ // A `\r\n` document leaves the carriage return on the line's end;
209
+ // colouring it would extend the token past the visible text.
210
+ const length = lines[index].replace(/\r$/, '').length;
211
+ const character = index === 0 ? cst.range.start.character : 0;
212
+ if (length === 0) {
213
+ continue;
214
+ }
215
+ const line = cst.range.start.line + index;
216
+ acceptor({
217
+ range: { start: { line, character }, end: { line, character: character + length } },
218
+ type: SemanticTokenTypes.comment
219
+ });
153
220
  }
154
221
  }
155
222
 
@@ -13,6 +13,7 @@ import {
13
13
  type ServiceRequirements
14
14
  } from '@hydranium/langium/lsp';
15
15
  import { HydraniumDocumentUpdateHandler } from './hydranium-document-update-handler.js';
16
+ import { guardDiagnosticsConnection } from './diagnostics-connection.js';
16
17
 
17
18
  /**
18
19
  * Fail the LSP head's start when its shared module was never composed.
@@ -58,11 +59,17 @@ export function assertLspHeadComposed(services: LangiumSharedServices): void {
58
59
 
59
60
  /**
60
61
  * The framework's LSP server entry point: {@link assertLspHeadComposed}, then
61
- * Langium's `startLanguageServer` unchanged. Signature-compatible with
62
- * Langium's, so it is a drop-in for adopters importing from
63
- * `@hydranium/core/lsp`.
62
+ * {@link guardDiagnosticsConnection} on the tree's connection, then Langium's
63
+ * `startLanguageServer`. Signature-compatible with Langium's, so it is a
64
+ * drop-in for adopters importing from `@hydranium/core/lsp`. Calling Langium's
65
+ * directly skips both: the composition check, and the guard without which a
66
+ * publish racing teardown fails the build that validated the document.
64
67
  */
65
68
  export function startLanguageServer(services: LangiumSharedServices, serviceRequirements: Partial<ServiceRequirements> = {}): void {
66
69
  assertLspHeadComposed(services);
70
+ const connection = services.lsp.Connection;
71
+ if (connection) {
72
+ guardDiagnosticsConnection(connection);
73
+ }
67
74
  startLangiumLanguageServer(services, serviceRequirements);
68
75
  }
@@ -0,0 +1,78 @@
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 {
11
+ hasMessageIdentity,
12
+ messageData,
13
+ type HydraniumMessageData,
14
+ type MessageDefinition,
15
+ type ParamsArg,
16
+ type ResolvedMessage
17
+ } from '@hydranium/protocol';
18
+ import type { AstNode, DiagnosticData, DiagnosticInfo, Properties, ValidationAcceptor } from '@hydranium/langium';
19
+ import { Diagnostic } from 'vscode-languageserver-protocol';
20
+
21
+ /**
22
+ * Raise a validation diagnostic from a declaration, so the call site never
23
+ * restates the code or the sentence.
24
+ *
25
+ * `code` is omitted from `info` because this owns it — the framework claims LSP
26
+ * `Diagnostic.code` for framework codes, which collides with no adopter today
27
+ * and is a stated rule rather than an accident. `data` is deliberately NOT
28
+ * omitted: Langium owns `data.code` for code-action dispatch, so a caller has to
29
+ * be able to request a quick fix. The identity is merged OVER whatever they
30
+ * pass, which makes the two conventions co-exist rather than compete.
31
+ *
32
+ * `data` admits adopter-owned keys beside Langium's, which is why the index
33
+ * signature is there rather than a bare `Partial<DiagnosticData>`. Langium's
34
+ * shape is closed, and a diagnostic that carries a COMPANION payload for its own
35
+ * surface to read — the field a form must highlight, say — has nowhere else to
36
+ * put it: an encoder projecting that payload reads `diagnostic.data`, so the
37
+ * narrower type made the identity and the companion mutually exclusive.
38
+ *
39
+ * The identity lands in both `code` and `data.hydranium` on purpose. `code` is
40
+ * what survives to the editor surface; Theia's converter drops `data` before the
41
+ * squiggle, so a parameterised diagnostic falls back to the server's English
42
+ * there while any adopter-owned surface can still render the parameterised form
43
+ * from the marker store.
44
+ *
45
+ * `DiagnosticInfo` must be Langium's own type: a `Parameters<ValidationAcceptor>[2]`
46
+ * shortcut compiles but loses the `property?: Properties<N>` relation. The `data`
47
+ * re-type has to be an intersection rather than an `Omit` alone, because Langium
48
+ * declares `data?: unknown` and an `unknown` cannot be spread.
49
+ */
50
+ export function acceptMessage<S extends string, N extends AstNode, P extends Properties<N> = Properties<N>>(
51
+ accept: ValidationAcceptor,
52
+ severity: 'error' | 'warning' | 'info' | 'hint',
53
+ message: MessageDefinition<S>,
54
+ info: Omit<DiagnosticInfo<N, P>, 'code' | 'data'> & { data?: Partial<DiagnosticData> & Record<string, unknown> },
55
+ ...params: ParamsArg<S>
56
+ ): void {
57
+ const data: HydraniumMessageData & Partial<DiagnosticData> & Record<string, unknown> = {
58
+ ...info.data,
59
+ ...messageData(message, ...params)
60
+ };
61
+ accept(severity, message.format(...params), { ...info, code: message.code, data });
62
+ }
63
+
64
+ /**
65
+ * Recover the identity from a published diagnostic, for a surface that
66
+ * identifies or renders diagnostics itself.
67
+ *
68
+ * `Diagnostic.message` is `string | MarkupContent` in LSP 3.17+, so the text
69
+ * comes from upstream's own `Diagnostic.getMessageString` rather than a
70
+ * hand-rolled narrowing — consuming the library in its style, and one fewer
71
+ * place restating the union.
72
+ */
73
+ export function resolvedFromDiagnostic(diagnostic: Diagnostic): ResolvedMessage | undefined {
74
+ if (!hasMessageIdentity(diagnostic.data)) {
75
+ return undefined;
76
+ }
77
+ return { ...diagnostic.data.hydranium, text: Diagnostic.getMessageString(diagnostic) };
78
+ }