@hydranium/core 1.0.0-next.12 → 1.0.0-next.127

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 (482) hide show
  1. package/README.md +5 -1
  2. package/lib/documents/ast-document-manager.d.ts +92 -19
  3. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  4. package/lib/documents/ast-document-manager.js +114 -26
  5. package/lib/documents/ast-document-manager.js.map +1 -1
  6. package/lib/documents/client-ids.d.ts +5 -19
  7. package/lib/documents/client-ids.d.ts.map +1 -1
  8. package/lib/documents/client-ids.js +5 -19
  9. package/lib/documents/client-ids.js.map +1 -1
  10. package/lib/documents/client-session-errors.d.ts +15 -0
  11. package/lib/documents/client-session-errors.d.ts.map +1 -0
  12. package/lib/documents/client-session-errors.js +15 -0
  13. package/lib/documents/client-session-errors.js.map +1 -0
  14. package/lib/documents/client-session-registry.d.ts +138 -0
  15. package/lib/documents/client-session-registry.d.ts.map +1 -0
  16. package/lib/documents/client-session-registry.js +184 -0
  17. package/lib/documents/client-session-registry.js.map +1 -0
  18. package/lib/documents/file-system-task-queue.d.ts +49 -0
  19. package/lib/documents/file-system-task-queue.d.ts.map +1 -0
  20. package/lib/documents/file-system-task-queue.js +34 -0
  21. package/lib/documents/file-system-task-queue.js.map +1 -0
  22. package/lib/documents/hydranium-text-documents.d.ts +386 -57
  23. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  24. package/lib/documents/hydranium-text-documents.js +701 -114
  25. package/lib/documents/hydranium-text-documents.js.map +1 -1
  26. package/lib/documents/index.d.ts +3 -0
  27. package/lib/documents/index.d.ts.map +1 -1
  28. package/lib/documents/index.js +3 -0
  29. package/lib/documents/index.js.map +1 -1
  30. package/lib/documents/language-client-text-shadow.d.ts +41 -9
  31. package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
  32. package/lib/documents/language-client-text-shadow.js +56 -11
  33. package/lib/documents/language-client-text-shadow.js.map +1 -1
  34. package/lib/documents/self-save-registry.d.ts +37 -6
  35. package/lib/documents/self-save-registry.d.ts.map +1 -1
  36. package/lib/documents/self-save-registry.js +28 -15
  37. package/lib/documents/self-save-registry.js.map +1 -1
  38. package/lib/index.d.ts +3 -0
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +6 -0
  41. package/lib/index.js.map +1 -1
  42. package/lib/langium/ast-extension/ast-node-builder.d.ts +32 -9
  43. package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
  44. package/lib/langium/ast-extension/ast-node-builder.js +60 -22
  45. package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
  46. package/lib/langium/bootstrap.d.ts.map +1 -1
  47. package/lib/langium/bootstrap.js +16 -9
  48. package/lib/langium/bootstrap.js.map +1 -1
  49. package/lib/langium/config/configuration-provider.d.ts +81 -0
  50. package/lib/langium/config/configuration-provider.d.ts.map +1 -0
  51. package/lib/langium/config/configuration-provider.js +112 -0
  52. package/lib/langium/config/configuration-provider.js.map +1 -0
  53. package/lib/langium/config/index.d.ts +1 -0
  54. package/lib/langium/config/index.d.ts.map +1 -1
  55. package/lib/langium/config/index.js +1 -0
  56. package/lib/langium/config/index.js.map +1 -1
  57. package/lib/langium/diagnostics/lsp-logger.d.ts +17 -0
  58. package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
  59. package/lib/langium/diagnostics/lsp-logger.js +40 -6
  60. package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
  61. package/lib/langium/document-builder/build-pipeline-integration.d.ts +21 -1
  62. package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
  63. package/lib/langium/document-builder/build-pipeline-integration.js +32 -5
  64. package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
  65. package/lib/langium/document-builder/build-session.d.ts +93 -0
  66. package/lib/langium/document-builder/build-session.d.ts.map +1 -0
  67. package/lib/langium/document-builder/build-session.js +72 -0
  68. package/lib/langium/document-builder/build-session.js.map +1 -0
  69. package/lib/langium/document-builder/document-builder.d.ts +165 -9
  70. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  71. package/lib/langium/document-builder/document-builder.js +313 -13
  72. package/lib/langium/document-builder/document-builder.js.map +1 -1
  73. package/lib/langium/document-builder/index.d.ts +1 -0
  74. package/lib/langium/document-builder/index.d.ts.map +1 -1
  75. package/lib/langium/document-builder/index.js +1 -0
  76. package/lib/langium/document-builder/index.js.map +1 -1
  77. package/lib/langium/integration-services.d.ts +45 -1
  78. package/lib/langium/integration-services.d.ts.map +1 -1
  79. package/lib/langium/integration-services.js +10 -5
  80. package/lib/langium/integration-services.js.map +1 -1
  81. package/lib/langium/integrity/integrity-rule.d.ts +13 -5
  82. package/lib/langium/integrity/integrity-rule.d.ts.map +1 -1
  83. package/lib/langium/integrity/integrity-rule.js.map +1 -1
  84. package/lib/langium/integrity/integrity-service.d.ts +50 -6
  85. package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
  86. package/lib/langium/integrity/integrity-service.js +193 -17
  87. package/lib/langium/integrity/integrity-service.js.map +1 -1
  88. package/lib/langium/keys/containment.d.ts +64 -0
  89. package/lib/langium/keys/containment.d.ts.map +1 -0
  90. package/lib/langium/keys/containment.js +53 -0
  91. package/lib/langium/keys/containment.js.map +1 -0
  92. package/lib/langium/keys/index.d.ts +1 -0
  93. package/lib/langium/keys/index.d.ts.map +1 -1
  94. package/lib/langium/keys/index.js +1 -0
  95. package/lib/langium/keys/index.js.map +1 -1
  96. package/lib/langium/keys/name-based-key-provider.d.ts +4 -0
  97. package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
  98. package/lib/langium/keys/name-based-key-provider.js +4 -0
  99. package/lib/langium/keys/name-based-key-provider.js.map +1 -1
  100. package/lib/langium/language-module.d.ts +27 -2
  101. package/lib/langium/language-module.d.ts.map +1 -1
  102. package/lib/langium/language-module.js +16 -2
  103. package/lib/langium/language-module.js.map +1 -1
  104. package/lib/langium/model-service/client-session.d.ts +299 -0
  105. package/lib/langium/model-service/client-session.d.ts.map +1 -0
  106. package/lib/langium/model-service/client-session.js +379 -0
  107. package/lib/langium/model-service/client-session.js.map +1 -0
  108. package/lib/langium/model-service/index.d.ts +1 -0
  109. package/lib/langium/model-service/index.d.ts.map +1 -1
  110. package/lib/langium/model-service/index.js +1 -0
  111. package/lib/langium/model-service/index.js.map +1 -1
  112. package/lib/langium/model-service/model-service.d.ts +226 -162
  113. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  114. package/lib/langium/model-service/model-service.js +203 -309
  115. package/lib/langium/model-service/model-service.js.map +1 -1
  116. package/lib/langium/module.d.ts +95 -16
  117. package/lib/langium/module.d.ts.map +1 -1
  118. package/lib/langium/module.js +30 -12
  119. package/lib/langium/module.js.map +1 -1
  120. package/lib/langium/naming/name-provider.d.ts +25 -4
  121. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  122. package/lib/langium/naming/name-provider.js +2 -1
  123. package/lib/langium/naming/name-provider.js.map +1 -1
  124. package/lib/langium/naming/name-separator-validation.d.ts +23 -0
  125. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  126. package/lib/langium/naming/name-separator-validation.js +30 -1
  127. package/lib/langium/naming/name-separator-validation.js.map +1 -1
  128. package/lib/langium/residency/cst-residency-service.d.ts +31 -11
  129. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  130. package/lib/langium/residency/cst-residency-service.js +13 -56
  131. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  132. package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
  133. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  134. package/lib/langium/scope/hydranium-scope-provider.js +56 -20
  135. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  136. package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
  137. package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
  138. package/lib/langium/serialization/abstract-serializer.js +12 -21
  139. package/lib/langium/serialization/abstract-serializer.js.map +1 -1
  140. package/lib/langium/service-registry.d.ts +15 -1
  141. package/lib/langium/service-registry.d.ts.map +1 -1
  142. package/lib/langium/service-registry.js +16 -0
  143. package/lib/langium/service-registry.js.map +1 -1
  144. package/lib/langium/shared-services.d.ts +15 -2
  145. package/lib/langium/shared-services.d.ts.map +1 -1
  146. package/lib/langium/shared-services.js.map +1 -1
  147. package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
  148. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  149. package/lib/langium/transfer/transfer-encoder.js +38 -16
  150. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  151. package/lib/langium/trivia/comment-preserver.d.ts +423 -0
  152. package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
  153. package/lib/langium/trivia/comment-preserver.js +906 -0
  154. package/lib/langium/trivia/comment-preserver.js.map +1 -0
  155. package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
  156. package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
  157. package/lib/langium/trivia/document-ending-preserver.js +48 -0
  158. package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
  159. package/lib/langium/trivia/index.d.ts +14 -0
  160. package/lib/langium/trivia/index.d.ts.map +1 -0
  161. package/lib/langium/trivia/index.js +14 -0
  162. package/lib/langium/trivia/index.js.map +1 -0
  163. package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
  164. package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
  165. package/lib/langium/trivia/trivia-contribution.js +10 -0
  166. package/lib/langium/trivia/trivia-contribution.js.map +1 -0
  167. package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
  168. package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
  169. package/lib/langium/trivia/trivia-preserver.js +10 -0
  170. package/lib/langium/trivia/trivia-preserver.js.map +1 -0
  171. package/lib/langium/trivia/trivia-service.d.ts +70 -0
  172. package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
  173. package/lib/langium/trivia/trivia-service.js +56 -0
  174. package/lib/langium/trivia/trivia-service.js.map +1 -0
  175. package/lib/langium/update-rewrite/update-rewrite.d.ts +1 -1
  176. package/lib/langium/validation/document-validator.d.ts +294 -10
  177. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  178. package/lib/langium/validation/document-validator.js +448 -5
  179. package/lib/langium/validation/document-validator.js.map +1 -1
  180. package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
  181. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  182. package/lib/langium/workspace/document-uri-policy.js +3 -4
  183. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  184. package/lib/langium/workspace/file-system-provider.d.ts +9 -1
  185. package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
  186. package/lib/langium/workspace/file-system-provider.js +40 -7
  187. package/lib/langium/workspace/file-system-provider.js.map +1 -1
  188. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  189. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  190. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  191. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  192. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
  193. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  194. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  195. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  196. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
  197. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  198. package/lib/langium/workspace/in-memory-file-system-provider.js +55 -9
  199. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  200. package/lib/langium/workspace/initialize-workspace.d.ts +31 -2
  201. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  202. package/lib/langium/workspace/initialize-workspace.js +13 -5
  203. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  204. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  205. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  206. package/lib/langium/workspace/langium-documents.js +83 -28
  207. package/lib/langium/workspace/langium-documents.js.map +1 -1
  208. package/lib/langium/workspace/virtual-document.d.ts +18 -3
  209. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  210. package/lib/langium/workspace/virtual-document.js +19 -2
  211. package/lib/langium/workspace/virtual-document.js.map +1 -1
  212. package/lib/langium/workspace/write-lock-scope.d.ts +1 -1
  213. package/lib/langium/workspace/write-lock-scope.js +1 -1
  214. package/lib/locale/index.d.ts +10 -0
  215. package/lib/locale/index.d.ts.map +1 -0
  216. package/lib/locale/index.js +10 -0
  217. package/lib/locale/index.js.map +1 -0
  218. package/lib/locale/server-locale.d.ts +73 -0
  219. package/lib/locale/server-locale.d.ts.map +1 -0
  220. package/lib/locale/server-locale.js +61 -0
  221. package/lib/locale/server-locale.js.map +1 -0
  222. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  223. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  224. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  225. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  226. package/lib/lsp/connection-features.d.ts +23 -0
  227. package/lib/lsp/connection-features.d.ts.map +1 -0
  228. package/lib/lsp/connection-features.js +60 -0
  229. package/lib/lsp/connection-features.js.map +1 -0
  230. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  231. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  232. package/lib/lsp/diagnostics-connection.js +53 -0
  233. package/lib/lsp/diagnostics-connection.js.map +1 -0
  234. package/lib/lsp/hydranium-document-update-handler.d.ts +70 -62
  235. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  236. package/lib/lsp/hydranium-document-update-handler.js +130 -75
  237. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  238. package/lib/lsp/index.d.ts +3 -1
  239. package/lib/lsp/index.d.ts.map +1 -1
  240. package/lib/lsp/index.js +3 -1
  241. package/lib/lsp/index.js.map +1 -1
  242. package/lib/lsp/lsp-latency.d.ts +39 -0
  243. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  244. package/lib/lsp/lsp-latency.js +58 -0
  245. package/lib/lsp/lsp-latency.js.map +1 -0
  246. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  247. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  248. package/lib/lsp/semantic-token-provider.js +55 -8
  249. package/lib/lsp/semantic-token-provider.js.map +1 -1
  250. package/lib/lsp/shared-module.d.ts +4 -8
  251. package/lib/lsp/shared-module.d.ts.map +1 -1
  252. package/lib/lsp/shared-module.js.map +1 -1
  253. package/lib/lsp/start-language-server.d.ts +8 -7
  254. package/lib/lsp/start-language-server.d.ts.map +1 -1
  255. package/lib/lsp/start-language-server.js +13 -7
  256. package/lib/lsp/start-language-server.js.map +1 -1
  257. package/lib/messages/carriers.d.ts +54 -0
  258. package/lib/messages/carriers.d.ts.map +1 -0
  259. package/lib/messages/carriers.js +62 -0
  260. package/lib/messages/carriers.js.map +1 -0
  261. package/lib/messages/index.d.ts +25 -0
  262. package/lib/messages/index.d.ts.map +1 -0
  263. package/lib/messages/index.js +25 -0
  264. package/lib/messages/index.js.map +1 -0
  265. package/lib/messages/renderer.d.ts +142 -0
  266. package/lib/messages/renderer.d.ts.map +1 -0
  267. package/lib/messages/renderer.js +165 -0
  268. package/lib/messages/renderer.js.map +1 -0
  269. package/lib/node/heap-ceiling.d.ts +61 -0
  270. package/lib/node/heap-ceiling.d.ts.map +1 -0
  271. package/lib/node/heap-ceiling.js +72 -0
  272. package/lib/node/heap-ceiling.js.map +1 -0
  273. package/lib/node/index.d.ts +1 -0
  274. package/lib/node/index.d.ts.map +1 -1
  275. package/lib/node/index.js +1 -0
  276. package/lib/node/index.js.map +1 -1
  277. package/lib/node/latency-from-env.d.ts +1 -1
  278. package/lib/node/latency-from-env.js +1 -1
  279. package/lib/node/measure-memory.d.ts.map +1 -1
  280. package/lib/node/measure-memory.js +1 -0
  281. package/lib/node/measure-memory.js.map +1 -1
  282. package/lib/node/node-file-system-provider.d.ts +35 -1
  283. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  284. package/lib/node/node-file-system-provider.js +140 -45
  285. package/lib/node/node-file-system-provider.js.map +1 -1
  286. package/lib/node/profile-capture.d.ts +8 -2
  287. package/lib/node/profile-capture.d.ts.map +1 -1
  288. package/lib/node/profile-capture.js.map +1 -1
  289. package/lib/node/profiling-run.d.ts +2 -2
  290. package/lib/node/profiling-run.js +1 -1
  291. package/lib/node/profiling-run.js.map +1 -1
  292. package/lib/node/rename-over-open-readers.d.ts +52 -0
  293. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  294. package/lib/node/rename-over-open-readers.js +66 -0
  295. package/lib/node/rename-over-open-readers.js.map +1 -0
  296. package/lib/node/socket-launcher.d.ts +16 -1
  297. package/lib/node/socket-launcher.d.ts.map +1 -1
  298. package/lib/node/socket-launcher.js +4 -3
  299. package/lib/node/socket-launcher.js.map +1 -1
  300. package/lib/node/stdio-launcher.d.ts +1 -1
  301. package/lib/node/stdio-launcher.d.ts.map +1 -1
  302. package/lib/node/stdio-launcher.js +1 -1
  303. package/lib/node/stdio-launcher.js.map +1 -1
  304. package/lib/testing/fake-document.d.ts +16 -4
  305. package/lib/testing/fake-document.d.ts.map +1 -1
  306. package/lib/testing/fake-document.js +18 -3
  307. package/lib/testing/fake-document.js.map +1 -1
  308. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  309. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  310. package/lib/testing/make-noop-shared-services.js +28 -2
  311. package/lib/testing/make-noop-shared-services.js.map +1 -1
  312. package/lib/testing/make-test-services.d.ts +61 -13
  313. package/lib/testing/make-test-services.d.ts.map +1 -1
  314. package/lib/testing/make-test-services.js +25 -5
  315. package/lib/testing/make-test-services.js.map +1 -1
  316. package/lib/testing/node/index.d.ts +1 -0
  317. package/lib/testing/node/index.d.ts.map +1 -1
  318. package/lib/testing/node/index.js +1 -0
  319. package/lib/testing/node/index.js.map +1 -1
  320. package/lib/testing/node/lsp-harness.d.ts.map +1 -1
  321. package/lib/testing/node/lsp-harness.js +2 -0
  322. package/lib/testing/node/lsp-harness.js.map +1 -1
  323. package/lib/testing/node/lsp-server-connection.d.ts +11 -3
  324. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  325. package/lib/testing/node/lsp-server-connection.js +42 -9
  326. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  327. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  328. package/lib/testing/node/scratch-workspace.js +10 -5
  329. package/lib/testing/node/scratch-workspace.js.map +1 -1
  330. package/lib/testing/node/spawned-server.d.ts +14 -0
  331. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  332. package/lib/testing/node/spawned-server.js +33 -1
  333. package/lib/testing/node/spawned-server.js.map +1 -1
  334. package/lib/testing/node/unhandled-rejections.d.ts +20 -0
  335. package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
  336. package/lib/testing/node/unhandled-rejections.js +32 -0
  337. package/lib/testing/node/unhandled-rejections.js.map +1 -0
  338. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  339. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  340. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  341. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  342. package/lib/testing/playwright/index.d.ts +1 -0
  343. package/lib/testing/playwright/index.d.ts.map +1 -1
  344. package/lib/testing/playwright/index.js +1 -0
  345. package/lib/testing/playwright/index.js.map +1 -1
  346. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  347. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  348. package/lib/testing/playwright/server-log-capture.js +21 -4
  349. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  350. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  351. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  352. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  353. package/lib/testing/run-update-pipeline.d.ts +4 -4
  354. package/lib/testing/run-update-pipeline.d.ts.map +1 -1
  355. package/lib/testing/stub-ast-document-manager.d.ts +10 -9
  356. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  357. package/lib/testing/stub-ast-document-manager.js +8 -9
  358. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  359. package/lib/testing/stub-document-builder.d.ts +8 -0
  360. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  361. package/lib/testing/stub-document-builder.js +22 -3
  362. package/lib/testing/stub-document-builder.js.map +1 -1
  363. package/lib/testing/stub-hydranium-text-documents.d.ts +22 -7
  364. package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
  365. package/lib/testing/stub-hydranium-text-documents.js +92 -15
  366. package/lib/testing/stub-hydranium-text-documents.js.map +1 -1
  367. package/lib/testing/stub-langium-documents.d.ts +3 -2
  368. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  369. package/lib/testing/stub-langium-documents.js.map +1 -1
  370. package/lib/testing/stub-model-service.d.ts +7 -6
  371. package/lib/testing/stub-model-service.d.ts.map +1 -1
  372. package/lib/testing/stub-model-service.js +4 -4
  373. package/lib/testing/stub-model-service.js.map +1 -1
  374. package/lib/util/connection-liveness.d.ts +7 -6
  375. package/lib/util/connection-liveness.d.ts.map +1 -1
  376. package/lib/util/connection-liveness.js +35 -14
  377. package/lib/util/connection-liveness.js.map +1 -1
  378. package/package.json +17 -8
  379. package/src/documents/ast-document-manager.ts +195 -45
  380. package/src/documents/client-ids.ts +5 -22
  381. package/src/documents/client-session-errors.ts +22 -0
  382. package/src/documents/client-session-registry.ts +237 -0
  383. package/src/documents/file-system-task-queue.ts +68 -0
  384. package/src/documents/hydranium-text-documents.ts +856 -142
  385. package/src/documents/index.ts +3 -0
  386. package/src/documents/language-client-text-shadow.ts +59 -11
  387. package/src/documents/self-save-registry.ts +52 -13
  388. package/src/index.ts +6 -0
  389. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  390. package/src/langium/bootstrap.ts +16 -9
  391. package/src/langium/config/configuration-provider.ts +119 -0
  392. package/src/langium/config/index.ts +1 -0
  393. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  394. package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
  395. package/src/langium/document-builder/build-session.ts +87 -0
  396. package/src/langium/document-builder/document-builder.ts +355 -13
  397. package/src/langium/document-builder/index.ts +1 -0
  398. package/src/langium/integration-services.ts +67 -6
  399. package/src/langium/integrity/integrity-rule.ts +13 -5
  400. package/src/langium/integrity/integrity-service.ts +202 -17
  401. package/src/langium/keys/containment.ts +87 -0
  402. package/src/langium/keys/index.ts +1 -0
  403. package/src/langium/keys/name-based-key-provider.ts +4 -0
  404. package/src/langium/language-module.ts +42 -4
  405. package/src/langium/model-service/client-session.ts +581 -0
  406. package/src/langium/model-service/index.ts +1 -0
  407. package/src/langium/model-service/model-service.ts +347 -298
  408. package/src/langium/module.ts +119 -22
  409. package/src/langium/naming/name-provider.ts +27 -5
  410. package/src/langium/naming/name-separator-validation.ts +34 -5
  411. package/src/langium/residency/cst-residency-service.ts +36 -14
  412. package/src/langium/scope/hydranium-scope-provider.ts +60 -22
  413. package/src/langium/serialization/abstract-serializer.ts +13 -32
  414. package/src/langium/service-registry.ts +19 -2
  415. package/src/langium/shared-services.ts +19 -2
  416. package/src/langium/transfer/transfer-encoder.ts +85 -24
  417. package/src/langium/trivia/comment-preserver.ts +1100 -0
  418. package/src/langium/trivia/document-ending-preserver.ts +55 -0
  419. package/src/langium/trivia/index.ts +14 -0
  420. package/src/langium/trivia/trivia-contribution.ts +39 -0
  421. package/src/langium/trivia/trivia-preserver.ts +54 -0
  422. package/src/langium/trivia/trivia-service.ts +99 -0
  423. package/src/langium/update-rewrite/update-rewrite.ts +1 -1
  424. package/src/langium/validation/document-validator.ts +537 -11
  425. package/src/langium/workspace/document-uri-policy.ts +3 -4
  426. package/src/langium/workspace/file-system-provider.ts +48 -8
  427. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  428. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  429. package/src/langium/workspace/in-memory-file-system-provider.ts +62 -10
  430. package/src/langium/workspace/initialize-workspace.ts +43 -5
  431. package/src/langium/workspace/langium-documents.ts +106 -33
  432. package/src/langium/workspace/virtual-document.ts +21 -3
  433. package/src/langium/workspace/write-lock-scope.ts +1 -1
  434. package/src/locale/index.ts +10 -0
  435. package/src/locale/server-locale.ts +86 -0
  436. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  437. package/src/lsp/connection-features.ts +67 -0
  438. package/src/lsp/diagnostics-connection.ts +56 -0
  439. package/src/lsp/hydranium-document-update-handler.ts +159 -79
  440. package/src/lsp/index.ts +3 -1
  441. package/src/lsp/lsp-latency.ts +62 -0
  442. package/src/lsp/semantic-token-provider.ts +74 -7
  443. package/src/lsp/shared-module.ts +4 -8
  444. package/src/lsp/start-language-server.ts +13 -7
  445. package/src/messages/carriers.ts +78 -0
  446. package/src/messages/index.ts +36 -0
  447. package/src/messages/renderer.ts +212 -0
  448. package/src/node/heap-ceiling.ts +108 -0
  449. package/src/node/index.ts +1 -0
  450. package/src/node/latency-from-env.ts +1 -1
  451. package/src/node/measure-memory.ts +1 -0
  452. package/src/node/node-file-system-provider.ts +154 -47
  453. package/src/node/profile-capture.ts +8 -2
  454. package/src/node/profiling-run.ts +3 -3
  455. package/src/node/rename-over-open-readers.ts +78 -0
  456. package/src/node/socket-launcher.ts +19 -4
  457. package/src/node/stdio-launcher.ts +1 -2
  458. package/src/testing/fake-document.ts +24 -5
  459. package/src/testing/make-noop-shared-services.ts +50 -2
  460. package/src/testing/make-test-services.ts +99 -24
  461. package/src/testing/node/index.ts +1 -0
  462. package/src/testing/node/lsp-harness.ts +2 -0
  463. package/src/testing/node/lsp-server-connection.ts +61 -11
  464. package/src/testing/node/scratch-workspace.ts +10 -5
  465. package/src/testing/node/spawned-server.ts +35 -1
  466. package/src/testing/node/unhandled-rejections.ts +31 -0
  467. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  468. package/src/testing/playwright/index.ts +1 -0
  469. package/src/testing/playwright/server-log-capture.ts +24 -5
  470. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  471. package/src/testing/run-update-pipeline.ts +4 -4
  472. package/src/testing/stub-ast-document-manager.ts +18 -31
  473. package/src/testing/stub-document-builder.ts +32 -3
  474. package/src/testing/stub-hydranium-text-documents.ts +127 -22
  475. package/src/testing/stub-langium-documents.ts +3 -2
  476. package/src/testing/stub-model-service.ts +9 -8
  477. package/src/util/connection-liveness.ts +37 -14
  478. package/lib/lsp/instrument-connection.d.ts +0 -36
  479. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  480. package/lib/lsp/instrument-connection.js +0 -62
  481. package/lib/lsp/instrument-connection.js.map +0 -1
  482. 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
+ }
@@ -7,30 +7,27 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { Debouncer, type Logger, ObservableValue, type MaybeObservableValue } from '@hydranium/protocol';
10
+ import { Debouncer, Deferred, type Logger, ObservableValue, type MaybeObservableValue, TIMED_OUT } from '@hydranium/protocol';
11
11
  import { URI, UriUtils } from '@hydranium/langium';
12
12
  import { DefaultDocumentUpdateHandler } from '@hydranium/langium/lsp';
13
- import { type HydraniumDocumentBuilder } from '../langium/document-builder/document-builder.js';
13
+ import { HYDRANIUM_BUILD_REASONS, type HydraniumDocumentBuilder } from '../langium/document-builder/document-builder.js';
14
14
  import { type ServerSharedServices } from '../langium/module.js';
15
15
  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 { type DidChangeWatchedFilesParams, type FileEvent, FileChangeType, type TextDocumentChangeEvent } from 'vscode-languageserver';
19
+ import { isConnectionGoneError } from '../util/connection-liveness.js';
20
+ import {
21
+ type DidChangeWatchedFilesParams,
22
+ type FileEvent,
23
+ FileChangeType,
24
+ type TextDocumentChangeEvent,
25
+ type TextDocumentWillSaveEvent,
26
+ type TextEdit
27
+ } from 'vscode-languageserver';
20
28
  import { type TextDocument } from 'vscode-languageserver-textdocument';
21
29
 
22
- /**
23
- * Default LSP-event reasons stamped by {@link HydraniumDocumentUpdateHandler}
24
- * onto the next `documentBuilder.update` call via `markNextReason`. Exposed
25
- * so adopters subclassing the handler can layer additional reasons or rename
26
- * existing ones without losing the canonical names the framework emits.
27
- */
28
- export const HYDRANIUM_BUILD_REASONS = Object.freeze({
29
- didOpen: 'didOpen',
30
- didChangeContent: 'didChangeContent',
31
- didChangeWatchedFiles: 'didChangeWatchedFiles',
32
- didClose: 'didClose'
33
- } as const);
30
+ export { HYDRANIUM_BUILD_REASONS };
34
31
 
35
32
  /**
36
33
  * Construction options for {@link HydraniumDocumentUpdateHandler}.
@@ -56,6 +53,17 @@ export interface HydraniumDocumentUpdateHandlerOptions {
56
53
  * load tests).
57
54
  */
58
55
  readonly bypassNonLanguageClientChanges?: boolean;
56
+ /**
57
+ * Longest an editor's save waits on the server, in milliseconds, at each of
58
+ * its two steps: the `willSaveWaitUntil` answer waits for the server's disk
59
+ * writes of the document already queued, and server writes queued after the
60
+ * answer wait for the editor's `didSave`. Either wait that runs out is
61
+ * logged, and the editor's write and the server's can then land in either
62
+ * order. Default `1000`: VS Code gives up on an answer after about 1.5 s,
63
+ * and switches the listener off for the session once four answers, over
64
+ * all documents, timed out or failed.
65
+ */
66
+ readonly willSaveGateMs?: number;
59
67
  }
60
68
 
61
69
  /**
@@ -71,19 +79,25 @@ export interface HydraniumDocumentUpdateHandlerOptions {
71
79
  * changes and watched-file changes bypass the debounce (their
72
80
  * producers are already coalesced).
73
81
  * 3. **Build-reason stamping** — the LSP event overrides
74
- * (`didOpenDocument`, `didChangeContent`, `didChangeWatchedFiles`,
75
- * `didCloseDocument`) populate {@link nextReason} with a canonical
76
- * reason string. {@link fireDocumentUpdate} captures the reason;
77
- * {@link dispatch} stages it on the builder via
78
- * {@link HydraniumDocumentBuilder.markNextReason}, so a subclass that
79
- * formats build logs can tag its line with the event that caused the
80
- * build without per-adopter wiring to capture it.
81
- * 4. **Last-client-close rebuild trigger** ({@link didCloseDocument}) —
82
- * when the final client closes a document, dispatch a build so the
83
- * LangiumDocument's `textDocument` is refreshed (from disk for `file:`
84
- * URIs, or removed from the index for ephemeral URIs). Langium's
85
- * default handler does not implement `didCloseDocument`, so the
86
- * in-memory text of the last writer would otherwise persist.
82
+ * (`didOpenDocument`, `didChangeContent`, `didChangeWatchedFiles`)
83
+ * populate {@link nextReason} with a canonical reason string.
84
+ * {@link fireDocumentUpdate} captures the reason; {@link dispatch} stages
85
+ * it on the builder via {@link HydraniumDocumentBuilder.markNextReason},
86
+ * so a subclass that formats build logs can tag its line with the event
87
+ * that caused the build without per-adopter wiring to capture it.
88
+ * 4. **Editor save gate** ({@link willSaveDocumentWaitUntil}) — the editor
89
+ * writes the file itself, so the server's disk writes of that document
90
+ * are ordered around it: the editor's save waits for the ones already
91
+ * queued, and the ones queued during its save wait for its `didSave`.
92
+ * Both waits are capped by
93
+ * {@link HydraniumDocumentUpdateHandlerOptions.willSaveGateMs}.
94
+ *
95
+ * The revert to disk after a document's last close is not dispatched here:
96
+ * the text store runs it for every head, the LSP head included, so a
97
+ * `didCloseDocument` added in a subclass would build the document twice. A
98
+ * change still debounced for the document is dropped when the store releases
99
+ * it: a `file:` document's revert rebuilds it, and a document of another
100
+ * scheme, which does not revert, keeps its last build.
87
101
  *
88
102
  * Adopters with their own handler subclass should extend this class
89
103
  * (not Langium's `DefaultDocumentUpdateHandler`) so all of them are
@@ -132,7 +146,21 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
132
146
  */
133
147
  protected pendingReason?: string;
134
148
 
135
- constructor(services: ServerSharedServices, options: HydraniumDocumentUpdateHandlerOptions = {}) {
149
+ protected readonly willSaveGateMs: number;
150
+ /**
151
+ * Per document URI, the release of the hold an editor's save in progress
152
+ * keeps on the document's disk queue. A second save of the document
153
+ * releases the first: its hold would otherwise keep the second's answer
154
+ * waiting until the cap. The key is the document, not the save, so for a
155
+ * client that sends its next `willSaveWaitUntil` before the previous
156
+ * `didSave`, that late `didSave` releases the next save's hold.
157
+ */
158
+ protected readonly editorSaves = new Map<string, () => void>();
159
+
160
+ constructor(
161
+ protected readonly services: ServerSharedServices,
162
+ options: HydraniumDocumentUpdateHandlerOptions = {}
163
+ ) {
136
164
  super(services);
137
165
  this.logger = services.Logger;
138
166
  this.selfSaveRegistry = services.workspace.SelfSaveRegistry;
@@ -141,6 +169,19 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
141
169
  this.debounce = ObservableValue.from(options.debounceMs ?? 0);
142
170
  this.flushDebouncer = new Debouncer(services.Clock, () => this.flushPending(), { delayMs: this.debounce });
143
171
  this.bypassNonLanguageClientChanges = options.bypassNonLanguageClientChanges ?? true;
172
+ this.willSaveGateMs = options.willSaveGateMs ?? 1000;
173
+ this.textDocuments.onDidSaveInLanguageClient(event => this.editorSaves.get(event.uri)?.());
174
+ // A change still debounced for a document the store has released would
175
+ // build it once more after the store's own revert: from disk again, or,
176
+ // for a file that never existed, by reading a file that is not there. A
177
+ // document of another scheme does not revert, so dropping its change
178
+ // leaves it at its last build.
179
+ this.textDocuments.onDidCloseLastOpen(event => {
180
+ this.pendingChanged.delete(URI.parse(event.uri).toString());
181
+ if (this.pendingChanged.size === 0 && this.pendingDeleted.size === 0) {
182
+ this.pendingReason = undefined;
183
+ }
184
+ });
144
185
  }
145
186
 
146
187
  didOpenDocument(_change: TextDocumentChangeEvent<TextDocument>): void {
@@ -165,61 +206,21 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
165
206
  super.didChangeContent(change);
166
207
  }
167
208
 
168
- /**
169
- * Last-client close is the missing rebuild trigger in Langium's
170
- * `DefaultDocumentUpdateHandler`: `documents.onDidClose` fires on every
171
- * per-client close, but the default handler ignores the event entirely.
172
- * For multi-client servers this leaves the LangiumDocument carrying the
173
- * unsaved in-memory `textDocument` of the last writer until something
174
- * else triggers a rebuild — integrity rules walking `langiumDocuments.all`
175
- * then see stale text long after the user discarded their edits.
176
- *
177
- * We funnel the last-client transition through the same dispatch path
178
- * the other LSP events use, so the rebuild gets debouncing, workspace
179
- * locking, build-reason stamping, and `markNextReason` for free. The
180
- * scheme split governs the `DocumentBuilder.update` shape:
181
- *
182
- * - **`file:` URI** → `update([uri], [])`. Routes through
183
- * `LangiumDocuments.invalidateDocument` (doc stays in the trie) and
184
- * `LangiumDocumentFactory.update`, which consults the LSP-tracked
185
- * open-documents map first. That map is cleared LATER in the same
186
- * synchronous `notifyDidCloseTextDocument` call that fires this event,
187
- * and {@link dispatch} defers the build behind `workspaceManager.ready`
188
- * — so by the time the factory looks, the URI is gone: it falls back to
189
- * `FileSystemProvider.readFile` and the doc rebuilds from disk,
190
- * Langium's canonical close-without-save semantic. A dispatch made
191
- * synchronously here would still see the open document and re-read the
192
- * in-memory text instead.
193
- * - **non-`file:` URI** (`builtin:`, `untitled:`) → no dispatch.
194
- * Adopter-loaded schemes — documents pulled in via
195
- * `loadAdditionalDocuments` — need to stay in the workspace index;
196
- * dropping them on close would break every cross-reference that
197
- * targets them. The framework cannot enumerate adopter schemes, so the
198
- * safe default is "leave alone unless it's a file." Adopters that need
199
- * ephemeral-URI cleanup handle it explicitly.
200
- *
201
- * Per-client closes (where other clients still hold the URI) are
202
- * suppressed via {@link HydraniumTextDocuments.isOpenInAnyClient}.
203
- */
204
- didCloseDocument(change: TextDocumentChangeEvent<TextDocument>): void {
205
- if (this.textDocuments.isOpenInAnyClient(change.document.uri)) {
206
- return;
207
- }
208
- const uri = URI.parse(change.document.uri);
209
- if (uri.scheme !== 'file') {
210
- return;
211
- }
212
- this.nextReason = HYDRANIUM_BUILD_REASONS.didClose;
213
- this.immediateFlush = true;
214
- this.fireDocumentUpdate([uri], []);
215
- }
216
-
217
209
  override didChangeWatchedFiles(params: DidChangeWatchedFilesParams): void {
218
210
  void this.filterSelfSaves(params)
219
211
  .then(filtered => {
220
212
  if (filtered.changes.length === 0) {
221
213
  return;
222
214
  }
215
+ // Only an open document keeps the file's text as its disk
216
+ // baseline; the store ignores the rest. The self-save filter has
217
+ // dropped the server's own writes, which set the baseline when
218
+ // they wrote.
219
+ for (const change of filtered.changes) {
220
+ this.textDocuments
221
+ .reloadDiskBaseline(change.uri)
222
+ .catch((err: unknown) => this.logger.error(`Disk baseline not reloaded for ${change.uri}. ${String(err)}`));
223
+ }
223
224
  // External file-system changes are authoritative — overwrite any
224
225
  // pending reason (typically empty here; could be `didChangeContent`
225
226
  // if a watcher event raced an in-flight Monaco edit, in which case
@@ -241,6 +242,77 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
241
242
  });
242
243
  }
243
244
 
245
+ /**
246
+ * Answer an editor's `willSaveWaitUntil` with no edits once the server's
247
+ * disk writes of the document already queued have landed, or after
248
+ * {@link willSaveGateMs}, whichever is first. From the moment the queue
249
+ * reaches this save, disk tasks of the document queued behind it wait for
250
+ * the editor's `didSave`, reported through
251
+ * `HydraniumTextDocuments.onDidSaveInLanguageClient`, or for
252
+ * {@link willSaveGateMs} more.
253
+ *
254
+ * The hold is a disk task that waits only for that signal or its timer:
255
+ * waiting on a build, a save or anything else queued for the document would
256
+ * wedge its queue. Resolves in every case, since a failed request counts
257
+ * against the listener in VS Code; Langium drops the request's cancellation,
258
+ * so an abandoned request still runs to the cap. A hold that runs out is
259
+ * logged at debug only: an editor skips `didSave` for a save it cancels or
260
+ * that changed nothing.
261
+ */
262
+ willSaveDocumentWaitUntil(event: TextDocumentWillSaveEvent<TextDocument>): Promise<TextEdit[]> {
263
+ const uri = event.document.uri;
264
+ this.editorSaves.get(uri)?.();
265
+ const hold = new Deferred();
266
+ const release = (): void => {
267
+ if (this.editorSaves.get(uri) === release) {
268
+ this.editorSaves.delete(uri);
269
+ }
270
+ hold.resolve();
271
+ };
272
+ this.editorSaves.set(uri, release);
273
+ return new Promise<TextEdit[]>(resolve => {
274
+ const clock = this.services.Clock;
275
+ const cap = clock.setTimer(() => {
276
+ this.logger.warn(
277
+ `willSaveWaitUntil for ${uri} answered after ${this.willSaveGateMs} ms with server writes of it still queued; the editor's save may race them.`
278
+ );
279
+ resolve([]);
280
+ }, this.willSaveGateMs);
281
+ try {
282
+ void this.services.workspace.FileSystemTaskQueue.enqueue(uri, async () => {
283
+ cap.dispose();
284
+ resolve([]);
285
+ if ((await clock.raceTimer(hold.promise, this.willSaveGateMs)) === TIMED_OUT) {
286
+ this.logger.debug(
287
+ `No didSave for ${uri} within ${this.willSaveGateMs} ms; server writes of it no longer wait for the editor's save.`
288
+ );
289
+ release();
290
+ }
291
+ });
292
+ } catch (err) {
293
+ cap.dispose();
294
+ this.logger.error(
295
+ `willSaveWaitUntil for ${uri} answered without waiting. ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`
296
+ );
297
+ release();
298
+ resolve([]);
299
+ }
300
+ });
301
+ }
302
+
303
+ /**
304
+ * Present so that Langium advertises `save` and editors send `didSave`;
305
+ * without it every hold of {@link willSaveDocumentWaitUntil} waits out its
306
+ * cap. The hold is released on
307
+ * `HydraniumTextDocuments.onDidSaveInLanguageClient` instead: it fires as
308
+ * soon as the editor reports the save, while `onDidSave`, and so this
309
+ * method, follows only once the file has been read back, and not at all
310
+ * when the file does not hold the store's text.
311
+ */
312
+ didSaveDocument(_event: TextDocumentChangeEvent<TextDocument>): void {
313
+ // Nothing to do; see the doc comment.
314
+ }
315
+
244
316
  /**
245
317
  * Drop watched-file changes whose path + mtime match a recent self-save
246
318
  * record. Deletions always pass (no mtime); stat failures pass (the
@@ -257,7 +329,7 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
257
329
  }
258
330
  const uri = UriUtils.toUri(change.uri);
259
331
  const mtimeMs = await this.fileSystemProvider.mtimeMs?.(uri);
260
- if (mtimeMs !== undefined && this.selfSaveRegistry.matches(uri.fsPath, mtimeMs)) {
332
+ if (mtimeMs !== undefined && this.selfSaveRegistry.isRegistered(uri.fsPath, mtimeMs)) {
261
333
  return undefined;
262
334
  }
263
335
  return change;
@@ -338,6 +410,14 @@ export class HydraniumDocumentUpdateHandler extends DefaultDocumentUpdateHandler
338
410
  })
339
411
  )
340
412
  .catch(err => {
413
+ // A deferred rebuild can finish after the LSP peer has gone away.
414
+ // Unless the head was started through `startLanguageServer`, whose
415
+ // guard absorbs it, the diagnostics publish then fails this build:
416
+ // a routine teardown race, not a failed workspace update. Keep
417
+ // genuine update failures visible.
418
+ if (isConnectionGoneError(err)) {
419
+ return;
420
+ }
341
421
  const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
342
422
  this.logger.error(`Workspace initialization failed. Could not perform document update. ${detail}`);
343
423
  });
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';