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

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 (424) hide show
  1. package/lib/documents/ast-document-manager.d.ts +75 -16
  2. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  3. package/lib/documents/ast-document-manager.js +57 -19
  4. package/lib/documents/ast-document-manager.js.map +1 -1
  5. package/lib/documents/client-ids.d.ts +5 -19
  6. package/lib/documents/client-ids.d.ts.map +1 -1
  7. package/lib/documents/client-ids.js +5 -19
  8. package/lib/documents/client-ids.js.map +1 -1
  9. package/lib/documents/hydranium-text-documents.d.ts +132 -42
  10. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  11. package/lib/documents/hydranium-text-documents.js +199 -57
  12. package/lib/documents/hydranium-text-documents.js.map +1 -1
  13. package/lib/documents/language-client-text-shadow.d.ts +41 -9
  14. package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
  15. package/lib/documents/language-client-text-shadow.js +56 -11
  16. package/lib/documents/language-client-text-shadow.js.map +1 -1
  17. package/lib/documents/self-save-registry.d.ts +37 -6
  18. package/lib/documents/self-save-registry.d.ts.map +1 -1
  19. package/lib/documents/self-save-registry.js +28 -15
  20. package/lib/documents/self-save-registry.js.map +1 -1
  21. package/lib/index.d.ts +3 -0
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +6 -0
  24. package/lib/index.js.map +1 -1
  25. package/lib/langium/ast-extension/ast-node-builder.d.ts +32 -9
  26. package/lib/langium/ast-extension/ast-node-builder.d.ts.map +1 -1
  27. package/lib/langium/ast-extension/ast-node-builder.js +60 -22
  28. package/lib/langium/ast-extension/ast-node-builder.js.map +1 -1
  29. package/lib/langium/bootstrap.d.ts.map +1 -1
  30. package/lib/langium/bootstrap.js +16 -9
  31. package/lib/langium/bootstrap.js.map +1 -1
  32. package/lib/langium/config/configuration-provider.d.ts +81 -0
  33. package/lib/langium/config/configuration-provider.d.ts.map +1 -0
  34. package/lib/langium/config/configuration-provider.js +112 -0
  35. package/lib/langium/config/configuration-provider.js.map +1 -0
  36. package/lib/langium/config/index.d.ts +1 -0
  37. package/lib/langium/config/index.d.ts.map +1 -1
  38. package/lib/langium/config/index.js +1 -0
  39. package/lib/langium/config/index.js.map +1 -1
  40. package/lib/langium/diagnostics/lsp-logger.d.ts +17 -0
  41. package/lib/langium/diagnostics/lsp-logger.d.ts.map +1 -1
  42. package/lib/langium/diagnostics/lsp-logger.js +40 -6
  43. package/lib/langium/diagnostics/lsp-logger.js.map +1 -1
  44. package/lib/langium/document-builder/build-pipeline-integration.d.ts +21 -1
  45. package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
  46. package/lib/langium/document-builder/build-pipeline-integration.js +32 -5
  47. package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
  48. package/lib/langium/document-builder/build-session.d.ts +93 -0
  49. package/lib/langium/document-builder/build-session.d.ts.map +1 -0
  50. package/lib/langium/document-builder/build-session.js +72 -0
  51. package/lib/langium/document-builder/build-session.js.map +1 -0
  52. package/lib/langium/document-builder/document-builder.d.ts +150 -7
  53. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  54. package/lib/langium/document-builder/document-builder.js +298 -11
  55. package/lib/langium/document-builder/document-builder.js.map +1 -1
  56. package/lib/langium/document-builder/index.d.ts +1 -0
  57. package/lib/langium/document-builder/index.d.ts.map +1 -1
  58. package/lib/langium/document-builder/index.js +1 -0
  59. package/lib/langium/document-builder/index.js.map +1 -1
  60. package/lib/langium/integration-services.d.ts +45 -1
  61. package/lib/langium/integration-services.d.ts.map +1 -1
  62. package/lib/langium/integration-services.js +10 -5
  63. package/lib/langium/integration-services.js.map +1 -1
  64. package/lib/langium/integrity/integrity-service.d.ts +16 -0
  65. package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
  66. package/lib/langium/integrity/integrity-service.js +110 -2
  67. package/lib/langium/integrity/integrity-service.js.map +1 -1
  68. package/lib/langium/keys/containment.d.ts +64 -0
  69. package/lib/langium/keys/containment.d.ts.map +1 -0
  70. package/lib/langium/keys/containment.js +53 -0
  71. package/lib/langium/keys/containment.js.map +1 -0
  72. package/lib/langium/keys/index.d.ts +1 -0
  73. package/lib/langium/keys/index.d.ts.map +1 -1
  74. package/lib/langium/keys/index.js +1 -0
  75. package/lib/langium/keys/index.js.map +1 -1
  76. package/lib/langium/keys/name-based-key-provider.d.ts +4 -0
  77. package/lib/langium/keys/name-based-key-provider.d.ts.map +1 -1
  78. package/lib/langium/keys/name-based-key-provider.js +4 -0
  79. package/lib/langium/keys/name-based-key-provider.js.map +1 -1
  80. package/lib/langium/language-module.d.ts +25 -0
  81. package/lib/langium/language-module.d.ts.map +1 -1
  82. package/lib/langium/language-module.js +14 -0
  83. package/lib/langium/language-module.js.map +1 -1
  84. package/lib/langium/model-service/model-service.d.ts +161 -10
  85. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  86. package/lib/langium/model-service/model-service.js +176 -97
  87. package/lib/langium/model-service/model-service.js.map +1 -1
  88. package/lib/langium/module.d.ts +84 -15
  89. package/lib/langium/module.d.ts.map +1 -1
  90. package/lib/langium/module.js +26 -12
  91. package/lib/langium/module.js.map +1 -1
  92. package/lib/langium/naming/name-provider.d.ts +25 -4
  93. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  94. package/lib/langium/naming/name-provider.js +2 -1
  95. package/lib/langium/naming/name-provider.js.map +1 -1
  96. package/lib/langium/naming/name-separator-validation.d.ts +23 -0
  97. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  98. package/lib/langium/naming/name-separator-validation.js +30 -1
  99. package/lib/langium/naming/name-separator-validation.js.map +1 -1
  100. package/lib/langium/residency/cst-residency-service.d.ts +31 -11
  101. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  102. package/lib/langium/residency/cst-residency-service.js +13 -56
  103. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  104. package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
  105. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  106. package/lib/langium/scope/hydranium-scope-provider.js +56 -20
  107. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  108. package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
  109. package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
  110. package/lib/langium/serialization/abstract-serializer.js +12 -21
  111. package/lib/langium/serialization/abstract-serializer.js.map +1 -1
  112. package/lib/langium/service-registry.d.ts +15 -1
  113. package/lib/langium/service-registry.d.ts.map +1 -1
  114. package/lib/langium/service-registry.js +16 -0
  115. package/lib/langium/service-registry.js.map +1 -1
  116. package/lib/langium/shared-services.d.ts +15 -2
  117. package/lib/langium/shared-services.d.ts.map +1 -1
  118. package/lib/langium/shared-services.js.map +1 -1
  119. package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
  120. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  121. package/lib/langium/transfer/transfer-encoder.js +38 -16
  122. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  123. package/lib/langium/trivia/comment-preserver.d.ts +423 -0
  124. package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
  125. package/lib/langium/trivia/comment-preserver.js +906 -0
  126. package/lib/langium/trivia/comment-preserver.js.map +1 -0
  127. package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
  128. package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
  129. package/lib/langium/trivia/document-ending-preserver.js +48 -0
  130. package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
  131. package/lib/langium/trivia/index.d.ts +14 -0
  132. package/lib/langium/trivia/index.d.ts.map +1 -0
  133. package/lib/langium/trivia/index.js +14 -0
  134. package/lib/langium/trivia/index.js.map +1 -0
  135. package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
  136. package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
  137. package/lib/langium/trivia/trivia-contribution.js +10 -0
  138. package/lib/langium/trivia/trivia-contribution.js.map +1 -0
  139. package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
  140. package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
  141. package/lib/langium/trivia/trivia-preserver.js +10 -0
  142. package/lib/langium/trivia/trivia-preserver.js.map +1 -0
  143. package/lib/langium/trivia/trivia-service.d.ts +70 -0
  144. package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
  145. package/lib/langium/trivia/trivia-service.js +56 -0
  146. package/lib/langium/trivia/trivia-service.js.map +1 -0
  147. package/lib/langium/validation/document-validator.d.ts +294 -10
  148. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  149. package/lib/langium/validation/document-validator.js +448 -5
  150. package/lib/langium/validation/document-validator.js.map +1 -1
  151. package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
  152. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  153. package/lib/langium/workspace/document-uri-policy.js +3 -4
  154. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  155. package/lib/langium/workspace/file-system-provider.d.ts +9 -1
  156. package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
  157. package/lib/langium/workspace/file-system-provider.js +40 -7
  158. package/lib/langium/workspace/file-system-provider.js.map +1 -1
  159. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  160. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  161. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  162. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  163. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
  164. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  165. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  166. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  167. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
  168. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  169. package/lib/langium/workspace/in-memory-file-system-provider.js +47 -9
  170. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  171. package/lib/langium/workspace/initialize-workspace.d.ts +23 -2
  172. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  173. package/lib/langium/workspace/initialize-workspace.js +13 -5
  174. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  175. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  176. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  177. package/lib/langium/workspace/langium-documents.js +83 -28
  178. package/lib/langium/workspace/langium-documents.js.map +1 -1
  179. package/lib/langium/workspace/virtual-document.d.ts +18 -3
  180. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  181. package/lib/langium/workspace/virtual-document.js +19 -2
  182. package/lib/langium/workspace/virtual-document.js.map +1 -1
  183. package/lib/locale/index.d.ts +10 -0
  184. package/lib/locale/index.d.ts.map +1 -0
  185. package/lib/locale/index.js +10 -0
  186. package/lib/locale/index.js.map +1 -0
  187. package/lib/locale/server-locale.d.ts +73 -0
  188. package/lib/locale/server-locale.d.ts.map +1 -0
  189. package/lib/locale/server-locale.js +61 -0
  190. package/lib/locale/server-locale.js.map +1 -0
  191. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  192. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  193. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  194. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  195. package/lib/lsp/connection-features.d.ts +23 -0
  196. package/lib/lsp/connection-features.d.ts.map +1 -0
  197. package/lib/lsp/connection-features.js +60 -0
  198. package/lib/lsp/connection-features.js.map +1 -0
  199. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  200. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  201. package/lib/lsp/diagnostics-connection.js +53 -0
  202. package/lib/lsp/diagnostics-connection.js.map +1 -0
  203. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  204. package/lib/lsp/hydranium-document-update-handler.js +10 -1
  205. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  206. package/lib/lsp/index.d.ts +3 -1
  207. package/lib/lsp/index.d.ts.map +1 -1
  208. package/lib/lsp/index.js +3 -1
  209. package/lib/lsp/index.js.map +1 -1
  210. package/lib/lsp/lsp-latency.d.ts +39 -0
  211. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  212. package/lib/lsp/lsp-latency.js +58 -0
  213. package/lib/lsp/lsp-latency.js.map +1 -0
  214. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  215. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  216. package/lib/lsp/semantic-token-provider.js +55 -8
  217. package/lib/lsp/semantic-token-provider.js.map +1 -1
  218. package/lib/lsp/start-language-server.d.ts +5 -3
  219. package/lib/lsp/start-language-server.d.ts.map +1 -1
  220. package/lib/lsp/start-language-server.js +10 -3
  221. package/lib/lsp/start-language-server.js.map +1 -1
  222. package/lib/messages/carriers.d.ts +54 -0
  223. package/lib/messages/carriers.d.ts.map +1 -0
  224. package/lib/messages/carriers.js +62 -0
  225. package/lib/messages/carriers.js.map +1 -0
  226. package/lib/messages/index.d.ts +25 -0
  227. package/lib/messages/index.d.ts.map +1 -0
  228. package/lib/messages/index.js +25 -0
  229. package/lib/messages/index.js.map +1 -0
  230. package/lib/messages/renderer.d.ts +136 -0
  231. package/lib/messages/renderer.d.ts.map +1 -0
  232. package/lib/messages/renderer.js +159 -0
  233. package/lib/messages/renderer.js.map +1 -0
  234. package/lib/node/heap-ceiling.d.ts +61 -0
  235. package/lib/node/heap-ceiling.d.ts.map +1 -0
  236. package/lib/node/heap-ceiling.js +72 -0
  237. package/lib/node/heap-ceiling.js.map +1 -0
  238. package/lib/node/index.d.ts +1 -0
  239. package/lib/node/index.d.ts.map +1 -1
  240. package/lib/node/index.js +1 -0
  241. package/lib/node/index.js.map +1 -1
  242. package/lib/node/latency-from-env.d.ts +1 -1
  243. package/lib/node/latency-from-env.js +1 -1
  244. package/lib/node/measure-memory.d.ts.map +1 -1
  245. package/lib/node/measure-memory.js +1 -0
  246. package/lib/node/measure-memory.js.map +1 -1
  247. package/lib/node/node-file-system-provider.d.ts +35 -1
  248. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  249. package/lib/node/node-file-system-provider.js +140 -45
  250. package/lib/node/node-file-system-provider.js.map +1 -1
  251. package/lib/node/profile-capture.d.ts +8 -2
  252. package/lib/node/profile-capture.d.ts.map +1 -1
  253. package/lib/node/profile-capture.js.map +1 -1
  254. package/lib/node/profiling-run.d.ts +2 -2
  255. package/lib/node/profiling-run.js +1 -1
  256. package/lib/node/profiling-run.js.map +1 -1
  257. package/lib/node/rename-over-open-readers.d.ts +52 -0
  258. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  259. package/lib/node/rename-over-open-readers.js +66 -0
  260. package/lib/node/rename-over-open-readers.js.map +1 -0
  261. package/lib/node/socket-launcher.d.ts +15 -0
  262. package/lib/node/socket-launcher.d.ts.map +1 -1
  263. package/lib/node/socket-launcher.js +3 -2
  264. package/lib/node/socket-launcher.js.map +1 -1
  265. package/lib/testing/fake-document.d.ts +16 -4
  266. package/lib/testing/fake-document.d.ts.map +1 -1
  267. package/lib/testing/fake-document.js +18 -3
  268. package/lib/testing/fake-document.js.map +1 -1
  269. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  270. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  271. package/lib/testing/make-noop-shared-services.js +28 -2
  272. package/lib/testing/make-noop-shared-services.js.map +1 -1
  273. package/lib/testing/make-test-services.d.ts +45 -9
  274. package/lib/testing/make-test-services.d.ts.map +1 -1
  275. package/lib/testing/make-test-services.js +19 -3
  276. package/lib/testing/make-test-services.js.map +1 -1
  277. package/lib/testing/node/index.d.ts +1 -0
  278. package/lib/testing/node/index.d.ts.map +1 -1
  279. package/lib/testing/node/index.js +1 -0
  280. package/lib/testing/node/index.js.map +1 -1
  281. package/lib/testing/node/lsp-harness.d.ts.map +1 -1
  282. package/lib/testing/node/lsp-harness.js +2 -0
  283. package/lib/testing/node/lsp-harness.js.map +1 -1
  284. package/lib/testing/node/lsp-server-connection.d.ts +10 -2
  285. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  286. package/lib/testing/node/lsp-server-connection.js +42 -9
  287. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  288. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  289. package/lib/testing/node/scratch-workspace.js +9 -4
  290. package/lib/testing/node/scratch-workspace.js.map +1 -1
  291. package/lib/testing/node/spawned-server.d.ts +14 -0
  292. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  293. package/lib/testing/node/spawned-server.js +33 -1
  294. package/lib/testing/node/spawned-server.js.map +1 -1
  295. package/lib/testing/node/unhandled-rejections.d.ts +20 -0
  296. package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
  297. package/lib/testing/node/unhandled-rejections.js +32 -0
  298. package/lib/testing/node/unhandled-rejections.js.map +1 -0
  299. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  300. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  301. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  302. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  303. package/lib/testing/playwright/index.d.ts +1 -0
  304. package/lib/testing/playwright/index.d.ts.map +1 -1
  305. package/lib/testing/playwright/index.js +1 -0
  306. package/lib/testing/playwright/index.js.map +1 -1
  307. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  308. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  309. package/lib/testing/playwright/server-log-capture.js +21 -4
  310. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  311. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  312. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  313. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  314. package/lib/testing/stub-ast-document-manager.d.ts +5 -5
  315. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  316. package/lib/testing/stub-ast-document-manager.js +4 -6
  317. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  318. package/lib/testing/stub-document-builder.d.ts +8 -0
  319. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  320. package/lib/testing/stub-document-builder.js +22 -3
  321. package/lib/testing/stub-document-builder.js.map +1 -1
  322. package/lib/testing/stub-langium-documents.d.ts +3 -2
  323. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  324. package/lib/testing/stub-langium-documents.js.map +1 -1
  325. package/lib/testing/stub-model-service.d.ts +5 -4
  326. package/lib/testing/stub-model-service.d.ts.map +1 -1
  327. package/lib/testing/stub-model-service.js +2 -2
  328. package/lib/testing/stub-model-service.js.map +1 -1
  329. package/lib/util/connection-liveness.d.ts +7 -6
  330. package/lib/util/connection-liveness.d.ts.map +1 -1
  331. package/lib/util/connection-liveness.js +35 -14
  332. package/lib/util/connection-liveness.js.map +1 -1
  333. package/package.json +17 -8
  334. package/src/documents/ast-document-manager.ts +129 -36
  335. package/src/documents/client-ids.ts +5 -22
  336. package/src/documents/hydranium-text-documents.ts +256 -81
  337. package/src/documents/language-client-text-shadow.ts +59 -11
  338. package/src/documents/self-save-registry.ts +52 -13
  339. package/src/index.ts +6 -0
  340. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  341. package/src/langium/bootstrap.ts +16 -9
  342. package/src/langium/config/configuration-provider.ts +119 -0
  343. package/src/langium/config/index.ts +1 -0
  344. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  345. package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
  346. package/src/langium/document-builder/build-session.ts +87 -0
  347. package/src/langium/document-builder/document-builder.ts +339 -11
  348. package/src/langium/document-builder/index.ts +1 -0
  349. package/src/langium/integration-services.ts +67 -6
  350. package/src/langium/integrity/integrity-service.ts +116 -2
  351. package/src/langium/keys/containment.ts +87 -0
  352. package/src/langium/keys/index.ts +1 -0
  353. package/src/langium/keys/name-based-key-provider.ts +4 -0
  354. package/src/langium/language-module.ts +38 -0
  355. package/src/langium/model-service/model-service.ts +266 -34
  356. package/src/langium/module.ts +106 -21
  357. package/src/langium/naming/name-provider.ts +27 -5
  358. package/src/langium/naming/name-separator-validation.ts +34 -5
  359. package/src/langium/residency/cst-residency-service.ts +36 -14
  360. package/src/langium/scope/hydranium-scope-provider.ts +60 -22
  361. package/src/langium/serialization/abstract-serializer.ts +13 -32
  362. package/src/langium/service-registry.ts +19 -2
  363. package/src/langium/shared-services.ts +19 -2
  364. package/src/langium/transfer/transfer-encoder.ts +85 -24
  365. package/src/langium/trivia/comment-preserver.ts +1100 -0
  366. package/src/langium/trivia/document-ending-preserver.ts +55 -0
  367. package/src/langium/trivia/index.ts +14 -0
  368. package/src/langium/trivia/trivia-contribution.ts +39 -0
  369. package/src/langium/trivia/trivia-preserver.ts +54 -0
  370. package/src/langium/trivia/trivia-service.ts +99 -0
  371. package/src/langium/validation/document-validator.ts +537 -11
  372. package/src/langium/workspace/document-uri-policy.ts +3 -4
  373. package/src/langium/workspace/file-system-provider.ts +48 -8
  374. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  375. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  376. package/src/langium/workspace/in-memory-file-system-provider.ts +53 -10
  377. package/src/langium/workspace/initialize-workspace.ts +35 -5
  378. package/src/langium/workspace/langium-documents.ts +106 -33
  379. package/src/langium/workspace/virtual-document.ts +21 -3
  380. package/src/locale/index.ts +10 -0
  381. package/src/locale/server-locale.ts +86 -0
  382. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  383. package/src/lsp/connection-features.ts +67 -0
  384. package/src/lsp/diagnostics-connection.ts +56 -0
  385. package/src/lsp/hydranium-document-update-handler.ts +10 -1
  386. package/src/lsp/index.ts +3 -1
  387. package/src/lsp/lsp-latency.ts +62 -0
  388. package/src/lsp/semantic-token-provider.ts +74 -7
  389. package/src/lsp/start-language-server.ts +10 -3
  390. package/src/messages/carriers.ts +78 -0
  391. package/src/messages/index.ts +36 -0
  392. package/src/messages/renderer.ts +206 -0
  393. package/src/node/heap-ceiling.ts +108 -0
  394. package/src/node/index.ts +1 -0
  395. package/src/node/latency-from-env.ts +1 -1
  396. package/src/node/measure-memory.ts +1 -0
  397. package/src/node/node-file-system-provider.ts +154 -47
  398. package/src/node/profile-capture.ts +8 -2
  399. package/src/node/profiling-run.ts +3 -3
  400. package/src/node/rename-over-open-readers.ts +78 -0
  401. package/src/node/socket-launcher.ts +18 -2
  402. package/src/testing/fake-document.ts +24 -5
  403. package/src/testing/make-noop-shared-services.ts +50 -2
  404. package/src/testing/make-test-services.ts +74 -18
  405. package/src/testing/node/index.ts +1 -0
  406. package/src/testing/node/lsp-harness.ts +2 -0
  407. package/src/testing/node/lsp-server-connection.ts +60 -10
  408. package/src/testing/node/scratch-workspace.ts +9 -4
  409. package/src/testing/node/spawned-server.ts +35 -1
  410. package/src/testing/node/unhandled-rejections.ts +31 -0
  411. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  412. package/src/testing/playwright/index.ts +1 -0
  413. package/src/testing/playwright/server-log-capture.ts +24 -5
  414. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  415. package/src/testing/stub-ast-document-manager.ts +10 -14
  416. package/src/testing/stub-document-builder.ts +32 -3
  417. package/src/testing/stub-langium-documents.ts +3 -2
  418. package/src/testing/stub-model-service.ts +7 -6
  419. package/src/util/connection-liveness.ts +37 -14
  420. package/lib/lsp/instrument-connection.d.ts +0 -36
  421. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  422. package/lib/lsp/instrument-connection.js +0 -62
  423. package/lib/lsp/instrument-connection.js.map +0 -1
  424. package/src/lsp/instrument-connection.ts +0 -66
@@ -11,11 +11,12 @@ import {
11
11
  type CanonicalUri,
12
12
  type CloseModelArgs,
13
13
  ConflictError,
14
+ isSnapshotVersion,
15
+ defineMessage,
14
16
  Logger,
15
17
  type MaybeObservableValue,
16
18
  type MaybePromise,
17
19
  ObservableValue,
18
- type TransferDiagnostic,
19
20
  type TransferElement,
20
21
  type OpenModelArgs,
21
22
  type Tracer,
@@ -23,6 +24,7 @@ import {
23
24
  type TransferUpdateArgs
24
25
  } from '@hydranium/protocol';
25
26
  import { type AstNode, DocumentState, type LangiumDocument, UriUtils, type URI } from '@hydranium/langium';
27
+ import { type AstDiagnostic } from '../validation/document-validator.js';
26
28
  import { type DocumentUriPolicy } from '../workspace/document-uri-policy.js';
27
29
  import { ReentrantWriteLockError, isInsideWriteLock } from '../workspace/write-lock-scope.js';
28
30
  import { type CancellationToken, type Disposable } from 'vscode-languageserver';
@@ -34,6 +36,23 @@ import { labelPhaseListener } from '../document-builder/labeled-phase-listener.j
34
36
  import { LANGUAGE_CLIENT_ID } from '../../documents/client-ids.js';
35
37
  import { type ServerSharedServices } from '../module.js';
36
38
 
39
+ /**
40
+ * The undo-stack entry for a server-authored write pushed to the editor.
41
+ *
42
+ * **A user-facing LABEL, not a log string**, which is easy to miss because it
43
+ * travels as an options field rather than as a message: LSP specifies
44
+ * `ApplyWorkspaceEditParams.label` as "presented in the user interface for
45
+ * example on an undo stack to undo the workspace edit". So a user who edits
46
+ * through a form or drags a diagram node reads this in their editor's undo menu
47
+ * — which is why it is rendered like any other message the server sends rather
48
+ * than left as the English literal it was.
49
+ *
50
+ * Parameterless deliberately. The obvious improvement is to name the document,
51
+ * and it is the wrong one: an undo menu is already grouped under the file, so
52
+ * the URI would be noise in the one place it is redundant.
53
+ */
54
+ export const MODEL_UPDATE_EDIT = defineMessage('hydranium/core/model-update-edit', 'Update Model');
55
+
37
56
  /** Max time {@link ModelService.settleSave} waits for the build to settle and the sync chain to drain. */
38
57
  const SAVE_SETTLE_TIMEOUT_MS = 10_000;
39
58
 
@@ -115,6 +134,10 @@ export interface ModelServiceOptions extends LogNameOptions {
115
134
  }
116
135
 
117
136
  /**
137
+ * The seam every non-LSP head talks to: the data server, the GLSP head and an
138
+ * adopter's own services reach documents through this slot rather than through
139
+ * the workspace stores.
140
+ *
118
141
  * In-process facade over the framework's document plumbing
119
142
  * (`HydraniumTextDocuments`, `LangiumDocuments`,
120
143
  * `DocumentBuilder`, `WritableFileSystemProvider`). Owns the
@@ -173,18 +196,89 @@ export interface ModelServiceOptions extends LogNameOptions {
173
196
  * by URI happens here so consumers can subscribe per-document without
174
197
  * implementing the URI gate at each callsite.
175
198
  *
199
+ * **Two families, and the distinction matters more than the names suggest.**
200
+ * `waitFor*` is a pure wait — it never triggers a build, so a caller waiting on
201
+ * a document no build has touched waits until something else builds it.
202
+ * `ensureDocumentState` and the phase shorthands over it *dispatch*: warm
203
+ * documents are awaited, cold ones are built.
204
+ *
205
+ * **Diagnostics are typed `never` below `Validated`.** Validation is the last
206
+ * phase, so at any earlier landmark the array either is not yet computed or
207
+ * still holds the previous build's, and reading it would take stale results
208
+ * for fresh ones. A caller that needs diagnostics asks for {@link validated}.
209
+ *
210
+ * What the `never` buys, exactly: reading a field off an element is a compile
211
+ * error, and nothing can be appended. It does NOT stop a caller assigning an
212
+ * element to a typed variable, because `never` is assignable to everything — so
213
+ * this is a guard against reaching for diagnostics by accident, not a seal
214
+ * against doing it deliberately.
215
+ *
216
+ * The waits resolve at or ABOVE their target, so an already-validated document
217
+ * does carry usable diagnostics and the `never` over-forbids there. That
218
+ * direction is the safe one: the alternative permits stale reads silently. A
219
+ * member taking a phase as a PARAMETER cannot judge statically and so returns
220
+ * `TDiagnostic`, leaving the choice to the caller.
221
+ *
176
222
  * **Generic parameters.**
177
223
  * - `TAst` — the AST root type each consumer expects on the returned
178
224
  * {@link AstDocument}. Constrained to {@link AstNode}.
179
- * - `TDiagnostic` — wire diagnostic shape used by the injected
180
- * `TransferEncoder`. Defaults to {@link TransferDiagnostic}.
225
+ * - `TDiagnostic` — the AST-layer diagnostic: whatever the build left on
226
+ * `LangiumDocument.diagnostics`, carried through on the returned
227
+ * {@link AstDocument}. Defaults to {@link AstDiagnostic}.
228
+ * **Not the `TransferEncoder`'s parameter of the same name**, which is
229
+ * that encoder's OUTPUT and so names the wire shape. This one names its
230
+ * input, and an adopter binds the two to different types.
181
231
  * - `TTransfer` — transfer-model root accepted by `update` / `save`
182
232
  * args. Constrained to {@link TransferElement}. Defaults to the
183
233
  * structural base.
184
234
  */
185
- export class ModelService<
235
+ export interface ModelService<
236
+ TAst extends AstNode,
237
+ TDiagnostic extends AstDiagnostic = AstDiagnostic,
238
+ TTransfer extends TransferElement = TransferElement
239
+ > {
240
+ /**
241
+ * Resolves once the workspace has been initialised and its first build has
242
+ * completed — the gate every read should wait behind, since a document
243
+ * queried before it may be unbuilt and reach no phase.
244
+ *
245
+ * A property rather than a method, matching `ProjectManager.ready` and
246
+ * Langium's `WorkspaceManager.ready`. An implementation needing a stricter
247
+ * gate supplies a Promise that awaits its own concern as well.
248
+ */
249
+ readonly ready: Promise<void>;
250
+
251
+ // Pure waits — never trigger a build.
252
+ waitForDocumentState(uri: string, state: DocumentState, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
253
+ waitForDocumentSettled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
254
+ waitForBuilderState(state: DocumentState, cancelToken?: CancellationToken): Promise<void>;
255
+
256
+ // Wait if warm, build if cold.
257
+ ensureDocumentState(uri: string, state?: DocumentState, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
258
+ rebuild(uri: string, state?: DocumentState, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
259
+ parsed(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
260
+ linked(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
261
+ settled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
262
+ indexed(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>>;
263
+ validated(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
264
+
265
+ update(args: TransferUpdateArgs<TTransfer>, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
266
+ save(args: TransferSaveArgs<TTransfer>, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>>;
267
+
268
+ open(args: OpenModelArgs): Promise<Disposable>;
269
+ close(args: CloseModelArgs): Promise<void>;
270
+ isOpen(uri: string): boolean;
271
+ snapshot(uri: string): AstDocument<TAst, TDiagnostic> | undefined;
272
+ getDocument(uri: string): LangiumDocument | undefined;
273
+
274
+ onModelUpdated(uri: string, listener: (event: AstDocumentUpdatedEvent<TAst, TDiagnostic>) => void): Disposable;
275
+ onModelSaved(uri: string, listener: (event: AstDocumentSavedEvent<TAst, TDiagnostic>) => void): Disposable;
276
+ onClientClosed(uri: string, clientId: string, listener: () => void): Disposable;
277
+ }
278
+
279
+ export class DefaultModelService<
186
280
  TAst extends AstNode,
187
- TDiagnostic = TransferDiagnostic,
281
+ TDiagnostic extends AstDiagnostic = AstDiagnostic,
188
282
  /**
189
283
  * Structured payload accepted by `update` / `save`. Constrained to
190
284
  * {@link TransferElement} — the minimal `{ readonly $type: string }`
@@ -195,7 +289,7 @@ export class ModelService<
195
289
  * integrity service) that pass AST roots remain compatible.
196
290
  */
197
291
  TTransfer extends TransferElement = TransferElement
198
- > {
292
+ > implements ModelService<TAst, TDiagnostic, TTransfer> {
199
293
  protected readonly tracer: Tracer;
200
294
  /**
201
295
  * The single document-identity seam (`services.workspace.DocumentUriPolicy`),
@@ -329,8 +423,12 @@ export class ModelService<
329
423
  * {@link waitForDocumentState} for the common "wait until content is stable"
330
424
  * case (e.g. settling a save). Pure wait — does not trigger a build.
331
425
  */
332
- async waitForDocumentSettled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>> {
333
- return this.waitForDocumentStateCanonical(this.uriPolicy.canonicalUri(uri), IntegrityService.SettledState, cancelToken);
426
+ async waitForDocumentSettled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
427
+ return (await this.waitForDocumentStateCanonical(
428
+ this.uriPolicy.canonicalUri(uri),
429
+ IntegrityService.SettledState,
430
+ cancelToken
431
+ )) as AstDocument<TAst, never>;
334
432
  }
335
433
 
336
434
  /**
@@ -484,11 +582,19 @@ export class ModelService<
484
582
  *
485
583
  * Phase invariants encoded in the return type:
486
584
  * - `parsed` / `linked` / `settled` / `indexed` return
487
- * `AstDocument<TAst, never>` — diagnostics array is empty by phase
488
- * contract.
585
+ * `AstDocument<TAst, never>` — no diagnostics have been computed at
586
+ * those phases.
489
587
  * - `validated` returns `AstDocument<TAst, TDiagnostic>` — diagnostics
490
588
  * are populated.
491
589
  *
590
+ * **The `never` is enforced, not merely declared.** The wait underneath
591
+ * resolves at or ABOVE the requested state, so a document something else
592
+ * already carried past `Validated` would otherwise come back from
593
+ * `settled()` carrying a full diagnostics array typed `never`; these four
594
+ * strip it. An empty array here therefore means "this read does not report
595
+ * diagnostics", never "this document is clean" — call {@link validated}
596
+ * when the answer has to mean the second.
597
+ *
492
598
  * `settled` is the integrity-overlay name for "all integrity rules
493
599
  * have fired"; it maps to {@link IntegrityService.SettledState} (which
494
600
  * equals `DocumentState.IndexedReferences`), but the dedicated method
@@ -496,19 +602,19 @@ export class ModelService<
496
602
  * ever moves.
497
603
  */
498
604
  async parsed(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
499
- return this.ensureDocumentState(uri, DocumentState.Parsed, cancelToken) as Promise<AstDocument<TAst, never>>;
605
+ return this.withoutDiagnostics(await this.ensureDocumentState(uri, DocumentState.Parsed, cancelToken));
500
606
  }
501
607
 
502
608
  async linked(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
503
- return this.ensureDocumentState(uri, DocumentState.Linked, cancelToken) as Promise<AstDocument<TAst, never>>;
609
+ return this.withoutDiagnostics(await this.ensureDocumentState(uri, DocumentState.Linked, cancelToken));
504
610
  }
505
611
 
506
612
  async settled(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
507
- return this.ensureDocumentState(uri, IntegrityService.SettledState, cancelToken) as Promise<AstDocument<TAst, never>>;
613
+ return this.withoutDiagnostics(await this.ensureDocumentState(uri, IntegrityService.SettledState, cancelToken));
508
614
  }
509
615
 
510
616
  async indexed(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, never>> {
511
- return this.ensureDocumentState(uri, DocumentState.IndexedReferences, cancelToken) as Promise<AstDocument<TAst, never>>;
617
+ return this.withoutDiagnostics(await this.ensureDocumentState(uri, DocumentState.IndexedReferences, cancelToken));
512
618
  }
513
619
 
514
620
  async validated(uri: string, cancelToken?: CancellationToken): Promise<AstDocument<TAst, TDiagnostic>> {
@@ -596,22 +702,44 @@ export class ModelService<
596
702
  const run = async <T>(stage: string, fn: () => MaybePromise<T>): Promise<T> => (session ? session.scope(stage, fn) : fn());
597
703
  // Open WITH the new text so a cold URI (no open editor, no file on disk) is
598
704
  // created from the payload rather than read from the filesystem — `update`
599
- // is an upsert. For an already-open document `open` refreshes content (the
600
- // text is ignored on that branch), so existing-document behaviour is
601
- // unchanged. `version` is intentionally NOT forwarded to `open`: a cold
602
- // create stays at its initial version, so a based-on-`version` update of a
603
- // not-yet-existing document still trips the conflict gate below.
705
+ // is an upsert. For an already-open document `open` only attaches the
706
+ // client (the text is ignored on that branch), and the actual update is
707
+ // `AstDocumentManager.update`'s. `version` is intentionally NOT
708
+ // forwarded to `open`, so a cold create stays at its initial version
709
+ // rather than adopting a number the caller chose.
710
+ //
711
+ // The gate reads the version and decides BEFORE the open, and both halves
712
+ // of that matter.
713
+ //
714
+ // Reading first, because for a document no client holds open the open
715
+ // assigns the shared version from the INCOMING text, so a version read
716
+ // afterwards has already absorbed the caller's own write. Gating on it
717
+ // rejects every modifying write to a closed document, comparing the
718
+ // caller's `basedOn` against a number the caller itself produced — and a
719
+ // serialised round-trip that is not byte-identical to the stored text is
720
+ // enough to trigger that. Reading first keeps both cases the gate exists
721
+ // for: an unknown URI answers 0, so a based-on-version update of a
722
+ // not-yet-existing document still trips it, and a genuine conflict still
723
+ // trips it, another writer having advanced the sequence past the version
724
+ // the caller read.
725
+ //
726
+ // Deciding first, because the open is a mutation and `update` is an
727
+ // upsert: for a cold URI it installs the payload in the shared text store
728
+ // and records a hold for the writing client. Throwing after it hands the
729
+ // caller its rejection while the server keeps the refused text — and the
730
+ // hold outlives the call, because a client whose write failed has no
731
+ // reason to close a document it never asked to open. Serialisation is
732
+ // held back too; it is the adopter's code and need not be side-effect
733
+ // free.
734
+ const currentVersion = this.services.workspace.TextDocuments.version(uri);
735
+ if (isSnapshotVersion(args.basedOn) && currentVersion !== args.basedOn) {
736
+ // Distinct from the post-build "superseded" debug line below: this is a
737
+ // based-on-stale rejection (the write never applies), not two writes racing.
738
+ this.tracer.debug(`Conflict on ${uri}: based-on v${args.basedOn} stale, server at v${currentVersion}`);
739
+ throw new ConflictError(uri, args.basedOn, currentVersion);
740
+ }
604
741
  const text = await run('serialize', () => this.modelToText(uri, args.model, cancelToken));
605
742
  await run('open', () => this.open({ uri, clientId: args.clientId, text }));
606
- if (args.baseVersion !== undefined) {
607
- const current = this.services.workspace.TextDocuments.version(uri);
608
- if (current !== args.baseVersion) {
609
- // Distinct from the post-build "superseded" debug line below: this is a
610
- // based-on-stale rejection (the write never applies), not two writes racing.
611
- this.tracer.debug(`Conflict on ${uri}: based-on v${args.baseVersion} stale, server at v${current}`);
612
- throw new ConflictError(uri, args.baseVersion, current);
613
- }
614
- }
615
743
  const appliedVersion = await run('apply', () => this.services.workspace.AstDocumentManager.update(uri, text, args.clientId));
616
744
  // Dispatch through the public `rebuild` (which re-canonicalizes the already-
617
745
  // canonical `uri` once, idempotently) rather than `rebuildCanonical`, so an
@@ -733,6 +861,31 @@ export class ModelService<
733
861
  return this.services.workspace.AstDocumentManager.isOpen(uri);
734
862
  }
735
863
 
864
+ /**
865
+ * Snapshot of `uri` as it stands RIGHT NOW — the synchronous sibling of the
866
+ * phase reads, which all wait. `undefined` when no document is registered.
867
+ *
868
+ * **This is what a writer wants, and {@link getDocument} is not.** The
869
+ * envelope's `version` is copied by value at projection time, so it cannot
870
+ * move afterwards; a version read off the live document at write time is
871
+ * whatever the server is at *now*, which is the number an optimistic gate is
872
+ * about to compare it against.
873
+ *
874
+ * Diagnostics only from a document that has reached `Validated`, and an
875
+ * empty array otherwise. Unlike the phase reads this one names no phase, so
876
+ * the state it finds is the only thing that can say whether the array
877
+ * describes the content being handed back or whatever an earlier build left.
878
+ * A caller that needs them unconditionally waits, via {@link validated}.
879
+ */
880
+ snapshot(uri: string): AstDocument<TAst, TDiagnostic> | undefined {
881
+ const document = this.getDocument(uri);
882
+ if (!document) {
883
+ return undefined;
884
+ }
885
+ const envelope = AstDocument.from<TAst, TDiagnostic>(document);
886
+ return document.state >= DocumentState.Validated ? envelope : this.withoutDiagnostics(envelope);
887
+ }
888
+
736
889
  /**
737
890
  * The built {@link LangiumDocument} for `uri`, looked up by canonical identity —
738
891
  * the synchronous, phase-agnostic sibling of {@link ensureDocumentState} /
@@ -741,6 +894,13 @@ export class ModelService<
741
894
  * into `LangiumDocuments` directly: a symlinked / `..` / case-divergent URI
742
895
  * still resolves to the one document the build keys by its real path. Returns
743
896
  * `undefined` if no document is registered for `uri`.
897
+ *
898
+ * **Live, so do not take a based-on version off it.** `textDocument` is the
899
+ * store's own object rather than a copy, so `.version` read here answers for
900
+ * the moment of the READ, not the moment of the earlier content — pass it to
901
+ * a write and the server compares its current version against itself, the
902
+ * gate passes unconditionally, and a concurrent edit is overwritten with
903
+ * nothing logged. Use {@link snapshot} for that, or a phase read.
744
904
  */
745
905
  getDocument(uri: string): LangiumDocument | undefined {
746
906
  return this.services.workspace.AstDocumentManager.getDocument(uri);
@@ -834,17 +994,17 @@ export class ModelService<
834
994
  *
835
995
  * The signal is "a known client other than the language client authored this
836
996
  * version **and** the URI was in the last build's changed set
837
- * (`isDirectChange`)". A framework-internal rebuild reports no author
997
+ * (`isTriggeringEdit`)". A framework-internal rebuild reports no author
838
998
  * (`getAuthor` → `undefined`), so it fails `hasKnownAuthor` without comparing
839
999
  * against a sentinel. This is NOT redundant with content/registration — it
840
1000
  * distinguishes "client edited" from "framework rebuilt", which neither the
841
- * shadow nor `isDirectChange` alone can.
1001
+ * shadow nor `isTriggeringEdit` alone can.
842
1002
  */
843
1003
  protected isNonLanguageClientEdit(document: LangiumDocument): boolean {
844
1004
  const documents = this.services.workspace.AstDocumentManager;
845
1005
  const author = documents.getAuthor(document);
846
1006
  const hasKnownAuthor = !!author && author !== LANGUAGE_CLIENT_ID;
847
- return hasKnownAuthor && documents.isDirectChange(document.textDocument.uri);
1007
+ return hasKnownAuthor && documents.isTriggeringEdit(document.textDocument.uri);
848
1008
  }
849
1009
 
850
1010
  /**
@@ -878,13 +1038,26 @@ export class ModelService<
878
1038
  this.syncChains.set(uri, chain);
879
1039
  }
880
1040
 
1041
+ /**
1042
+ * The undo-stack label for a server-authored write, in the locale the server
1043
+ * was handed at init.
1044
+ *
1045
+ * One method rather than the literal at each `applyEdit`, because the two
1046
+ * call sites are the same edit — a push and its full-replace retry — and an
1047
+ * undo menu showing two different words for one operation would read as two
1048
+ * operations.
1049
+ */
1050
+ protected editLabel(): string {
1051
+ return this.services.MessageRenderer.renderMessage(MODEL_UPDATE_EDIT);
1052
+ }
1053
+
881
1054
  protected async drainSyncQueue(uri: string): Promise<void> {
882
1055
  const uriLogger = this.tracer.withUri(uri);
883
1056
  while (this.pendingSync.has(uri)) {
884
1057
  const text = this.pendingSync.get(uri)!;
885
1058
  this.pendingSync.delete(uri);
886
1059
  try {
887
- let result = await this.services.workspace.TextDocuments.applyEditToLanguageClient(uri, text, { label: 'Update Model' });
1060
+ let result = await this.services.workspace.TextDocuments.applyEditToLanguageClient(uri, text, { label: this.editLabel() });
888
1061
  if (result?.applied === false && !this.pendingSync.has(uri)) {
889
1062
  // The push is addressed at the client's LAST DECLARED VERSION, so a
890
1063
  // rejection normally means the client's buffer moved while the
@@ -905,7 +1078,7 @@ export class ModelService<
905
1078
  // queued — best-effort, since a settle arriving later simply pushes
906
1079
  // after this and still wins.
907
1080
  uriLogger.warn(`Language client rejected applyEdit at its declared version — re-pushing a full replace`);
908
- result = await this.services.workspace.TextDocuments.applyEditToLanguageClient(uri, text, { label: 'Update Model' });
1081
+ result = await this.services.workspace.TextDocuments.applyEditToLanguageClient(uri, text, { label: this.editLabel() });
909
1082
  if (result?.applied === false) {
910
1083
  uriLogger.warn(`Language client rejected the full-replace retry too — client content is stale`);
911
1084
  }
@@ -1013,7 +1186,47 @@ export class ModelService<
1013
1186
  return model;
1014
1187
  }
1015
1188
  const rewritten = await this.rewriteModel(uri, model, cancelToken);
1016
- return this.serialize(uri, rewritten);
1189
+ const target = UriUtils.toUri(uri);
1190
+ const trivia = this.services.ServiceRegistry?.getServices(target)?.trivia?.TriviaService;
1191
+ // Extracted BEFORE serializing, so it reads the document the write is
1192
+ // about to replace rather than whatever a concurrent build left behind.
1193
+ let document = this.services.workspace.LangiumDocuments.getDocument(target);
1194
+ if (trivia !== undefined && document === undefined) {
1195
+ const source = await this.textToTakeTriviaFrom(uri, target);
1196
+ if (source !== undefined) {
1197
+ document = this.services.workspace.LangiumDocumentFactory.fromString(source, target);
1198
+ }
1199
+ }
1200
+ const extracted = trivia !== undefined && document !== undefined ? trivia.extract(document) : undefined;
1201
+ const serialized = await this.serialize(uri, rewritten);
1202
+ return extracted === undefined ? serialized : trivia!.apply(serialized, extracted, target);
1203
+ }
1204
+
1205
+ /**
1206
+ * The text a write into an unloaded document should take its trivia from —
1207
+ * the open editor's if one holds it, the file's otherwise, and `undefined`
1208
+ * when neither can supply it.
1209
+ *
1210
+ * **A read that fails answers `undefined` rather than throwing.** `getDocument`
1211
+ * is a store read, so an ordinary create answers nothing here and must still
1212
+ * write; letting a permission error or a path that turned into a directory
1213
+ * escape would fail the user's write instead, having found nothing to preserve
1214
+ * in a file the write is about to replace anyway. Losing the comments is
1215
+ * recoverable, and traced; losing the write is neither.
1216
+ */
1217
+ protected async textToTakeTriviaFrom(uri: string, target: URI): Promise<string | undefined> {
1218
+ const open = this.services.workspace.TextDocuments.get(uri)?.getText();
1219
+ if (open !== undefined) {
1220
+ return open;
1221
+ }
1222
+ const fileSystem = this.services.workspace.FileSystemProvider;
1223
+ try {
1224
+ return (await fileSystem.exists(target)) ? await fileSystem.readFile(target) : undefined;
1225
+ } catch (err: unknown) {
1226
+ const detail = err instanceof Error ? err.message : String(err);
1227
+ this.tracer.withUri(uri).debug(`Could not read the file to take trivia from; writing as emitted. ${detail}`);
1228
+ return undefined;
1229
+ }
1017
1230
  }
1018
1231
 
1019
1232
  /**
@@ -1048,4 +1261,23 @@ export class ModelService<
1048
1261
  ? AstDocument.from<TAst, TDiagnostic>(document)
1049
1262
  : AstDocument.create<TAst, TDiagnostic>(uri.toString(), 0, undefined as unknown as TAst, []);
1050
1263
  }
1264
+
1265
+ /**
1266
+ * The same envelope with no diagnostics, for a read that names a phase below
1267
+ * `Validated`.
1268
+ *
1269
+ * Langium fills `LangiumDocument.diagnostics` from inside `validateDocument`
1270
+ * and from nowhere else, so below that phase the array holds whatever an
1271
+ * EARLIER build left — a verdict about text the document may no longer have.
1272
+ * The wait underneath resolves at or above the phase asked for, so a document
1273
+ * something else carried past `Validated` would otherwise hand a full array
1274
+ * back from `parsed()`.
1275
+ *
1276
+ * A copy rather than a clear: the envelope is freshly built here, but
1277
+ * {@link toAstDocument} is overridable and an adopter's version may return
1278
+ * one it also keeps.
1279
+ */
1280
+ protected withoutDiagnostics(document: AstDocument<TAst, TDiagnostic>): AstDocument<TAst, never> {
1281
+ return { ...document, diagnostics: [] };
1282
+ }
1051
1283
  }