@hydranium/core 1.0.0-next.16 → 1.0.0-next.168

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 (505) hide show
  1. package/README.md +5 -1
  2. package/lib/documents/ast-document-manager.d.ts +190 -29
  3. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  4. package/lib/documents/ast-document-manager.js +235 -38
  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 +187 -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 +439 -82
  23. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  24. package/lib/documents/hydranium-text-documents.js +769 -159
  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 +365 -19
  70. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  71. package/lib/langium/document-builder/document-builder.js +637 -44
  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 +307 -0
  105. package/lib/langium/model-service/client-session.d.ts.map +1 -0
  106. package/lib/langium/model-service/client-session.js +395 -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 +261 -198
  113. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  114. package/lib/langium/model-service/model-service.js +234 -346
  115. package/lib/langium/model-service/model-service.js.map +1 -1
  116. package/lib/langium/module.d.ts +144 -35
  117. package/lib/langium/module.d.ts.map +1 -1
  118. package/lib/langium/module.js +45 -34
  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/project/abstract-project-manager.d.ts.map +1 -1
  129. package/lib/langium/project/abstract-project-manager.js +9 -2
  130. package/lib/langium/project/abstract-project-manager.js.map +1 -1
  131. package/lib/langium/project/project-change-event.d.ts +2 -2
  132. package/lib/langium/residency/cst-residency-service.d.ts +31 -11
  133. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  134. package/lib/langium/residency/cst-residency-service.js +13 -56
  135. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  136. package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
  137. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  138. package/lib/langium/scope/hydranium-scope-provider.js +56 -20
  139. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  140. package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
  141. package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
  142. package/lib/langium/serialization/abstract-serializer.js +12 -21
  143. package/lib/langium/serialization/abstract-serializer.js.map +1 -1
  144. package/lib/langium/service-registry.d.ts +15 -1
  145. package/lib/langium/service-registry.d.ts.map +1 -1
  146. package/lib/langium/service-registry.js +16 -0
  147. package/lib/langium/service-registry.js.map +1 -1
  148. package/lib/langium/shared-services.d.ts +15 -2
  149. package/lib/langium/shared-services.d.ts.map +1 -1
  150. package/lib/langium/shared-services.js.map +1 -1
  151. package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
  152. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  153. package/lib/langium/transfer/transfer-encoder.js +38 -16
  154. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  155. package/lib/langium/trivia/comment-preserver.d.ts +423 -0
  156. package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
  157. package/lib/langium/trivia/comment-preserver.js +906 -0
  158. package/lib/langium/trivia/comment-preserver.js.map +1 -0
  159. package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
  160. package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
  161. package/lib/langium/trivia/document-ending-preserver.js +48 -0
  162. package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
  163. package/lib/langium/trivia/index.d.ts +14 -0
  164. package/lib/langium/trivia/index.d.ts.map +1 -0
  165. package/lib/langium/trivia/index.js +14 -0
  166. package/lib/langium/trivia/index.js.map +1 -0
  167. package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
  168. package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
  169. package/lib/langium/trivia/trivia-contribution.js +10 -0
  170. package/lib/langium/trivia/trivia-contribution.js.map +1 -0
  171. package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
  172. package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
  173. package/lib/langium/trivia/trivia-preserver.js +10 -0
  174. package/lib/langium/trivia/trivia-preserver.js.map +1 -0
  175. package/lib/langium/trivia/trivia-service.d.ts +70 -0
  176. package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
  177. package/lib/langium/trivia/trivia-service.js +56 -0
  178. package/lib/langium/trivia/trivia-service.js.map +1 -0
  179. package/lib/langium/update-rewrite/update-rewrite.d.ts +1 -1
  180. package/lib/langium/validation/document-validator.d.ts +294 -10
  181. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  182. package/lib/langium/validation/document-validator.js +448 -5
  183. package/lib/langium/validation/document-validator.js.map +1 -1
  184. package/lib/langium/workspace/document-uri-policy.d.ts +17 -12
  185. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  186. package/lib/langium/workspace/document-uri-policy.js +10 -9
  187. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  188. package/lib/langium/workspace/file-not-found.d.ts +17 -0
  189. package/lib/langium/workspace/file-not-found.d.ts.map +1 -0
  190. package/lib/langium/workspace/file-not-found.js +20 -0
  191. package/lib/langium/workspace/file-not-found.js.map +1 -0
  192. package/lib/langium/workspace/file-system-provider.d.ts +84 -8
  193. package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
  194. package/lib/langium/workspace/file-system-provider.js +93 -14
  195. package/lib/langium/workspace/file-system-provider.js.map +1 -1
  196. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  197. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  198. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  199. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  200. package/lib/langium/workspace/hydranium-workspace-lock.d.ts +13 -0
  201. package/lib/langium/workspace/hydranium-workspace-lock.d.ts.map +1 -1
  202. package/lib/langium/workspace/hydranium-workspace-lock.js +18 -0
  203. package/lib/langium/workspace/hydranium-workspace-lock.js.map +1 -1
  204. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +77 -8
  205. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  206. package/lib/langium/workspace/hydranium-workspace-manager.js +115 -9
  207. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  208. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +25 -2
  209. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  210. package/lib/langium/workspace/in-memory-file-system-provider.js +41 -15
  211. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  212. package/lib/langium/workspace/initialize-workspace.d.ts +31 -2
  213. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  214. package/lib/langium/workspace/initialize-workspace.js +13 -5
  215. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  216. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  217. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  218. package/lib/langium/workspace/langium-documents.js +91 -29
  219. package/lib/langium/workspace/langium-documents.js.map +1 -1
  220. package/lib/langium/workspace/persistent-file-system-provider.d.ts +0 -7
  221. package/lib/langium/workspace/persistent-file-system-provider.d.ts.map +1 -1
  222. package/lib/langium/workspace/persistent-file-system-provider.js +0 -7
  223. package/lib/langium/workspace/persistent-file-system-provider.js.map +1 -1
  224. package/lib/langium/workspace/virtual-document.d.ts +48 -15
  225. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  226. package/lib/langium/workspace/virtual-document.js +78 -18
  227. package/lib/langium/workspace/virtual-document.js.map +1 -1
  228. package/lib/langium/workspace/write-lock-scope.d.ts +4 -3
  229. package/lib/langium/workspace/write-lock-scope.d.ts.map +1 -1
  230. package/lib/langium/workspace/write-lock-scope.js +4 -3
  231. package/lib/langium/workspace/write-lock-scope.js.map +1 -1
  232. package/lib/locale/index.d.ts +10 -0
  233. package/lib/locale/index.d.ts.map +1 -0
  234. package/lib/locale/index.js +10 -0
  235. package/lib/locale/index.js.map +1 -0
  236. package/lib/locale/server-locale.d.ts +73 -0
  237. package/lib/locale/server-locale.d.ts.map +1 -0
  238. package/lib/locale/server-locale.js +61 -0
  239. package/lib/locale/server-locale.js.map +1 -0
  240. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  241. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  242. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  243. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  244. package/lib/lsp/connection-features.d.ts +23 -0
  245. package/lib/lsp/connection-features.d.ts.map +1 -0
  246. package/lib/lsp/connection-features.js +60 -0
  247. package/lib/lsp/connection-features.js.map +1 -0
  248. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  249. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  250. package/lib/lsp/diagnostics-connection.js +53 -0
  251. package/lib/lsp/diagnostics-connection.js.map +1 -0
  252. package/lib/lsp/hydranium-document-update-handler.d.ts +90 -79
  253. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  254. package/lib/lsp/hydranium-document-update-handler.js +152 -99
  255. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  256. package/lib/lsp/index.d.ts +3 -1
  257. package/lib/lsp/index.d.ts.map +1 -1
  258. package/lib/lsp/index.js +3 -1
  259. package/lib/lsp/index.js.map +1 -1
  260. package/lib/lsp/lsp-latency.d.ts +39 -0
  261. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  262. package/lib/lsp/lsp-latency.js +58 -0
  263. package/lib/lsp/lsp-latency.js.map +1 -0
  264. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  265. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  266. package/lib/lsp/semantic-token-provider.js +55 -8
  267. package/lib/lsp/semantic-token-provider.js.map +1 -1
  268. package/lib/lsp/shared-module.d.ts +4 -8
  269. package/lib/lsp/shared-module.d.ts.map +1 -1
  270. package/lib/lsp/shared-module.js.map +1 -1
  271. package/lib/lsp/start-language-server.d.ts +8 -7
  272. package/lib/lsp/start-language-server.d.ts.map +1 -1
  273. package/lib/lsp/start-language-server.js +13 -7
  274. package/lib/lsp/start-language-server.js.map +1 -1
  275. package/lib/messages/carriers.d.ts +54 -0
  276. package/lib/messages/carriers.d.ts.map +1 -0
  277. package/lib/messages/carriers.js +62 -0
  278. package/lib/messages/carriers.js.map +1 -0
  279. package/lib/messages/index.d.ts +25 -0
  280. package/lib/messages/index.d.ts.map +1 -0
  281. package/lib/messages/index.js +25 -0
  282. package/lib/messages/index.js.map +1 -0
  283. package/lib/messages/renderer.d.ts +142 -0
  284. package/lib/messages/renderer.d.ts.map +1 -0
  285. package/lib/messages/renderer.js +165 -0
  286. package/lib/messages/renderer.js.map +1 -0
  287. package/lib/node/heap-ceiling.d.ts +61 -0
  288. package/lib/node/heap-ceiling.d.ts.map +1 -0
  289. package/lib/node/heap-ceiling.js +72 -0
  290. package/lib/node/heap-ceiling.js.map +1 -0
  291. package/lib/node/index.d.ts +1 -0
  292. package/lib/node/index.d.ts.map +1 -1
  293. package/lib/node/index.js +1 -0
  294. package/lib/node/index.js.map +1 -1
  295. package/lib/node/latency-from-env.d.ts +1 -1
  296. package/lib/node/latency-from-env.js +1 -1
  297. package/lib/node/measure-memory.d.ts.map +1 -1
  298. package/lib/node/measure-memory.js +1 -0
  299. package/lib/node/measure-memory.js.map +1 -1
  300. package/lib/node/node-file-system-provider.d.ts +39 -4
  301. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  302. package/lib/node/node-file-system-provider.js +109 -49
  303. package/lib/node/node-file-system-provider.js.map +1 -1
  304. package/lib/node/profile-capture.d.ts +8 -2
  305. package/lib/node/profile-capture.d.ts.map +1 -1
  306. package/lib/node/profile-capture.js.map +1 -1
  307. package/lib/node/profiling-run.d.ts +2 -2
  308. package/lib/node/profiling-run.js +1 -1
  309. package/lib/node/profiling-run.js.map +1 -1
  310. package/lib/node/rename-over-open-readers.d.ts +52 -0
  311. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  312. package/lib/node/rename-over-open-readers.js +66 -0
  313. package/lib/node/rename-over-open-readers.js.map +1 -0
  314. package/lib/node/socket-launcher.d.ts +16 -1
  315. package/lib/node/socket-launcher.d.ts.map +1 -1
  316. package/lib/node/socket-launcher.js +4 -3
  317. package/lib/node/socket-launcher.js.map +1 -1
  318. package/lib/node/stdio-launcher.d.ts +1 -1
  319. package/lib/node/stdio-launcher.d.ts.map +1 -1
  320. package/lib/node/stdio-launcher.js +1 -1
  321. package/lib/node/stdio-launcher.js.map +1 -1
  322. package/lib/testing/fake-document.d.ts +16 -4
  323. package/lib/testing/fake-document.d.ts.map +1 -1
  324. package/lib/testing/fake-document.js +18 -3
  325. package/lib/testing/fake-document.js.map +1 -1
  326. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  327. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  328. package/lib/testing/make-noop-shared-services.js +28 -2
  329. package/lib/testing/make-noop-shared-services.js.map +1 -1
  330. package/lib/testing/make-test-services.d.ts +62 -14
  331. package/lib/testing/make-test-services.d.ts.map +1 -1
  332. package/lib/testing/make-test-services.js +27 -6
  333. package/lib/testing/make-test-services.js.map +1 -1
  334. package/lib/testing/node/index.d.ts +1 -0
  335. package/lib/testing/node/index.d.ts.map +1 -1
  336. package/lib/testing/node/index.js +1 -0
  337. package/lib/testing/node/index.js.map +1 -1
  338. package/lib/testing/node/lsp-harness.d.ts.map +1 -1
  339. package/lib/testing/node/lsp-harness.js +2 -0
  340. package/lib/testing/node/lsp-harness.js.map +1 -1
  341. package/lib/testing/node/lsp-server-connection.d.ts +11 -3
  342. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  343. package/lib/testing/node/lsp-server-connection.js +42 -9
  344. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  345. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  346. package/lib/testing/node/scratch-workspace.js +10 -5
  347. package/lib/testing/node/scratch-workspace.js.map +1 -1
  348. package/lib/testing/node/spawned-server.d.ts +14 -0
  349. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  350. package/lib/testing/node/spawned-server.js +33 -1
  351. package/lib/testing/node/spawned-server.js.map +1 -1
  352. package/lib/testing/node/unhandled-rejections.d.ts +20 -0
  353. package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
  354. package/lib/testing/node/unhandled-rejections.js +32 -0
  355. package/lib/testing/node/unhandled-rejections.js.map +1 -0
  356. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  357. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  358. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  359. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  360. package/lib/testing/playwright/index.d.ts +1 -0
  361. package/lib/testing/playwright/index.d.ts.map +1 -1
  362. package/lib/testing/playwright/index.js +1 -0
  363. package/lib/testing/playwright/index.js.map +1 -1
  364. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  365. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  366. package/lib/testing/playwright/server-log-capture.js +21 -4
  367. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  368. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  369. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  370. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  371. package/lib/testing/run-update-pipeline.d.ts +4 -4
  372. package/lib/testing/run-update-pipeline.d.ts.map +1 -1
  373. package/lib/testing/stub-ast-document-manager.d.ts +10 -9
  374. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  375. package/lib/testing/stub-ast-document-manager.js +17 -9
  376. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  377. package/lib/testing/stub-document-builder.d.ts +26 -11
  378. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  379. package/lib/testing/stub-document-builder.js +57 -4
  380. package/lib/testing/stub-document-builder.js.map +1 -1
  381. package/lib/testing/stub-hydranium-text-documents.d.ts +22 -7
  382. package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
  383. package/lib/testing/stub-hydranium-text-documents.js +92 -15
  384. package/lib/testing/stub-hydranium-text-documents.js.map +1 -1
  385. package/lib/testing/stub-langium-documents.d.ts +3 -2
  386. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  387. package/lib/testing/stub-langium-documents.js.map +1 -1
  388. package/lib/testing/stub-model-service.d.ts +7 -6
  389. package/lib/testing/stub-model-service.d.ts.map +1 -1
  390. package/lib/testing/stub-model-service.js +4 -4
  391. package/lib/testing/stub-model-service.js.map +1 -1
  392. package/lib/util/connection-liveness.d.ts +7 -6
  393. package/lib/util/connection-liveness.d.ts.map +1 -1
  394. package/lib/util/connection-liveness.js +35 -14
  395. package/lib/util/connection-liveness.js.map +1 -1
  396. package/package.json +17 -8
  397. package/src/documents/ast-document-manager.ts +348 -64
  398. package/src/documents/client-ids.ts +5 -22
  399. package/src/documents/client-session-errors.ts +24 -0
  400. package/src/documents/client-session-registry.ts +240 -0
  401. package/src/documents/file-system-task-queue.ts +68 -0
  402. package/src/documents/hydranium-text-documents.ts +959 -201
  403. package/src/documents/index.ts +3 -0
  404. package/src/documents/language-client-text-shadow.ts +59 -11
  405. package/src/documents/self-save-registry.ts +52 -13
  406. package/src/index.ts +6 -0
  407. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  408. package/src/langium/bootstrap.ts +16 -9
  409. package/src/langium/config/configuration-provider.ts +119 -0
  410. package/src/langium/config/index.ts +1 -0
  411. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  412. package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
  413. package/src/langium/document-builder/build-session.ts +87 -0
  414. package/src/langium/document-builder/document-builder.ts +720 -43
  415. package/src/langium/document-builder/index.ts +1 -0
  416. package/src/langium/integration-services.ts +67 -6
  417. package/src/langium/integrity/integrity-rule.ts +13 -5
  418. package/src/langium/integrity/integrity-service.ts +202 -17
  419. package/src/langium/keys/containment.ts +87 -0
  420. package/src/langium/keys/index.ts +1 -0
  421. package/src/langium/keys/name-based-key-provider.ts +4 -0
  422. package/src/langium/language-module.ts +42 -4
  423. package/src/langium/model-service/client-session.ts +606 -0
  424. package/src/langium/model-service/index.ts +1 -0
  425. package/src/langium/model-service/model-service.ts +395 -351
  426. package/src/langium/module.ts +192 -63
  427. package/src/langium/naming/name-provider.ts +27 -5
  428. package/src/langium/naming/name-separator-validation.ts +34 -5
  429. package/src/langium/project/abstract-project-manager.ts +9 -1
  430. package/src/langium/project/project-change-event.ts +2 -2
  431. package/src/langium/residency/cst-residency-service.ts +36 -14
  432. package/src/langium/scope/hydranium-scope-provider.ts +60 -22
  433. package/src/langium/serialization/abstract-serializer.ts +13 -32
  434. package/src/langium/service-registry.ts +19 -2
  435. package/src/langium/shared-services.ts +19 -2
  436. package/src/langium/transfer/transfer-encoder.ts +85 -24
  437. package/src/langium/trivia/comment-preserver.ts +1100 -0
  438. package/src/langium/trivia/document-ending-preserver.ts +55 -0
  439. package/src/langium/trivia/index.ts +14 -0
  440. package/src/langium/trivia/trivia-contribution.ts +39 -0
  441. package/src/langium/trivia/trivia-preserver.ts +54 -0
  442. package/src/langium/trivia/trivia-service.ts +99 -0
  443. package/src/langium/update-rewrite/update-rewrite.ts +1 -1
  444. package/src/langium/validation/document-validator.ts +537 -11
  445. package/src/langium/workspace/document-uri-policy.ts +17 -12
  446. package/src/langium/workspace/file-not-found.ts +23 -0
  447. package/src/langium/workspace/file-system-provider.ts +153 -16
  448. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  449. package/src/langium/workspace/hydranium-workspace-lock.ts +21 -0
  450. package/src/langium/workspace/hydranium-workspace-manager.ts +133 -12
  451. package/src/langium/workspace/in-memory-file-system-provider.ts +47 -16
  452. package/src/langium/workspace/initialize-workspace.ts +43 -5
  453. package/src/langium/workspace/langium-documents.ts +113 -33
  454. package/src/langium/workspace/persistent-file-system-provider.ts +0 -7
  455. package/src/langium/workspace/virtual-document.ts +96 -19
  456. package/src/langium/workspace/write-lock-scope.ts +4 -3
  457. package/src/locale/index.ts +10 -0
  458. package/src/locale/server-locale.ts +86 -0
  459. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  460. package/src/lsp/connection-features.ts +67 -0
  461. package/src/lsp/diagnostics-connection.ts +56 -0
  462. package/src/lsp/hydranium-document-update-handler.ts +182 -106
  463. package/src/lsp/index.ts +3 -1
  464. package/src/lsp/lsp-latency.ts +62 -0
  465. package/src/lsp/semantic-token-provider.ts +74 -7
  466. package/src/lsp/shared-module.ts +4 -8
  467. package/src/lsp/start-language-server.ts +13 -7
  468. package/src/messages/carriers.ts +78 -0
  469. package/src/messages/index.ts +36 -0
  470. package/src/messages/renderer.ts +212 -0
  471. package/src/node/heap-ceiling.ts +108 -0
  472. package/src/node/index.ts +1 -0
  473. package/src/node/latency-from-env.ts +1 -1
  474. package/src/node/measure-memory.ts +1 -0
  475. package/src/node/node-file-system-provider.ts +122 -51
  476. package/src/node/profile-capture.ts +8 -2
  477. package/src/node/profiling-run.ts +3 -3
  478. package/src/node/rename-over-open-readers.ts +78 -0
  479. package/src/node/socket-launcher.ts +19 -4
  480. package/src/node/stdio-launcher.ts +1 -2
  481. package/src/testing/fake-document.ts +24 -5
  482. package/src/testing/make-noop-shared-services.ts +50 -2
  483. package/src/testing/make-test-services.ts +103 -27
  484. package/src/testing/node/index.ts +1 -0
  485. package/src/testing/node/lsp-harness.ts +2 -0
  486. package/src/testing/node/lsp-server-connection.ts +61 -11
  487. package/src/testing/node/scratch-workspace.ts +10 -5
  488. package/src/testing/node/spawned-server.ts +35 -1
  489. package/src/testing/node/unhandled-rejections.ts +31 -0
  490. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  491. package/src/testing/playwright/index.ts +1 -0
  492. package/src/testing/playwright/server-log-capture.ts +24 -5
  493. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  494. package/src/testing/run-update-pipeline.ts +4 -4
  495. package/src/testing/stub-ast-document-manager.ts +28 -21
  496. package/src/testing/stub-document-builder.ts +83 -17
  497. package/src/testing/stub-hydranium-text-documents.ts +127 -22
  498. package/src/testing/stub-langium-documents.ts +3 -2
  499. package/src/testing/stub-model-service.ts +9 -8
  500. package/src/util/connection-liveness.ts +37 -14
  501. package/lib/lsp/instrument-connection.d.ts +0 -36
  502. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  503. package/lib/lsp/instrument-connection.js +0 -62
  504. package/lib/lsp/instrument-connection.js.map +0 -1
  505. package/src/lsp/instrument-connection.ts +0 -66
@@ -8,5 +8,6 @@
8
8
  ********************************************************************************/
9
9
 
10
10
  export * from './build-pipeline-integration.js';
11
+ export * from './build-session.js';
11
12
  export * from './document-builder.js';
12
13
  export * from './labeled-phase-listener.js';
@@ -7,7 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { inject, type Module } from '@hydranium/langium';
10
+ import { type DeepPartial, inject, type Module } from '@hydranium/langium';
11
11
  import {
12
12
  createDefaultModule,
13
13
  createDefaultSharedModule,
@@ -98,6 +98,31 @@ export interface IntegrationServicesOptions<
98
98
  * it is silently inert.
99
99
  */
100
100
  extra?: ReadonlyArray<Module<TShared, PartialLangiumSharedServices>>;
101
+ /**
102
+ * Shared modules composed LAST — after `adopter` — and partial over
103
+ * `TShared` rather than over Langium's shared services.
104
+ *
105
+ * Both differences are the point, and neither is served by
106
+ * {@link extra}. Most framework shared services are constructed with
107
+ * no options (`ModelService: services => new DefaultModelService(services)`),
108
+ * so rebinding the slot is the only way to boot one configured
109
+ * differently; a caller that needs that is usually a test, and the slot
110
+ * it needs is as often a framework ADDITION (`Clock`, `Tracer`,
111
+ * `ProjectManager`) as a Langium one. `extra` can express neither: it
112
+ * is typed `DeepPartial<LangiumSharedServices>`, and it loses to
113
+ * `adopter` on every slot the adopter binds.
114
+ *
115
+ * Kept separate rather than widening `extra` because `extra` is the
116
+ * protocol-head tier, and a head must NOT outrank the adopter. Widening
117
+ * it also does not typecheck — `Module<I, T>` puts `T` in factory
118
+ * return position, so a head module declaring Langium slot types stops
119
+ * being assignable once `T` becomes `DeepPartial<TShared>`.
120
+ *
121
+ * Composing last means an override wins over everything, including the
122
+ * adopter. That is what makes it usable for the slots an adopter binds
123
+ * itself, and why production code should not reach for it.
124
+ */
125
+ overrides?: ReadonlyArray<Module<TShared, DeepPartial<TShared>>>;
101
126
  };
102
127
 
103
128
  /**
@@ -134,6 +159,23 @@ export interface IntegrationServicesOptions<
134
159
  * still win over heads.
135
160
  */
136
161
  extra?: ReadonlyArray<Module<TLanguage, PartialLangiumServices>>;
162
+ /**
163
+ * Modules composed LAST, after `adopter`, and partial over `TLanguage`
164
+ * rather than over Langium's per-language services. The language-tier
165
+ * twin of {@link IntegrationServicesOptions.sharedModules}'s
166
+ * `overrides`, and both differences carry the same weight they do
167
+ * there.
168
+ *
169
+ * A per-language framework service is constructed by the framework's
170
+ * own module with no options
171
+ * (`IntegrityService: services => new DefaultIntegrityService(services)`),
172
+ * so booting one configured differently means rebinding the slot.
173
+ * {@link extra} serves neither half of that: it composes BEFORE
174
+ * `adopter` and so loses every slot the adopter binds, and it is typed
175
+ * `PartialLangiumServices`, which cannot express a framework ADDITION
176
+ * like `IntegrityService` or `AstExtensionService` at all.
177
+ */
178
+ overrides?: ReadonlyArray<Module<TLanguage, DeepPartial<TLanguage>>>;
137
179
  };
138
180
 
139
181
  /**
@@ -197,6 +239,8 @@ export interface AdditionalLanguageModules<
197
239
  adopter?: (shared: TShared) => Module<TLanguage, PartialLangiumServices>;
198
240
  /** Protocol-head modules for this language. Defaults to the primary language's. */
199
241
  extra?: ReadonlyArray<Module<TLanguage, PartialLangiumServices>>;
242
+ /** Last-composed overrides for this language. Defaults to the primary language's. */
243
+ overrides?: ReadonlyArray<Module<TLanguage, DeepPartial<TLanguage>>>;
200
244
  }
201
245
 
202
246
  /**
@@ -247,7 +291,9 @@ export function createIntegrationServices<
247
291
  sharedModules.generated,
248
292
  sharedModule,
249
293
  ...(sharedModules.extra ?? []),
250
- sharedModules.adopter
294
+ sharedModules.adopter,
295
+ // Last, so an override outranks the adopter too — see `overrides`.
296
+ ...((sharedModules.overrides ?? []) as ReadonlyArray<Module<TShared, PartialLangiumSharedServices>>)
251
297
  ) as unknown as TShared;
252
298
 
253
299
  // Each language gets its OWN `createDefaultModule({ shared })` — the
@@ -256,19 +302,34 @@ export function createIntegrationServices<
256
302
  const composeLanguage = (
257
303
  generated: Module<TLanguage, PartialLangiumServices>,
258
304
  adopter: (shared: TShared) => Module<TLanguage, PartialLangiumServices>,
259
- extra: ReadonlyArray<Module<TLanguage, PartialLangiumServices>>
305
+ extra: ReadonlyArray<Module<TLanguage, PartialLangiumServices>>,
306
+ overrides: ReadonlyArray<Module<TLanguage, DeepPartial<TLanguage>>>
260
307
  ): TLanguage => {
261
308
  const defaultLanguageModule = createDefaultModule({ shared }) as unknown as Module<TLanguage, PartialLangiumServices>;
262
309
  const serverLanguageModule = createServerLanguageModule(context) as unknown as Module<TLanguage, PartialLangiumServices>;
263
- return inject(defaultLanguageModule, generated, serverLanguageModule, ...extra, adopter(shared)) as unknown as TLanguage;
310
+ return inject(
311
+ defaultLanguageModule,
312
+ generated,
313
+ serverLanguageModule,
314
+ ...extra,
315
+ adopter(shared),
316
+ // Last, so an override outranks the adopter too — see `overrides`.
317
+ ...(overrides as ReadonlyArray<Module<TLanguage, PartialLangiumServices>>)
318
+ ) as unknown as TLanguage;
264
319
  };
265
320
 
266
321
  const primaryExtra = (languageModules.extra ?? []) as ReadonlyArray<Module<TLanguage, PartialLangiumServices>>;
267
- const language = composeLanguage(languageModules.generated, languageModules.adopter, primaryExtra);
322
+ const primaryOverrides = languageModules.overrides ?? [];
323
+ const language = composeLanguage(languageModules.generated, languageModules.adopter, primaryExtra, primaryOverrides);
268
324
  const languages = [
269
325
  language,
270
326
  ...additionalLanguages.map(additional =>
271
- composeLanguage(additional.generated, additional.adopter ?? languageModules.adopter, additional.extra ?? primaryExtra)
327
+ composeLanguage(
328
+ additional.generated,
329
+ additional.adopter ?? languageModules.adopter,
330
+ additional.extra ?? primaryExtra,
331
+ additional.overrides ?? primaryOverrides
332
+ )
272
333
  )
273
334
  ];
274
335
 
@@ -49,14 +49,22 @@ export namespace IntegrityPhase {
49
49
  }
50
50
 
51
51
  /**
52
- * Controls how integrity corrections are persisted for *closed* files. The
53
- * mode does not reach open files: their corrected text is delivered by the
54
- * model service's settled-state sync, whatever the mode says.
52
+ * Controls how integrity corrections are persisted for files no editor holds.
53
+ * The mode does not reach a file open in the language client: the correction
54
+ * is delivered to the editor, which shows it as an unsaved change.
55
55
  *
56
- * - `silent`: write to disk immediately; no editor involvement.
56
+ * - `silent`: write to disk immediately; no editor involvement. For a file
57
+ * held only through the data or GLSP head, only while disk still holds the
58
+ * text the correction was computed from and the text store still holds the
59
+ * correction. Otherwise — the holder has unsaved edits, disk changed behind
60
+ * the server, or disk cannot be read — the correction waits in the text
61
+ * store for the next save.
57
62
  * - `editor`: open the file dirty via `applyEdit`. If the user closes without
58
63
  * saving, the correction is suppressed until the file changes or the
59
- * workspace reloads.
64
+ * workspace reloads. A file held only through the data or GLSP head gets
65
+ * neither: the correction stays in the text store, and since those heads
66
+ * mark only their own edits unsaved, nothing shows that store and disk
67
+ * differ until a save or the last close.
60
68
  */
61
69
  export type IntegritySyncMode = 'silent' | 'editor';
62
70
 
@@ -17,6 +17,7 @@ import { type LogNameOptions } from '../diagnostics/logger.js';
17
17
  import { type HydraniumTextDocuments } from '../../documents/hydranium-text-documents.js';
18
18
  import { Registry } from '../../util/registry.js';
19
19
  import { type ServerLanguageServices } from '../language-module.js';
20
+ import { isVirtualUri } from '../workspace/virtual-document.js';
20
21
  import { type IntegrityRuleRegistry } from './integrity-contribution.js';
21
22
  import { IntegrityPhase, type IntegrityRule, type IntegritySyncMode } from './integrity-rule.js';
22
23
 
@@ -75,6 +76,11 @@ export namespace IntegrityService {
75
76
  * at `DocumentState.Validated` and delivered separately via `publishDiagnostics`
76
77
  * or the `onModelUpdated` event.
77
78
  *
79
+ * The AST is what this guarantees, not the text. For a closed document whose
80
+ * repair was STAGED rather than written (`'editor'` sync mode),
81
+ * `textDocument` still mirrors disk — read the repair off the AST, or from
82
+ * the staged content the next open consumes.
83
+ *
78
84
  * Invariant: integrity rules only register at Parsed or Linked, so the build
79
85
  * is guaranteed post-integrity once it advances past `onBuildPhase(Linked)`
80
86
  * into `IndexedReferences`. {@link IntegrityService.register} enforces this at
@@ -310,24 +316,123 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
310
316
  return;
311
317
  }
312
318
  const root = document.parseResult.value as TRoot;
313
- const newText = await this.services.serializer.Serializer.serializeAst(root);
319
+ // Extracted while `document` still holds the text being replaced — the
320
+ // resync that follows overwrites it with `newText`. Absent on a test
321
+ // stub that binds no trivia group; `createServerLanguageModule` always
322
+ // provides it, and an empty registry is already the no-op.
323
+ const trivia = this.services.trivia?.TriviaService;
324
+ const extracted = trivia?.extract(document);
325
+ const serialized = await this.services.serializer.Serializer.serializeAst(root);
326
+ const newText =
327
+ extracted === undefined ? serialized : trivia!.apply(serialized, extracted, UriUtils.toUri(document.textDocument.uri));
314
328
 
315
329
  const oldText = document.textDocument.getText();
316
330
  if (oldText === newText) {
317
331
  return;
318
332
  }
319
333
 
320
- // Keep the same version so HydraniumTextDocuments doesn't reject subsequent client edits.
321
- // Use manager.update so the `instanceof FullTextDocument` gate in the bare
322
- // `TextDocument.update` doesn't trip on adopter-custom text-document types.
323
- const textDocument = this.textDocuments.update(document.textDocument, [{ text: newText }], document.textDocument.version);
334
+ const version = document.textDocument.version;
335
+ // Commit into the store's own document when the URI is open there, rather
336
+ // than into whichever text-document object this build is holding. The two
337
+ // are the same on the LSP path and are NOT when the document was built for
338
+ // an already-open URI through `fromString`: repairing only the build's copy
339
+ // leaves the store — the server's authority on what an open document says —
340
+ // on the unrepaired text, and the reconciling re-parse then redefines
341
+ // `textDocument` onto the store's object and discards the repair with it.
342
+ //
343
+ // Compared against the CST's own text, not against `document.textDocument`.
344
+ // That object IS the store's on the LSP path, so asking it whether the
345
+ // store still agrees compares the store with itself and can only ever say
346
+ // yes — the guard would be vacuous exactly where an editor race can happen.
347
+ // The CST's `fullText` is the text that actually produced this AST, which
348
+ // is the only thing a repair derived from it is valid against. A document
349
+ // with no CST (built from a model rather than parsed) has no better answer
350
+ // than the text it is carrying.
351
+ const parsedFrom = document.parseResult.value.$cstNode?.root.fullText ?? oldText;
352
+ const commit = this.textDocuments.commitRepair(document.textDocument.uri, parsedFrom, newText);
353
+ if (commit.status === 'stale') {
354
+ // An edit landed after the parse this repair came from, so the repair
355
+ // describes text the store has already replaced. Abandon it rather than
356
+ // hand it to `syncCorrections`: an override that persists from there
357
+ // would write a correction over the edit that superseded it. That edit
358
+ // drives a build of its own, which re-runs these rules against what the
359
+ // document actually says.
360
+ //
361
+ // Abandoning the WRITE is only half of it. The build carries on to its
362
+ // settled phase with whatever AST is registered, and every settled
363
+ // consumer reads that AST rather than the store — so leaving the rule's
364
+ // mutation in place would answer questions about this document from a
365
+ // parse of text nobody has. Reconciling discards it by re-reading the
366
+ // store, which is what the document now has to agree with.
367
+ await this.reconcileDocument(document, cancelToken);
368
+ return;
369
+ }
370
+
371
+ // A committed repair is a new version; a document no client has open keeps
372
+ // its number here, and the re-versioning below moves it once the build
373
+ // has reconciled. Through the store's `update`, so the `instanceof
374
+ // FullTextDocument` gate in the bare `TextDocument.update` does not trip
375
+ // on adopter-custom text-document types.
376
+ const textDocument =
377
+ commit.status === 'committed' ? commit.document : this.textDocuments.update(document.textDocument, [{ text: newText }], version);
324
378
 
325
- await this.syncCorrections(textDocument);
379
+ await this.syncCorrections(textDocument, parsedFrom);
326
380
 
381
+ await this.reconcileDocument(document, cancelToken);
382
+
383
+ // Re-version against the REPAIRED text, after either branch. The repair is
384
+ // a content change the store has not seen: whatever reconciliation ran
385
+ // during the re-parse saw the text on disk, which in `'editor'` sync mode
386
+ // is the PRE-repair text. Leave the sequence describing that and the next
387
+ // open hashes the repair, finds a mismatch and steps the version again, so
388
+ // every based-on version taken from this build is stale before it is used.
389
+ // Falls back to the pre-re-parse number for a URI the store never tracked,
390
+ // where there is no sequence to advance; an OPEN document answers
391
+ // `undefined` and keeps the store's own version, which is not this
392
+ // method's to move.
393
+ //
394
+ // Deliberately NOT gated on cancellation, unlike the entry to this method:
395
+ // `syncCorrections` has already written or staged the repair by now, so a
396
+ // preempted build that skipped this would leave the sequence describing
397
+ // text that is no longer there.
398
+ //
399
+ // Gated on the document being closed, because the fallback restores a
400
+ // number this method captured off whatever object it started with — and
401
+ // for a separately created document that is its own seeded numbering, not
402
+ // the store's. Applying it to the store's document (which the re-parse has
403
+ // by then made `textDocument`) would roll the shared version backwards.
404
+ // The store's own answer for an open document is `undefined` either way.
405
+ if (!this.textDocuments.isOpen(document.textDocument.uri)) {
406
+ const reconciled = this.textDocuments.reconcileExternalContent(document.textDocument.uri, newText) ?? version;
407
+ if (document.textDocument.version !== reconciled) {
408
+ this.textDocuments.update(document.textDocument, [], reconciled);
409
+ }
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Bring `document` back into agreement with the text it is supposed to
415
+ * describe, after a rule has mutated its AST.
416
+ *
417
+ * Used for a correction that was applied and for one that was abandoned,
418
+ * because the document is left inconsistent either way: applied, the text
419
+ * moved under the CST; abandoned, the AST carries a mutation the text never
420
+ * had. Both are the same question — re-read and re-derive — and the phase is
421
+ * what decides how far that has to go.
422
+ */
423
+ protected async reconcileDocument(document: LangiumDocument, cancelToken?: CancellationToken): Promise<void> {
327
424
  if (document.state <= DocumentState.Parsed) {
328
425
  // Parsed-phase correction: re-parse only. The build pipeline still runs
329
426
  // IndexedContent → ComputedScopes → Linked → … on the fresh AST afterwards,
330
427
  // so the document reconciles naturally with no extra work here.
428
+ //
429
+ // Except for a closed document whose repair did not reach disk — `'editor'`
430
+ // sync mode stages it instead of writing it — where the re-parse is SKIPPED:
431
+ // Langium's factory gates the parse on the CST's `fullText`, which still
432
+ // equals the disk text the same call re-reads and redefines `textDocument`
433
+ // over. So `document.textDocument` is NOT post-integrity for such a
434
+ // document — it mirrors disk, and the AST is the only place the repair is
435
+ // legible, until an open consumes the staging.
331
436
  await this.documentBuilder.reparse(document, cancelToken);
332
437
  } else {
333
438
  // Linked-phase (or later) correction. The rule mutated the AST in place but
@@ -347,27 +452,66 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
347
452
  * already in the synced store (in-place in {@link resyncDocument}); this
348
453
  * method routes the out-of-band part by document registration:
349
454
  *
350
- * - **Open in the language client** → nothing to push here. The AST mutation
351
- * rode the current build, so `ModelService.syncToLanguageClient` (the
352
- * persistent integrity-settled listener) mirrors the corrected text to the
353
- * client by *content* (shadow diff) — no authorship rewrite, no second
354
- * rebuild.
455
+ * - **Open in the language client** → nothing to push here. The AST
456
+ * mutation rode the current build, so `ModelService.syncToLanguageClient`
457
+ * (the persistent integrity-settled listener) mirrors it to the client by
458
+ * *content* (shadow diff), and the editor shows it as an unsaved change.
459
+ * - **Held only through the data or GLSP head**, or waiting out the revert
460
+ * grace after its last client's connection was lost → the store is the
461
+ * authority, and those heads read the correction from the settled build.
462
+ * In silent mode it is also written to disk when the text it was computed
463
+ * from is what disk holds, since the repair is then the only difference;
464
+ * otherwise it waits for the next save. Editor mode always waits, and the
465
+ * repair leaves the document dirty until a save.
355
466
  * - **Closed, silent mode** → write to disk directly (disk is authoritative).
356
467
  * - **Closed, editor mode** → stage so the next open picks it up via applyEdit.
468
+ *
469
+ * A held URI is not closed. Writing it as one persists the holder's unsaved
470
+ * edits with the repair, and staging it leaves text no open reads, because
471
+ * only a first open consumes the stage.
472
+ *
473
+ * `parsedFrom` is the text the repaired AST was parsed from. Without it a
474
+ * held URI's repair is left to the next save.
357
475
  */
358
- protected async syncCorrections(document: TextDocument): Promise<void> {
359
- // Open files: delivery is the ModelService settled listener's job (by
360
- // content, via the shadow) — nothing to do here. Only the open/closed
361
- // question is asked, so no reverse address resolution is needed: a file
362
- // open under a symlinked path still answers `true`.
476
+ protected async syncCorrections(document: TextDocument, parsedFrom?: string): Promise<void> {
477
+ // Only the open/closed question is asked, so no reverse address
478
+ // resolution is needed: a file open under a symlinked path still answers
479
+ // `true`.
363
480
  if (this.textDocuments.isOpenInLanguageClient(document.uri)) {
364
481
  return;
365
482
  }
366
483
 
484
+ // A virtual document has no disk backing, so neither persisting nor
485
+ // staging applies: there is no file to write and no editor that could open
486
+ // one. `URI.fsPath` yields a path for any scheme, so handing this to the
487
+ // provider would address `<cwd>/<contributor>/<name>` — an unrelated
488
+ // location a disk-backed provider refuses outright, turning a repair that
489
+ // was never persistable into a build failure. The correction is already on
490
+ // the AST and in the text store, which is the whole of where a virtual
491
+ // document lives, so there is nothing further to propagate.
492
+ if (isVirtualUri(document.uri)) {
493
+ this.tracer.with(document.uri).debug('Virtual document: correction kept in memory, nothing to persist');
494
+ return;
495
+ }
496
+
497
+ // A document waiting out the revert grace still holds its lost client's
498
+ // unsaved text, so writing it as a closed file would persist that text.
499
+ if (this.textDocuments.isOpenInAnyClient(document.uri) || this.textDocuments.isRevertPending(document.uri)) {
500
+ if (this.syncMode === 'silent' && parsedFrom !== undefined) {
501
+ await this.persistIfDiskMatches(document, parsedFrom);
502
+ }
503
+ return;
504
+ }
505
+
367
506
  // Closed files, silent mode: write to disk directly.
368
507
  if (this.syncMode === 'silent') {
508
+ const repaired = document.getText();
369
509
  // Async write so the integrity pass does not block the event loop on a slow filesystem.
370
- await this.fileSystemProvider.writeFile(UriUtils.toUri(document.uri), document.getText());
510
+ // In the file's task queue, behind any save already queued: written
511
+ // beside one, the older repair can land last and stay on disk.
512
+ await this.services.shared.workspace.FileSystemTaskQueue.enqueue(document.uri, () =>
513
+ this.fileSystemProvider.writeFile(UriUtils.toUri(document.uri), repaired)
514
+ );
371
515
  return;
372
516
  }
373
517
 
@@ -375,6 +519,47 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
375
519
  this.textDocuments.stagePendingContent(document.uri, document.getText());
376
520
  }
377
521
 
522
+ /**
523
+ * Write the repair in `document` to disk when disk still holds `parsedFrom`,
524
+ * the text the repair was computed from — so the write carries the repair
525
+ * and nothing a holder could still discard.
526
+ *
527
+ * The read, the compare and the write run as one task in the file's task
528
+ * queue, so no save of the file lands between them. The store is compared
529
+ * again once the read returns, and the write is skipped if it no longer
530
+ * holds the repair: an edit does not wait for the queue, and writing the
531
+ * older repair then puts text on disk that no participant holds. The
532
+ * skipped repair loses nothing: the edit drives a build of its own, and a
533
+ * save writes the store's text. A file that cannot be read is left alone
534
+ * too: there is no evidence it matches.
535
+ */
536
+ protected async persistIfDiskMatches(document: TextDocument, parsedFrom: string): Promise<void> {
537
+ const uri = UriUtils.toUri(document.uri);
538
+ const repaired = document.getText();
539
+ await this.services.shared.workspace.FileSystemTaskQueue.enqueue(document.uri, async () => {
540
+ let onDisk: string;
541
+ try {
542
+ onDisk = await this.fileSystemProvider.readFile(uri);
543
+ } catch (error: unknown) {
544
+ this.tracer.with(document.uri).debug(`Held document unreadable on disk, repair left to the next save: ${String(error)}`);
545
+ return;
546
+ }
547
+ // The file itself, not the store's disk baseline: the baseline moves
548
+ // only when the server hears of a change, so it can still hold the
549
+ // parsed text after another process rewrote the file and before the
550
+ // watcher reports it, and this write would then replace that change.
551
+ if (onDisk !== parsedFrom) {
552
+ return;
553
+ }
554
+ if (this.textDocuments.get(document.uri)?.getText() !== repaired) {
555
+ this.tracer.with(document.uri).debug('Held document changed while disk was read, repair left to that change');
556
+ return;
557
+ }
558
+ await this.fileSystemProvider.writeFile(uri, repaired);
559
+ this.textDocuments.updateDiskBaseline(document.uri, repaired);
560
+ });
561
+ }
562
+
378
563
  /**
379
564
  * Format a rule's name for inclusion in user-facing diagnostic strings
380
565
  * (error messages thrown during registration). Defaults to
@@ -0,0 +1,87 @@
1
+ /********************************************************************************
2
+ * Copyright (c) 2026 CrossBreeze, EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the MIT License which is available in the project root.
6
+ *
7
+ * SPDX-License-Identifier: MIT
8
+ ********************************************************************************/
9
+
10
+ /**
11
+ * Containment-array maintenance for callers that build or delete AST nodes
12
+ * programmatically — a GLSP operation handler being the usual one.
13
+ *
14
+ * **These live beside the key provider because the constraint is the key
15
+ * provider's.** `NameBasedKeyProvider` keys a node the `NameProvider` gives no
16
+ * name from its position, as `` `${$containerProperty}@${$containerIndex}` ``,
17
+ * so a node appended with a bare `array.push` carries neither and
18
+ * `getElementKey` answers `undefined` — the element has no key at all, and the
19
+ * GLSP index cannot address it. Deleting from the middle of such a list must
20
+ * renumber the survivors, or an id derived later in the same command names the
21
+ * wrong node. Shipping the constraint's statement without the code that
22
+ * discharges it leaves every adopter in that position re-deriving thirty lines
23
+ * from prose, and both failures are silent.
24
+ *
25
+ * The population is narrower than "anyone creating elements from a diagram": it
26
+ * takes a grammar with UNNAMED element types, programmatic creation or deletion
27
+ * of them, and this key provider. A grammar whose diagram elements are all
28
+ * named never reaches it, and the two ways out are named on the provider —
29
+ * give the type a name, or bind a provider whose fallback is content-derived.
30
+ *
31
+ * **Free functions, and deliberately not a step on the AST builder.** Building a
32
+ * node detached and appending it later is legitimate, so a containment step
33
+ * cannot be mandatory; and an optional fluent step could not check the index
34
+ * against the array unless it were handed the array, at which point it is these
35
+ * signatures with more ceremony. `AstNodeInit` already accepts the three fields
36
+ * for a caller who has them, which is the case these cover: it is the INDEX that
37
+ * cannot be known without reading the array.
38
+ */
39
+
40
+ import type { AstNode } from '@hydranium/langium';
41
+ import type { Mutable } from '@hydranium/protocol';
42
+
43
+ /**
44
+ * Append `child` to `children`, stamping the Langium containment plumbing
45
+ * (`$container`, `$containerProperty`, `$containerIndex`) so a positional key
46
+ * can address it.
47
+ *
48
+ * `property` and `children` are separate parameters rather than one property
49
+ * name the function dereferences: the array is what the index is derived from,
50
+ * so taking it directly is what makes the two agree by construction instead of
51
+ * by a lookup that a typo could point elsewhere.
52
+ *
53
+ * @returns `child`, so a caller can append and use it in one expression.
54
+ */
55
+ export function appendChild<TChild extends AstNode>(container: AstNode, property: string, children: TChild[], child: TChild): TChild {
56
+ const mutable = child as Mutable<TChild>;
57
+ mutable.$container = container;
58
+ mutable.$containerProperty = property;
59
+ mutable.$containerIndex = children.length;
60
+ children.push(child);
61
+ return child;
62
+ }
63
+
64
+ /**
65
+ * Remove every entry of `children` that is in `toRemove`, then renumber the
66
+ * survivors' `$containerIndex`.
67
+ *
68
+ * Mutates `children` in place rather than returning a new array, because the
69
+ * caller's array IS the AST's containment list — replacing the reference would
70
+ * leave the parent pointing at the old one.
71
+ *
72
+ * @returns how many entries were removed, so a caller can tell a no-op delete
73
+ * from one that changed the model without comparing lengths itself.
74
+ */
75
+ export function removeChildren<TChild extends AstNode>(children: TChild[], toRemove: ReadonlySet<TChild>): number {
76
+ if (toRemove.size === 0) {
77
+ return 0;
78
+ }
79
+ const survivors = children.filter(child => !toRemove.has(child));
80
+ const removed = children.length - survivors.length;
81
+ children.length = 0;
82
+ for (const [index, child] of survivors.entries()) {
83
+ (child as Mutable<TChild>).$containerIndex = index;
84
+ children.push(child);
85
+ }
86
+ return removed;
87
+ }
@@ -7,6 +7,7 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
+ export * from './containment.js';
10
11
  export * from './element-key-provider.js';
11
12
  export * from './name-based-key-provider.js';
12
13
  export * from './positional-key-provider.js';
@@ -55,6 +55,10 @@ import { type NameProvider } from '../naming/name-provider.js';
55
55
  * - After a removal, surviving siblings need their `$containerIndex`
56
56
  * renumbered, or a key derived afterwards addresses the wrong node.
57
57
  *
58
+ * {@link appendChild} and {@link removeChildren} discharge both, and ship
59
+ * beside this provider because the constraint is this provider's rather than
60
+ * any caller's.
61
+ *
58
62
  * Adopters that need insert / delete stability for unnamed types should
59
63
  * give those types a name in the grammar, or bind a key provider whose
60
64
  * fallback is content-derived rather than positional.
@@ -28,6 +28,10 @@ import { DefaultReferenceBuilder, type ReferenceBuilder } from './scope/referenc
28
28
  import { type ScopeExtensionContribution } from './scope/scope-extension-contribution.js';
29
29
  import { DefaultScopeExtensionService, type ScopeExtensionService } from './scope/scope-extension-service.js';
30
30
  import { type Serializer } from './serialization/serializer.js';
31
+ import { type TriviaContribution } from './trivia/trivia-contribution.js';
32
+ import { type TriviaService, DefaultTriviaService } from './trivia/trivia-service.js';
33
+ import { CommentPreserverContribution } from './trivia/comment-preserver.js';
34
+ import { DocumentEndingPreserverContribution } from './trivia/document-ending-preserver.js';
31
35
  import { type UpdateRewriteContribution } from './update-rewrite/update-rewrite-contribution.js';
32
36
  import { DefaultUpdateRewriteService, type UpdateRewriteService } from './update-rewrite/update-rewrite-service.js';
33
37
  import { HydraniumDocumentValidator } from './validation/document-validator.js';
@@ -154,7 +158,7 @@ export interface ServerAddedServices {
154
158
  * multi-grammar workspaces route to the right serializer per file. The
155
159
  * framework default throws a clear "no Serializer registered" error;
156
160
  * adopters bind their own per-language subclass to enable
157
- * `ModelService.update` / `ModelService.save`.
161
+ * a client session's `update` / `save`.
158
162
  */
159
163
  Serializer: Serializer;
160
164
  };
@@ -181,10 +185,33 @@ export interface ServerAddedServices {
181
185
  */
182
186
  rules: Record<string, IntegrityRuleContribution>;
183
187
  };
188
+ trivia: {
189
+ /**
190
+ * Runs the registered trivia preservers around a write, so what a
191
+ * document carries that its model does not — its comments, the whitespace
192
+ * it ended with — survives being re-serialized from that model.
193
+ *
194
+ * A separate group from {@link Serializer} because a preserver needs the
195
+ * document being overwritten, which a serializer is never given:
196
+ * `serializeAst` could reach one through its model, but
197
+ * `serializeTransfer` receives a plain wire object with no document at
198
+ * all, so folding this in would carry trivia on one write path and
199
+ * silently not on the other.
200
+ */
201
+ TriviaService: TriviaService;
202
+ /**
203
+ * Declarative {@link TriviaContribution} group. Distinct sub-keys
204
+ * accumulate across framework + adopter modules via Langium's deep-merge;
205
+ * binding one the framework already uses REPLACES it. The framework ships
206
+ * a sub-key per preserver, so an adopter replacing `comments` keeps
207
+ * `documentEnding` and whatever else ships beside it.
208
+ */
209
+ preservers: Record<string, TriviaContribution>;
210
+ };
184
211
  updateRewrite: {
185
212
  /**
186
213
  * Per-language runner for transfer-model rewrites applied on the
187
- * structured write path of `ModelService.update` / `save`, before
214
+ * structured write path of a client session's `update` / `save`, before
188
215
  * serialisation. The RPC-update-stage sibling of the
189
216
  * {@link IntegrityService} / {@link AstExtensionService} registries.
190
217
  * Framework default is a no-op (zero rewrites registered) — adopters
@@ -236,12 +263,12 @@ export interface ServerAddedServices {
236
263
  class UnboundSerializer implements Serializer {
237
264
  serializeAst(_model: AstNode): string {
238
265
  throw new Error(
239
- 'No Serializer registered at services.serializer.Serializer — bind a Serializer<TAst, TTransfer> in your per-language module before calling ModelService.update / ModelService.save.'
266
+ "No Serializer registered at services.serializer.Serializer — bind a Serializer<TAst, TTransfer> in your per-language module before writing through a client session's update or save."
240
267
  );
241
268
  }
242
269
  serializeTransfer(_model: TransferElement): string {
243
270
  throw new Error(
244
- 'No Serializer registered at services.serializer.Serializer — bind a Serializer<TAst, TTransfer> in your per-language module before calling ModelService.update / ModelService.save.'
271
+ "No Serializer registered at services.serializer.Serializer — bind a Serializer<TAst, TTransfer> in your per-language module before writing through a client session's update or save."
245
272
  );
246
273
  }
247
274
  }
@@ -334,6 +361,17 @@ export function createServerLanguageModule(
334
361
  serializer: {
335
362
  Serializer: () => new UnboundSerializer()
336
363
  },
364
+ // Bound with a sub-key per preserver so an adopter can replace one and
365
+ // keep the other. Adopters MUST avoid these two keys for their own
366
+ // contributions unless replacement is what they mean — Langium's
367
+ // deep-merge is last-wins on same-key leaves.
368
+ trivia: {
369
+ TriviaService: services => new DefaultTriviaService(services),
370
+ preservers: {
371
+ comments: services => new CommentPreserverContribution(services),
372
+ documentEnding: () => new DocumentEndingPreserverContribution()
373
+ }
374
+ },
337
375
  // The per-URI serialise / re-parse path only fires when a rule mutates,
338
376
  // so the no-op default is safe even before adopters bind a real
339
377
  // serializer / parser.