@hydranium/core 1.0.0-next.14 → 1.0.0-next.140

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 (484) hide show
  1. package/README.md +5 -1
  2. package/lib/documents/ast-document-manager.d.ts +187 -27
  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 +386 -57
  23. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  24. package/lib/documents/hydranium-text-documents.js +697 -113
  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 +220 -11
  70. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  71. package/lib/langium/document-builder/document-builder.js +409 -27
  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 +240 -174
  113. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  114. package/lib/langium/model-service/model-service.js +207 -314
  115. package/lib/langium/model-service/model-service.js.map +1 -1
  116. package/lib/langium/module.d.ts +102 -18
  117. package/lib/langium/module.d.ts.map +1 -1
  118. package/lib/langium/module.js +30 -12
  119. package/lib/langium/module.js.map +1 -1
  120. package/lib/langium/naming/name-provider.d.ts +25 -4
  121. package/lib/langium/naming/name-provider.d.ts.map +1 -1
  122. package/lib/langium/naming/name-provider.js +2 -1
  123. package/lib/langium/naming/name-provider.js.map +1 -1
  124. package/lib/langium/naming/name-separator-validation.d.ts +23 -0
  125. package/lib/langium/naming/name-separator-validation.d.ts.map +1 -1
  126. package/lib/langium/naming/name-separator-validation.js +30 -1
  127. package/lib/langium/naming/name-separator-validation.js.map +1 -1
  128. package/lib/langium/residency/cst-residency-service.d.ts +31 -11
  129. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  130. package/lib/langium/residency/cst-residency-service.js +13 -56
  131. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  132. package/lib/langium/scope/hydranium-scope-provider.d.ts +48 -13
  133. package/lib/langium/scope/hydranium-scope-provider.d.ts.map +1 -1
  134. package/lib/langium/scope/hydranium-scope-provider.js +56 -20
  135. package/lib/langium/scope/hydranium-scope-provider.js.map +1 -1
  136. package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
  137. package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
  138. package/lib/langium/serialization/abstract-serializer.js +12 -21
  139. package/lib/langium/serialization/abstract-serializer.js.map +1 -1
  140. package/lib/langium/service-registry.d.ts +15 -1
  141. package/lib/langium/service-registry.d.ts.map +1 -1
  142. package/lib/langium/service-registry.js +16 -0
  143. package/lib/langium/service-registry.js.map +1 -1
  144. package/lib/langium/shared-services.d.ts +15 -2
  145. package/lib/langium/shared-services.d.ts.map +1 -1
  146. package/lib/langium/shared-services.js.map +1 -1
  147. package/lib/langium/transfer/transfer-encoder.d.ts +60 -17
  148. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  149. package/lib/langium/transfer/transfer-encoder.js +38 -16
  150. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  151. package/lib/langium/trivia/comment-preserver.d.ts +423 -0
  152. package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
  153. package/lib/langium/trivia/comment-preserver.js +906 -0
  154. package/lib/langium/trivia/comment-preserver.js.map +1 -0
  155. package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
  156. package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
  157. package/lib/langium/trivia/document-ending-preserver.js +48 -0
  158. package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
  159. package/lib/langium/trivia/index.d.ts +14 -0
  160. package/lib/langium/trivia/index.d.ts.map +1 -0
  161. package/lib/langium/trivia/index.js +14 -0
  162. package/lib/langium/trivia/index.js.map +1 -0
  163. package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
  164. package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
  165. package/lib/langium/trivia/trivia-contribution.js +10 -0
  166. package/lib/langium/trivia/trivia-contribution.js.map +1 -0
  167. package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
  168. package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
  169. package/lib/langium/trivia/trivia-preserver.js +10 -0
  170. package/lib/langium/trivia/trivia-preserver.js.map +1 -0
  171. package/lib/langium/trivia/trivia-service.d.ts +70 -0
  172. package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
  173. package/lib/langium/trivia/trivia-service.js +56 -0
  174. package/lib/langium/trivia/trivia-service.js.map +1 -0
  175. package/lib/langium/update-rewrite/update-rewrite.d.ts +1 -1
  176. package/lib/langium/validation/document-validator.d.ts +294 -10
  177. package/lib/langium/validation/document-validator.d.ts.map +1 -1
  178. package/lib/langium/validation/document-validator.js +448 -5
  179. package/lib/langium/validation/document-validator.js.map +1 -1
  180. package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
  181. package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
  182. package/lib/langium/workspace/document-uri-policy.js +3 -4
  183. package/lib/langium/workspace/document-uri-policy.js.map +1 -1
  184. package/lib/langium/workspace/file-system-provider.d.ts +9 -1
  185. package/lib/langium/workspace/file-system-provider.d.ts.map +1 -1
  186. package/lib/langium/workspace/file-system-provider.js +40 -7
  187. package/lib/langium/workspace/file-system-provider.js.map +1 -1
  188. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts +25 -0
  189. package/lib/langium/workspace/hydranium-langium-document-factory.d.ts.map +1 -1
  190. package/lib/langium/workspace/hydranium-langium-document-factory.js +25 -0
  191. package/lib/langium/workspace/hydranium-langium-document-factory.js.map +1 -1
  192. package/lib/langium/workspace/hydranium-workspace-manager.d.ts +21 -1
  193. package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
  194. package/lib/langium/workspace/hydranium-workspace-manager.js +28 -0
  195. package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
  196. package/lib/langium/workspace/in-memory-file-system-provider.d.ts +23 -0
  197. package/lib/langium/workspace/in-memory-file-system-provider.d.ts.map +1 -1
  198. package/lib/langium/workspace/in-memory-file-system-provider.js +55 -9
  199. package/lib/langium/workspace/in-memory-file-system-provider.js.map +1 -1
  200. package/lib/langium/workspace/initialize-workspace.d.ts +31 -2
  201. package/lib/langium/workspace/initialize-workspace.d.ts.map +1 -1
  202. package/lib/langium/workspace/initialize-workspace.js +13 -5
  203. package/lib/langium/workspace/initialize-workspace.js.map +1 -1
  204. package/lib/langium/workspace/langium-documents.d.ts +79 -17
  205. package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
  206. package/lib/langium/workspace/langium-documents.js +83 -28
  207. package/lib/langium/workspace/langium-documents.js.map +1 -1
  208. package/lib/langium/workspace/virtual-document.d.ts +18 -3
  209. package/lib/langium/workspace/virtual-document.d.ts.map +1 -1
  210. package/lib/langium/workspace/virtual-document.js +19 -2
  211. package/lib/langium/workspace/virtual-document.js.map +1 -1
  212. package/lib/langium/workspace/write-lock-scope.d.ts +4 -3
  213. package/lib/langium/workspace/write-lock-scope.d.ts.map +1 -1
  214. package/lib/langium/workspace/write-lock-scope.js +4 -3
  215. package/lib/langium/workspace/write-lock-scope.js.map +1 -1
  216. package/lib/locale/index.d.ts +10 -0
  217. package/lib/locale/index.d.ts.map +1 -0
  218. package/lib/locale/index.js +10 -0
  219. package/lib/locale/index.js.map +1 -0
  220. package/lib/locale/server-locale.d.ts +73 -0
  221. package/lib/locale/server-locale.d.ts.map +1 -0
  222. package/lib/locale/server-locale.js +61 -0
  223. package/lib/locale/server-locale.js.map +1 -0
  224. package/lib/lsp/completion/hydranium-completion-provider.d.ts +44 -2
  225. package/lib/lsp/completion/hydranium-completion-provider.d.ts.map +1 -1
  226. package/lib/lsp/completion/hydranium-completion-provider.js +68 -0
  227. package/lib/lsp/completion/hydranium-completion-provider.js.map +1 -1
  228. package/lib/lsp/connection-features.d.ts +23 -0
  229. package/lib/lsp/connection-features.d.ts.map +1 -0
  230. package/lib/lsp/connection-features.js +60 -0
  231. package/lib/lsp/connection-features.js.map +1 -0
  232. package/lib/lsp/diagnostics-connection.d.ts +27 -0
  233. package/lib/lsp/diagnostics-connection.d.ts.map +1 -0
  234. package/lib/lsp/diagnostics-connection.js +53 -0
  235. package/lib/lsp/diagnostics-connection.js.map +1 -0
  236. package/lib/lsp/hydranium-document-update-handler.d.ts +70 -62
  237. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  238. package/lib/lsp/hydranium-document-update-handler.js +129 -75
  239. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  240. package/lib/lsp/index.d.ts +3 -1
  241. package/lib/lsp/index.d.ts.map +1 -1
  242. package/lib/lsp/index.js +3 -1
  243. package/lib/lsp/index.js.map +1 -1
  244. package/lib/lsp/lsp-latency.d.ts +39 -0
  245. package/lib/lsp/lsp-latency.d.ts.map +1 -0
  246. package/lib/lsp/lsp-latency.js +58 -0
  247. package/lib/lsp/lsp-latency.js.map +1 -0
  248. package/lib/lsp/semantic-token-provider.d.ts +34 -1
  249. package/lib/lsp/semantic-token-provider.d.ts.map +1 -1
  250. package/lib/lsp/semantic-token-provider.js +55 -8
  251. package/lib/lsp/semantic-token-provider.js.map +1 -1
  252. package/lib/lsp/shared-module.d.ts +4 -8
  253. package/lib/lsp/shared-module.d.ts.map +1 -1
  254. package/lib/lsp/shared-module.js.map +1 -1
  255. package/lib/lsp/start-language-server.d.ts +8 -7
  256. package/lib/lsp/start-language-server.d.ts.map +1 -1
  257. package/lib/lsp/start-language-server.js +13 -7
  258. package/lib/lsp/start-language-server.js.map +1 -1
  259. package/lib/messages/carriers.d.ts +54 -0
  260. package/lib/messages/carriers.d.ts.map +1 -0
  261. package/lib/messages/carriers.js +62 -0
  262. package/lib/messages/carriers.js.map +1 -0
  263. package/lib/messages/index.d.ts +25 -0
  264. package/lib/messages/index.d.ts.map +1 -0
  265. package/lib/messages/index.js +25 -0
  266. package/lib/messages/index.js.map +1 -0
  267. package/lib/messages/renderer.d.ts +142 -0
  268. package/lib/messages/renderer.d.ts.map +1 -0
  269. package/lib/messages/renderer.js +165 -0
  270. package/lib/messages/renderer.js.map +1 -0
  271. package/lib/node/heap-ceiling.d.ts +61 -0
  272. package/lib/node/heap-ceiling.d.ts.map +1 -0
  273. package/lib/node/heap-ceiling.js +72 -0
  274. package/lib/node/heap-ceiling.js.map +1 -0
  275. package/lib/node/index.d.ts +1 -0
  276. package/lib/node/index.d.ts.map +1 -1
  277. package/lib/node/index.js +1 -0
  278. package/lib/node/index.js.map +1 -1
  279. package/lib/node/latency-from-env.d.ts +1 -1
  280. package/lib/node/latency-from-env.js +1 -1
  281. package/lib/node/measure-memory.d.ts.map +1 -1
  282. package/lib/node/measure-memory.js +1 -0
  283. package/lib/node/measure-memory.js.map +1 -1
  284. package/lib/node/node-file-system-provider.d.ts +35 -1
  285. package/lib/node/node-file-system-provider.d.ts.map +1 -1
  286. package/lib/node/node-file-system-provider.js +140 -45
  287. package/lib/node/node-file-system-provider.js.map +1 -1
  288. package/lib/node/profile-capture.d.ts +8 -2
  289. package/lib/node/profile-capture.d.ts.map +1 -1
  290. package/lib/node/profile-capture.js.map +1 -1
  291. package/lib/node/profiling-run.d.ts +2 -2
  292. package/lib/node/profiling-run.js +1 -1
  293. package/lib/node/profiling-run.js.map +1 -1
  294. package/lib/node/rename-over-open-readers.d.ts +52 -0
  295. package/lib/node/rename-over-open-readers.d.ts.map +1 -0
  296. package/lib/node/rename-over-open-readers.js +66 -0
  297. package/lib/node/rename-over-open-readers.js.map +1 -0
  298. package/lib/node/socket-launcher.d.ts +16 -1
  299. package/lib/node/socket-launcher.d.ts.map +1 -1
  300. package/lib/node/socket-launcher.js +4 -3
  301. package/lib/node/socket-launcher.js.map +1 -1
  302. package/lib/node/stdio-launcher.d.ts +1 -1
  303. package/lib/node/stdio-launcher.d.ts.map +1 -1
  304. package/lib/node/stdio-launcher.js +1 -1
  305. package/lib/node/stdio-launcher.js.map +1 -1
  306. package/lib/testing/fake-document.d.ts +16 -4
  307. package/lib/testing/fake-document.d.ts.map +1 -1
  308. package/lib/testing/fake-document.js +18 -3
  309. package/lib/testing/fake-document.js.map +1 -1
  310. package/lib/testing/make-noop-shared-services.d.ts +14 -0
  311. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  312. package/lib/testing/make-noop-shared-services.js +28 -2
  313. package/lib/testing/make-noop-shared-services.js.map +1 -1
  314. package/lib/testing/make-test-services.d.ts +62 -14
  315. package/lib/testing/make-test-services.d.ts.map +1 -1
  316. package/lib/testing/make-test-services.js +25 -5
  317. package/lib/testing/make-test-services.js.map +1 -1
  318. package/lib/testing/node/index.d.ts +1 -0
  319. package/lib/testing/node/index.d.ts.map +1 -1
  320. package/lib/testing/node/index.js +1 -0
  321. package/lib/testing/node/index.js.map +1 -1
  322. package/lib/testing/node/lsp-harness.d.ts.map +1 -1
  323. package/lib/testing/node/lsp-harness.js +2 -0
  324. package/lib/testing/node/lsp-harness.js.map +1 -1
  325. package/lib/testing/node/lsp-server-connection.d.ts +11 -3
  326. package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
  327. package/lib/testing/node/lsp-server-connection.js +42 -9
  328. package/lib/testing/node/lsp-server-connection.js.map +1 -1
  329. package/lib/testing/node/scratch-workspace.d.ts.map +1 -1
  330. package/lib/testing/node/scratch-workspace.js +10 -5
  331. package/lib/testing/node/scratch-workspace.js.map +1 -1
  332. package/lib/testing/node/spawned-server.d.ts +14 -0
  333. package/lib/testing/node/spawned-server.d.ts.map +1 -1
  334. package/lib/testing/node/spawned-server.js +33 -1
  335. package/lib/testing/node/spawned-server.js.map +1 -1
  336. package/lib/testing/node/unhandled-rejections.d.ts +20 -0
  337. package/lib/testing/node/unhandled-rejections.d.ts.map +1 -0
  338. package/lib/testing/node/unhandled-rejections.js +32 -0
  339. package/lib/testing/node/unhandled-rejections.js.map +1 -0
  340. package/lib/testing/playwright/flaky-network-proxy.d.ts +76 -0
  341. package/lib/testing/playwright/flaky-network-proxy.d.ts.map +1 -0
  342. package/lib/testing/playwright/flaky-network-proxy.js +164 -0
  343. package/lib/testing/playwright/flaky-network-proxy.js.map +1 -0
  344. package/lib/testing/playwright/index.d.ts +1 -0
  345. package/lib/testing/playwright/index.d.ts.map +1 -1
  346. package/lib/testing/playwright/index.js +1 -0
  347. package/lib/testing/playwright/index.js.map +1 -1
  348. package/lib/testing/playwright/server-log-capture.d.ts +14 -4
  349. package/lib/testing/playwright/server-log-capture.d.ts.map +1 -1
  350. package/lib/testing/playwright/server-log-capture.js +21 -4
  351. package/lib/testing/playwright/server-log-capture.js.map +1 -1
  352. package/lib/testing/playwright/server-log-rename-reporter.d.ts.map +1 -1
  353. package/lib/testing/playwright/server-log-rename-reporter.js +10 -1
  354. package/lib/testing/playwright/server-log-rename-reporter.js.map +1 -1
  355. package/lib/testing/run-update-pipeline.d.ts +4 -4
  356. package/lib/testing/run-update-pipeline.d.ts.map +1 -1
  357. package/lib/testing/stub-ast-document-manager.d.ts +10 -9
  358. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  359. package/lib/testing/stub-ast-document-manager.js +17 -9
  360. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  361. package/lib/testing/stub-document-builder.d.ts +15 -6
  362. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  363. package/lib/testing/stub-document-builder.js +48 -3
  364. package/lib/testing/stub-document-builder.js.map +1 -1
  365. package/lib/testing/stub-hydranium-text-documents.d.ts +22 -7
  366. package/lib/testing/stub-hydranium-text-documents.d.ts.map +1 -1
  367. package/lib/testing/stub-hydranium-text-documents.js +92 -15
  368. package/lib/testing/stub-hydranium-text-documents.js.map +1 -1
  369. package/lib/testing/stub-langium-documents.d.ts +3 -2
  370. package/lib/testing/stub-langium-documents.d.ts.map +1 -1
  371. package/lib/testing/stub-langium-documents.js.map +1 -1
  372. package/lib/testing/stub-model-service.d.ts +7 -6
  373. package/lib/testing/stub-model-service.d.ts.map +1 -1
  374. package/lib/testing/stub-model-service.js +4 -4
  375. package/lib/testing/stub-model-service.js.map +1 -1
  376. package/lib/util/connection-liveness.d.ts +7 -6
  377. package/lib/util/connection-liveness.d.ts.map +1 -1
  378. package/lib/util/connection-liveness.js +35 -14
  379. package/lib/util/connection-liveness.js.map +1 -1
  380. package/package.json +17 -8
  381. package/src/documents/ast-document-manager.ts +345 -62
  382. package/src/documents/client-ids.ts +5 -22
  383. package/src/documents/client-session-errors.ts +24 -0
  384. package/src/documents/client-session-registry.ts +240 -0
  385. package/src/documents/file-system-task-queue.ts +68 -0
  386. package/src/documents/hydranium-text-documents.ts +853 -142
  387. package/src/documents/index.ts +3 -0
  388. package/src/documents/language-client-text-shadow.ts +59 -11
  389. package/src/documents/self-save-registry.ts +52 -13
  390. package/src/index.ts +6 -0
  391. package/src/langium/ast-extension/ast-node-builder.ts +66 -22
  392. package/src/langium/bootstrap.ts +16 -9
  393. package/src/langium/config/configuration-provider.ts +119 -0
  394. package/src/langium/config/index.ts +1 -0
  395. package/src/langium/diagnostics/lsp-logger.ts +41 -6
  396. package/src/langium/document-builder/build-pipeline-integration.ts +50 -5
  397. package/src/langium/document-builder/build-session.ts +87 -0
  398. package/src/langium/document-builder/document-builder.ts +460 -26
  399. package/src/langium/document-builder/index.ts +1 -0
  400. package/src/langium/integration-services.ts +67 -6
  401. package/src/langium/integrity/integrity-rule.ts +13 -5
  402. package/src/langium/integrity/integrity-service.ts +202 -17
  403. package/src/langium/keys/containment.ts +87 -0
  404. package/src/langium/keys/index.ts +1 -0
  405. package/src/langium/keys/name-based-key-provider.ts +4 -0
  406. package/src/langium/language-module.ts +42 -4
  407. package/src/langium/model-service/client-session.ts +606 -0
  408. package/src/langium/model-service/index.ts +1 -0
  409. package/src/langium/model-service/model-service.ts +363 -312
  410. package/src/langium/module.ts +132 -24
  411. package/src/langium/naming/name-provider.ts +27 -5
  412. package/src/langium/naming/name-separator-validation.ts +34 -5
  413. package/src/langium/residency/cst-residency-service.ts +36 -14
  414. package/src/langium/scope/hydranium-scope-provider.ts +60 -22
  415. package/src/langium/serialization/abstract-serializer.ts +13 -32
  416. package/src/langium/service-registry.ts +19 -2
  417. package/src/langium/shared-services.ts +19 -2
  418. package/src/langium/transfer/transfer-encoder.ts +85 -24
  419. package/src/langium/trivia/comment-preserver.ts +1100 -0
  420. package/src/langium/trivia/document-ending-preserver.ts +55 -0
  421. package/src/langium/trivia/index.ts +14 -0
  422. package/src/langium/trivia/trivia-contribution.ts +39 -0
  423. package/src/langium/trivia/trivia-preserver.ts +54 -0
  424. package/src/langium/trivia/trivia-service.ts +99 -0
  425. package/src/langium/update-rewrite/update-rewrite.ts +1 -1
  426. package/src/langium/validation/document-validator.ts +537 -11
  427. package/src/langium/workspace/document-uri-policy.ts +3 -4
  428. package/src/langium/workspace/file-system-provider.ts +48 -8
  429. package/src/langium/workspace/hydranium-langium-document-factory.ts +34 -0
  430. package/src/langium/workspace/hydranium-workspace-manager.ts +30 -1
  431. package/src/langium/workspace/in-memory-file-system-provider.ts +62 -10
  432. package/src/langium/workspace/initialize-workspace.ts +43 -5
  433. package/src/langium/workspace/langium-documents.ts +106 -33
  434. package/src/langium/workspace/virtual-document.ts +21 -3
  435. package/src/langium/workspace/write-lock-scope.ts +4 -3
  436. package/src/locale/index.ts +10 -0
  437. package/src/locale/server-locale.ts +86 -0
  438. package/src/lsp/completion/hydranium-completion-provider.ts +82 -2
  439. package/src/lsp/connection-features.ts +67 -0
  440. package/src/lsp/diagnostics-connection.ts +56 -0
  441. package/src/lsp/hydranium-document-update-handler.ts +158 -79
  442. package/src/lsp/index.ts +3 -1
  443. package/src/lsp/lsp-latency.ts +62 -0
  444. package/src/lsp/semantic-token-provider.ts +74 -7
  445. package/src/lsp/shared-module.ts +4 -8
  446. package/src/lsp/start-language-server.ts +13 -7
  447. package/src/messages/carriers.ts +78 -0
  448. package/src/messages/index.ts +36 -0
  449. package/src/messages/renderer.ts +212 -0
  450. package/src/node/heap-ceiling.ts +108 -0
  451. package/src/node/index.ts +1 -0
  452. package/src/node/latency-from-env.ts +1 -1
  453. package/src/node/measure-memory.ts +1 -0
  454. package/src/node/node-file-system-provider.ts +154 -47
  455. package/src/node/profile-capture.ts +8 -2
  456. package/src/node/profiling-run.ts +3 -3
  457. package/src/node/rename-over-open-readers.ts +78 -0
  458. package/src/node/socket-launcher.ts +19 -4
  459. package/src/node/stdio-launcher.ts +1 -2
  460. package/src/testing/fake-document.ts +24 -5
  461. package/src/testing/make-noop-shared-services.ts +50 -2
  462. package/src/testing/make-test-services.ts +101 -26
  463. package/src/testing/node/index.ts +1 -0
  464. package/src/testing/node/lsp-harness.ts +2 -0
  465. package/src/testing/node/lsp-server-connection.ts +61 -11
  466. package/src/testing/node/scratch-workspace.ts +10 -5
  467. package/src/testing/node/spawned-server.ts +35 -1
  468. package/src/testing/node/unhandled-rejections.ts +31 -0
  469. package/src/testing/playwright/flaky-network-proxy.ts +240 -0
  470. package/src/testing/playwright/index.ts +1 -0
  471. package/src/testing/playwright/server-log-capture.ts +24 -5
  472. package/src/testing/playwright/server-log-rename-reporter.ts +12 -1
  473. package/src/testing/run-update-pipeline.ts +4 -4
  474. package/src/testing/stub-ast-document-manager.ts +28 -21
  475. package/src/testing/stub-document-builder.ts +68 -12
  476. package/src/testing/stub-hydranium-text-documents.ts +127 -22
  477. package/src/testing/stub-langium-documents.ts +3 -2
  478. package/src/testing/stub-model-service.ts +9 -8
  479. package/src/util/connection-liveness.ts +37 -14
  480. package/lib/lsp/instrument-connection.d.ts +0 -36
  481. package/lib/lsp/instrument-connection.d.ts.map +0 -1
  482. package/lib/lsp/instrument-connection.js +0 -62
  483. package/lib/lsp/instrument-connection.js.map +0 -1
  484. package/src/lsp/instrument-connection.ts +0 -66
@@ -7,9 +7,13 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
  import { ObservableValue } from '@hydranium/protocol';
10
- import { DefaultDocumentBuilder, DocumentState, OperationCancelled, UriUtils, interruptAndCheck, isOperationCancelled } from '@hydranium/langium';
11
- import { CancellationToken } from 'vscode-languageserver-protocol';
10
+ import { DefaultDocumentBuilder, DocumentState, MultiMap, OperationCancelled, UriUtils, interruptAndCheck, isOperationCancelled } from '@hydranium/langium';
11
+ // `Diagnostic` as a VALUE: `renderDiagnostics` needs its `getMessageString`
12
+ // namespace helper to read the `string | MarkupContent` union without
13
+ // restating it.
14
+ import { CancellationToken, Diagnostic, Disposable } from 'vscode-languageserver-protocol';
12
15
  import { CST_REHYDRATION_RESET_STATE, isCstShed } from '../residency/cst-residency-service.js';
16
+ import { BuildSession } from './build-session.js';
13
17
  import { labelPhaseListener } from './labeled-phase-listener.js';
14
18
  /** Document states a phase-reached line is emitted for by default — every built phase. */
15
19
  export const DEFAULT_LOGGED_PHASES = [
@@ -20,6 +24,19 @@ export const DEFAULT_LOGGED_PHASES = [
20
24
  DocumentState.IndexedReferences,
21
25
  DocumentState.Validated
22
26
  ];
27
+ /**
28
+ * The build reasons the framework stages through
29
+ * {@link HydraniumDocumentBuilder.markNextReason}: the LSP events the update
30
+ * handler dispatches on, and `didClose` for the revert the text store runs
31
+ * after a last close. Exposed so a subclass layering reasons of its own keeps
32
+ * the names the framework emits.
33
+ */
34
+ export const HYDRANIUM_BUILD_REASONS = Object.freeze({
35
+ didOpen: 'didOpen',
36
+ didChangeContent: 'didChangeContent',
37
+ didChangeWatchedFiles: 'didChangeWatchedFiles',
38
+ didClose: 'didClose'
39
+ });
23
40
  /**
24
41
  * Consecutive re-queued builds that leave a waited-on document at the same state
25
42
  * before {@link HydraniumDocumentBuilder.awaitDocumentState} stops re-queuing it.
@@ -32,7 +49,8 @@ const MAX_STALLED_REQUEUES = 3;
32
49
  * Extends Langium's {@link DefaultDocumentBuilder} with:
33
50
  *
34
51
  * - **Bug-fixes** (always on): an {@link awaitDocumentState} that waits where
35
- * the default rejects, a {@link prepareBuild} that keeps a cancelled
52
+ * the default rejects (re-queuing, under the workspace lock, a document no
53
+ * build will carry), a {@link prepareBuild} that keeps a cancelled
36
54
  * non-validating build from suppressing validation, and a
37
55
  * {@link shouldRelink} that never judges a document unaffected on an index
38
56
  * that does not describe it.
@@ -42,15 +60,24 @@ const MAX_STALLED_REQUEUES = 3;
42
60
  * - **In-place rebuild helpers** — {@link reparse} and
43
61
  * {@link reparseAndRelink} — for a build-phase listener that mutated a
44
62
  * document's AST and must reconcile it within the same build.
45
- * - **Diagnostic dedupe** at `Validated` ({@link dedupeDiagnostics}).
63
+ * - **Diagnostic dedupe** at `Validated` ({@link dedupeDiagnostics}), followed
64
+ * by the **one server-side message render** every head inherits
65
+ * ({@link renderDiagnostics}).
66
+ * - **Build sessions** ({@link BuildSession}): each `update` / `build` call is
67
+ * one correlated unit carrying an id, a trigger label, a start time and
68
+ * cancellation lineage, so every line of a rebuild reads as belonging to it
69
+ * and a preempted build is distinguishable from the winner.
46
70
  * - **Logging instrumentation** (default on, opt-out via `logLevel: 'off'`):
47
- * phase-reached lines, slow-listener breakdowns on `notifyDocumentPhase`,
48
- * and slow-build-phase totals on `notifyBuildPhase`.
71
+ * a per-build line, phase-reached lines, slow-listener breakdowns on
72
+ * `notifyDocumentPhase`, and slow-build-phase totals on `notifyBuildPhase`.
49
73
  *
50
74
  * Adopters extend this class — the configuration knobs cover what most
51
75
  * adopters need; the `format*Line` methods, `formatUri`, and
52
76
  * `collectDeletedURIs` are protected so subclasses can customise wording or
53
- * domain-aware cascades without re-implementing surrounding logic.
77
+ * domain-aware cascades without re-implementing surrounding logic. An adopter
78
+ * with build-scoped state of its own subclasses {@link BuildSession} and
79
+ * overrides {@link createBuildSession}, which puts that state under the same
80
+ * preemption-correct teardown rather than a reimplementation of it.
54
81
  */
55
82
  export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
56
83
  tracer;
@@ -64,8 +91,13 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
64
91
  slowListenerMs;
65
92
  /** Live slow-build-phase-total threshold; read `.value` per phase. */
66
93
  slowBuildMs;
94
+ /** Live phase-detail buffering threshold; read `.value` once per build, onto the session. */
95
+ phaseDetailMs;
67
96
  uriPolicy;
68
97
  clock;
98
+ messageRenderer;
99
+ /** The lock a re-queued build takes; see {@link requeueOrphaned}. */
100
+ workspaceLock;
69
101
  /** Narrower handle on the same registry as the inherited `serviceRegistry`, for {@link ExtendedServiceRegistry.registrations}. */
70
102
  languageRegistry;
71
103
  languageFileExtensions = [];
@@ -76,17 +108,38 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
76
108
  refreshCrossDocumentComputedScopes;
77
109
  /** LSP event name (e.g. `'didChangeWatchedFiles'`) staged for the next `update()` call. */
78
110
  pendingUpdateReason;
111
+ /**
112
+ * The build currently in progress, or `undefined` between builds.
113
+ *
114
+ * A subclass carrying its own build-scoped state returns a {@link BuildSession}
115
+ * subclass from {@link createBuildSession} and narrows this with a typeguard
116
+ * where it reads that state — rather than redeclaring the field, whose
117
+ * initialiser would run after `super()` and clear a session opened during
118
+ * construction.
119
+ */
120
+ activeSession;
121
+ /**
122
+ * `traceId` of the last build that ended in cancellation, for the successor's
123
+ * "cancels #N" tag. Held here rather than on a session because the session
124
+ * that carries it is already gone by the time its successor is opened.
125
+ */
126
+ lastCancelledTraceId;
127
+ /** Registered through {@link onDocumentPhaseDelivered}, by phase. */
128
+ documentPhaseDeliveredListeners = new MultiMap();
79
129
  constructor(services, options = {}) {
80
130
  super(services);
81
131
  this.languageRegistry = services.ServiceRegistry;
82
132
  this.uriPolicy = services.workspace.DocumentUriPolicy;
83
133
  this.clock = services.Clock;
134
+ this.messageRenderer = services.MessageRenderer;
135
+ this.workspaceLock = services.workspace.WorkspaceLock;
84
136
  this.tracer = services.Tracer.for(options.logName ?? 'DocumentBuilder').trace('instantiated');
85
137
  this.logLevel = options.logLevel ?? 'debug';
86
138
  this.loggedPhases = options.loggedPhases ?? DEFAULT_LOGGED_PHASES;
87
139
  this.slowPhaseMs = ObservableValue.from(options.slowPhaseMs ?? 25);
88
140
  this.slowListenerMs = ObservableValue.from(options.slowListenerMs ?? 5);
89
141
  this.slowBuildMs = ObservableValue.from(options.slowBuildMs ?? 25);
142
+ this.phaseDetailMs = ObservableValue.from(options.phaseDetailMs ?? 0);
90
143
  this.refreshCrossDocumentComputedScopes = options.refreshCrossDocumentComputedScopes ?? false;
91
144
  if (this.logLevel !== 'off') {
92
145
  this.registerPhaseListeners();
@@ -97,8 +150,8 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
97
150
  // ============================================================
98
151
  /**
99
152
  * Stage an LSP event name for the next `update()` call. Adopters call before
100
- * the update fires; `HydraniumDocumentUpdateHandler` does it for the
101
- * four LSP events.
153
+ * the update fires; `HydraniumDocumentUpdateHandler` does it for the LSP
154
+ * events, and the text store for the revert that follows a last close.
102
155
  *
103
156
  * The framework stages the value and never reads it back. The consumer is a
104
157
  * subclass overriding the build logging, which takes
@@ -109,6 +162,23 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
109
162
  markNextReason(reason) {
110
163
  this.pendingUpdateReason = reason;
111
164
  }
165
+ /**
166
+ * Call `listener` with a document once every {@link onDocumentPhase}
167
+ * listener of `state` ran for it and none was skipped by cancellation, and
168
+ * with the text version the phase listeners were called at. A write during
169
+ * them moves the document's own version on, past what they saw.
170
+ *
171
+ * Only here is it known what the phase listeners delivered. Langium sets the
172
+ * document's state before it notifies, and a cancel between two listeners
173
+ * skips the rest, leaving the document at the phase with those listeners
174
+ * never run and nothing to run them later. A build-phase listener comes too
175
+ * late: a cancel after this document but before the batch ends skips it,
176
+ * although every listener of this document ran.
177
+ */
178
+ onDocumentPhaseDelivered(state, listener) {
179
+ this.documentPhaseDeliveredListeners.add(state, listener);
180
+ return Disposable.create(() => this.documentPhaseDeliveredListeners.delete(state, listener));
181
+ }
112
182
  /**
113
183
  * Diagnostic snapshot for use in timeout/error messages around document
114
184
  * state.
@@ -126,7 +196,15 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
126
196
  const doc = this.langiumDocuments.getDocument(UriUtils.toUri(this.uriPolicy.canonicalUri(uri)));
127
197
  const docState = doc ? DocumentState[doc.state] : 'unknown (document not loaded)';
128
198
  const lastPhase = this.lastPhaseMs > 0 ? `${Math.round(performance.now() - this.lastPhaseMs)}ms ago` : 'no phase observed';
129
- return `current state: '${docState}', last phase: ${lastPhase}`;
199
+ return `current state: '${docState}', last phase: ${lastPhase}, active build: ${this.formatSession(this.activeSession)}`;
200
+ }
201
+ /** Render a session for a status line. `undefined` — no build in progress — reads as `none`. */
202
+ formatSession(session) {
203
+ if (!session) {
204
+ return 'none';
205
+ }
206
+ const id = session.traceId !== undefined ? `#${session.traceId}` : 'untimed';
207
+ return `${id} (${session.trigger}, ${Math.round(performance.now() - session.startMs)}ms in)`;
130
208
  }
131
209
  // ============================================================
132
210
  // Bug-fixes (always on)
@@ -152,13 +230,24 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
152
230
  * {@link MAX_STALLED_REQUEUES} builds that fail to advance the document, so a
153
231
  * document the builder will never carry to `state` degrades to a pending wait
154
232
  * plus a warning rather than an endless build loop.
233
+ *
234
+ * A wait for `Validated` on a document that a validating build skips
235
+ * resolves once that build has indexed its references, without diagnostics:
236
+ * see {@link skipsValidation}. Waiting on would never end, and re-queuing
237
+ * would skip it again.
238
+ *
239
+ * A re-queued build takes the {@link WorkspaceLock} (see
240
+ * {@link requeueOrphaned}), so a caller that awaits this wait while it holds
241
+ * the lock, inside a read or write action, deadlocks on a document that needs
242
+ * a re-queue: the build waits for that action to end. Await it outside the
243
+ * lock.
155
244
  */
156
245
  awaitDocumentState(state, uri, cancelToken) {
157
246
  const document = this.langiumDocuments.getDocument(uri);
158
247
  if (!document) {
159
248
  return super.awaitDocumentState(state, uri, cancelToken);
160
249
  }
161
- if (document.state >= state) {
250
+ if (document.state >= state || (state === DocumentState.Validated && this.skipsValidation(document))) {
162
251
  return Promise.resolve(uri);
163
252
  }
164
253
  return new Promise((resolve, reject) => {
@@ -194,8 +283,18 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
194
283
  resolve(doc.uri);
195
284
  }
196
285
  }, 'awaitDocumentState'));
286
+ // The phase after which a validating build either validates the
287
+ // document or has skipped it.
288
+ const skipDisposable = state === DocumentState.Validated
289
+ ? this.onDocumentPhase(DocumentState.IndexedReferences, labelPhaseListener((doc) => {
290
+ if (UriUtils.equals(doc.uri, uri) && this.skipsValidation(doc)) {
291
+ cleanup();
292
+ resolve(doc.uri);
293
+ }
294
+ }, 'awaitDocumentState.skipsValidation'))
295
+ : Disposable.create(() => undefined);
197
296
  const buildDisposable = this.onBuildPhase(DocumentState.Validated, () => {
198
- if (document.state >= state) {
297
+ if (document.state >= state || (state === DocumentState.Validated && this.skipsValidation(document))) {
199
298
  cleanup();
200
299
  resolve(uri);
201
300
  }
@@ -210,6 +309,7 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
210
309
  });
211
310
  const cleanup = () => {
212
311
  phaseDisposable.dispose();
312
+ skipDisposable.dispose();
213
313
  buildDisposable.dispose();
214
314
  cancelDisposable.dispose();
215
315
  };
@@ -222,6 +322,20 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
222
322
  }
223
323
  });
224
324
  }
325
+ /**
326
+ * Whether the build that carries or last carried `document` has indexed its
327
+ * references, asks for validation, and still skips it, which is an override
328
+ * of `shouldValidate` excluding it. A build that does not ask for
329
+ * validation, such as the workspace's initial one, is not a skip: the next
330
+ * validating build validates the document. Nor is a document below
331
+ * `IndexedReferences`: the build writes its options before the first phase,
332
+ * so the document would pass for skipped before it is even parsed.
333
+ */
334
+ skipsValidation(document) {
335
+ return (document.state >= DocumentState.IndexedReferences &&
336
+ Boolean(this.getBuildOptions(document).validation) &&
337
+ !this.shouldValidate(document));
338
+ }
225
339
  /**
226
340
  * Whether no build will advance `document` to `state`, so a wait on it can
227
341
  * only be resolved by starting one.
@@ -250,14 +364,33 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
250
364
  * notification, so awaiting here would invert the dependency. A rejection is
251
365
  * logged rather than swallowed — it leaves the waiter pending until its own
252
366
  * cancellation token fires, which is worth a line in the log.
367
+ *
368
+ * The build is an ordinary {@link WorkspaceLock} write, entered from a read.
369
+ * The read waits until no write runs or is queued, so the build neither runs
370
+ * beside a locked one nor cancels the build a re-queue is fired from. Unlocked,
371
+ * it runs beside a locked build that cannot cancel it: both validate the same
372
+ * version and deliver it twice. Taking the write directly, without the read,
373
+ * cancels the running build and skips the rest of its build-phase listeners.
374
+ * The read must not await the write, which queues behind that read.
375
+ *
376
+ * A write that ran while the read waited may have carried the document to
377
+ * `state` already, and a second build would deliver that version again; the
378
+ * read skips the write then.
253
379
  */
254
380
  requeueOrphaned(document, state, reason) {
255
381
  const tracer = this.tracer.withUri(document.uri.toString());
256
382
  tracer.info(`Re-queuing orphaned document (at '${DocumentState[document.state]}', needs '${DocumentState[state]}'): ${reason}`);
257
- this.update([document.uri], []).catch((err) => {
258
- if (!isOperationCancelled(err)) {
259
- tracer.error(`Re-queue build failed: ${err instanceof Error ? err.message : String(err)}`);
383
+ void this.workspaceLock.read(() => {
384
+ if (document.state >= state) {
385
+ return;
260
386
  }
387
+ this.workspaceLock
388
+ .write(token => this.update([document.uri], [], token))
389
+ .catch((err) => {
390
+ if (!isOperationCancelled(err)) {
391
+ tracer.error(`Re-queue build failed: ${err instanceof Error ? err.message : String(err)}`);
392
+ }
393
+ });
261
394
  });
262
395
  }
263
396
  /**
@@ -331,7 +464,22 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
331
464
  this.ensureLanguageFileExtensions();
332
465
  const changedURIs = changed.flatMap(uri => this.flattenAndAdaptURI(uri));
333
466
  const deletedURIs = deleted.flatMap(uri => this.collectDeletedURIs(uri));
334
- return super.update(changedURIs, deletedURIs, cancelToken);
467
+ return this.runInSession({
468
+ kind: 'update',
469
+ trigger: this.buildTriggerLabel(changedURIs, deletedURIs),
470
+ triggerCountsDocs: changedURIs.length + deletedURIs.length !== 1,
471
+ changed: changedURIs,
472
+ deleted: deletedURIs
473
+ }, this.rebuildLabel(changedURIs, deletedURIs), () => super.update(changedURIs, deletedURIs, cancelToken));
474
+ }
475
+ /**
476
+ * The workspace-initialization entry point, bracketed by a session like
477
+ * {@link update}. Langium's `update` reaches `buildDocuments` directly rather
478
+ * than through here, so the two never nest.
479
+ */
480
+ build(documents, options, cancelToken) {
481
+ const uris = documents.map(document => document.uri);
482
+ return this.runInSession({ kind: 'build', trigger: `${documents.length} docs`, triggerCountsDocs: true, changed: uris, deleted: [] }, `Build documents (${documents.length} docs)`, () => super.build(documents, options, cancelToken));
335
483
  }
336
484
  /**
337
485
  * Refresh cross-document `ComputedScopes` derivations on cascade, when
@@ -528,6 +676,139 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
528
676
  ];
529
677
  }
530
678
  // ============================================================
679
+ // Build sessions — one rebuild as a correlated unit
680
+ // ============================================================
681
+ /**
682
+ * Open a session, run `body` inside it, and close it — the bracket every
683
+ * line of a rebuild is emitted within.
684
+ *
685
+ * The session is installed **synchronously**, before the timed body runs, so
686
+ * that state a subclass computed in {@link createBuildSession} is already
687
+ * readable by the time Langium's `update` consults `shouldRelink`.
688
+ *
689
+ * Teardown is preemption-correct, which is the reason this is framework code
690
+ * rather than a recipe. A build outside the workspace lock overlaps another:
691
+ * the later one installs itself as {@link activeSession} while the earlier
692
+ * one is still running, and the earlier one's `finally` runs LAST. Clearing
693
+ * unconditionally there would discard the later build's state mid-build.
694
+ * Only the session that is still current clears — and the check is
695
+ * reference equality on the session object,
696
+ * not on {@link BuildSession.traceId}, which is `undefined` for every build
697
+ * whenever the timing level is suppressed and would compare equal to itself
698
+ * across two different builds.
699
+ *
700
+ * Builds outside the lock are not hypothetical even without an adopter: the
701
+ * model service with `serializeBuilds` off starts one. Two locked builds
702
+ * never overlap, since the lock starts a write only once the write it
703
+ * cancelled has unwound.
704
+ */
705
+ runInSession(context, label, body) {
706
+ // Read before installing the new session: the id being superseded belongs
707
+ // to the OUTGOING build, or — when the previous one already finished
708
+ // cancelled — to the id it parked for its successor.
709
+ const supersededId = this.activeSession?.traceId ?? this.lastCancelledTraceId;
710
+ this.lastCancelledTraceId = undefined;
711
+ const reason = this.pendingUpdateReason;
712
+ this.pendingUpdateReason = undefined;
713
+ const tags = [];
714
+ if (reason) {
715
+ tags.push(`event: ${reason}`);
716
+ }
717
+ if (supersededId !== undefined) {
718
+ tags.push(`cancels #${supersededId}`);
719
+ }
720
+ const session = this.createBuildSession(context);
721
+ this.activeSession = session;
722
+ // A phase's "since previous phase" must measure from the build's start,
723
+ // not from whenever the last build's final phase happened to land.
724
+ this.lastPhaseMs = session.startMs;
725
+ return this.tracer.time(label, async () => {
726
+ try {
727
+ await body();
728
+ }
729
+ catch (err) {
730
+ if (isOperationCancelled(err)) {
731
+ session.cancelled = true;
732
+ }
733
+ throw err;
734
+ }
735
+ finally {
736
+ this.endSession(session);
737
+ }
738
+ }, this.logLevel, {
739
+ logAfterMs: 0,
740
+ forceMemoryAboveMs: session.buffers ? session.detailThresholdMs : undefined,
741
+ captureId: id => {
742
+ session.traceId = id;
743
+ },
744
+ tags
745
+ });
746
+ }
747
+ /**
748
+ * Construct the session for one build. Override to return a
749
+ * {@link BuildSession} subclass carrying adopter build-scoped state — it is
750
+ * called before the build body, so anything derived here is readable
751
+ * throughout it.
752
+ */
753
+ createBuildSession(context) {
754
+ return new BuildSession(performance.now(), context.trigger, context.triggerCountsDocs, this.phaseDetailMs.value);
755
+ }
756
+ /**
757
+ * Close `session`: flush what it buffered, then release it if it is still the
758
+ * current one (see {@link runInSession} on why that check is conditional).
759
+ * The flush is unconditional — a preempted build's lines still describe work
760
+ * that happened.
761
+ */
762
+ endSession(session) {
763
+ this.flushSession(session);
764
+ if (this.activeSession === session) {
765
+ this.activeSession = undefined;
766
+ if (session.cancelled) {
767
+ this.lastCancelledTraceId = session.traceId;
768
+ }
769
+ }
770
+ }
771
+ /**
772
+ * Emit the lines `session` held back, if it ran long enough to be worth the
773
+ * detail; drop them otherwise. Emits through {@link emit} rather than
774
+ * {@link log}, which would route them straight back into the buffer.
775
+ */
776
+ flushSession(session) {
777
+ const elapsedMs = performance.now() - session.startMs;
778
+ if (elapsedMs >= session.detailThresholdMs) {
779
+ for (const line of session.bufferedLines) {
780
+ this.emit(line);
781
+ }
782
+ }
783
+ session.bufferedLines.length = 0;
784
+ }
785
+ /** Label for the build's own log line. Override to customise wording. */
786
+ rebuildLabel(changed, deleted) {
787
+ if (changed.length === 0 && deleted.length === 0) {
788
+ return 'Rebuild documents (nothing to do)';
789
+ }
790
+ if (changed.length === 1 && deleted.length === 0) {
791
+ return `Rebuild document: ${this.formatUri(changed[0])}`;
792
+ }
793
+ if (changed.length === 0 && deleted.length === 1) {
794
+ return `Rebuild after delete: ${this.formatUri(deleted[0])}`;
795
+ }
796
+ return `Rebuild documents (${changed.length} changed, ${deleted.length} deleted)`;
797
+ }
798
+ /** Short description of what triggered the build, repeated on every phase line. Override to customise wording. */
799
+ buildTriggerLabel(changed, deleted) {
800
+ if (changed.length === 0 && deleted.length === 0) {
801
+ return 'nothing';
802
+ }
803
+ if (changed.length === 1 && deleted.length === 0) {
804
+ return this.formatUri(changed[0]);
805
+ }
806
+ if (changed.length === 0 && deleted.length === 1) {
807
+ return `deleted ${this.formatUri(deleted[0])}`;
808
+ }
809
+ return `${changed.length} changed, ${deleted.length} deleted`;
810
+ }
811
+ // ============================================================
531
812
  // Logging — phase-reached listeners
532
813
  // ============================================================
533
814
  registerPhaseListeners() {
@@ -540,15 +821,37 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
540
821
  const now = performance.now();
541
822
  const elapsedMs = Math.round(now - this.lastPhaseMs);
542
823
  this.lastPhaseMs = now;
824
+ // Counted before the line is formatted, so the formatter stays a pure
825
+ // function of state a caller can also set up in a test.
826
+ if (this.activeSession) {
827
+ this.activeSession.phasesLogged++;
828
+ }
543
829
  this.log(this.phaseReachedLine(state, documents, elapsedMs));
544
830
  }
545
- /** Format the phase-reached log line. Override to customise wording. */
831
+ /**
832
+ * Format the phase-reached log line. Override to customise wording.
833
+ *
834
+ * Within a session the line names what triggered the build, so a phase read
835
+ * in isolation still says which rebuild it belongs to. `elapsedMs` is ignored
836
+ * for the FIRST phase of a session: it measures from the previous build's
837
+ * last phase, an idle gap that says nothing about this build.
838
+ */
546
839
  phaseReachedLine(state, documents, elapsedMs) {
547
- const docInfo = documents.length === 1 ? this.formatUri(documents[0].uri) : `${documents.length} docs`;
548
- return `Reached phase '${DocumentState[state]}' [${docInfo}, ${elapsedMs}ms since previous phase]`;
840
+ const session = this.activeSession;
841
+ let docInfo;
842
+ if (session) {
843
+ docInfo = session.triggerCountsDocs ? `building ${session.trigger}` : `building ${session.trigger}, ${documents.length} docs`;
844
+ }
845
+ else {
846
+ docInfo = documents.length === 1 ? this.formatUri(documents[0].uri) : `${documents.length} docs`;
847
+ }
848
+ const elapsedInfo = session && session.phasesLogged <= 1
849
+ ? `${Math.round(performance.now() - session.startMs)}ms since build start`
850
+ : `${elapsedMs}ms since previous phase`;
851
+ return `Reached phase '${DocumentState[state]}' [${docInfo}, ${elapsedInfo}]`;
549
852
  }
550
853
  // ============================================================
551
- // Logging — slow-listener breakdown on notifyDocumentPhase
854
+ // notifyDocumentPhase — the delivery hook and the slow-listener breakdown
552
855
  // ============================================================
553
856
  async notifyDocumentPhase(document, state, cancelToken) {
554
857
  // Before the listeners, one of which is Langium's diagnostics publisher.
@@ -558,16 +861,15 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
558
861
  // build settling inside that window therefore appends after this call has
559
862
  // already deduped, and the appended duplicate is published by the listener
560
863
  // of the build that deduped. Only `serializeBuilds` closes the window.
864
+ //
865
+ // Dedupe before rendering: rendering is deterministic, so it cannot
866
+ // change which entries are structurally equal, and fewer survive to render.
561
867
  if (state === DocumentState.Validated) {
562
868
  this.dedupeDiagnostics(document);
563
- }
564
- if (this.logLevel === 'off') {
565
- return super.notifyDocumentPhase(document, state, cancelToken);
869
+ this.renderDiagnostics(document);
566
870
  }
567
871
  const listeners = this.documentPhaseListeners.get(state).slice();
568
- if (listeners.length === 0) {
569
- return;
570
- }
872
+ const version = document.textDocument.version;
571
873
  const perListenerMs = [];
572
874
  let cancelledListeners = 0;
573
875
  const { elapsedMs: totalMs } = await this.clock.measure(async () => {
@@ -587,6 +889,14 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
587
889
  perListenerMs.push(elapsedMs);
588
890
  }
589
891
  });
892
+ if (cancelledListeners === 0) {
893
+ for (const delivered of this.documentPhaseDeliveredListeners.get(state).slice()) {
894
+ delivered(document, version);
895
+ }
896
+ }
897
+ if (this.logLevel === 'off' || listeners.length === 0) {
898
+ return;
899
+ }
590
900
  if (cancelledListeners > 0) {
591
901
  this.tracer
592
902
  .withUri(document.uri.toString())
@@ -718,11 +1028,83 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
718
1028
  document.diagnostics = unique;
719
1029
  }
720
1030
  }
1031
+ /**
1032
+ * Render every diagnostic on `document` through the bound message renderer,
1033
+ * in ONE pass over the finished list.
1034
+ *
1035
+ * All three heads read `document.diagnostics` — the LSP publish,
1036
+ * `TransferEncoder.toTransferDiagnostic` and the GLSP validation path — so
1037
+ * one pass here is what keeps the render from happening per head. It is also
1038
+ * the only placement that covers lexer and parser errors, which Langium
1039
+ * pushes onto the document without routing them through `toDiagnostic`.
1040
+ *
1041
+ * Running here rather than from a `Validated` phase listener needs no
1042
+ * ordering assumption: Langium publishes from `addDiagnosticsHandler`, a free
1043
+ * function it registers as such a listener, which can only be outrun.
1044
+ *
1045
+ * **It inherits {@link dedupeDiagnostics}'s window, and therefore the same
1046
+ * precondition.** A build settling inside the listener window appends
1047
+ * diagnostics this pass never saw, and the publisher of the build that
1048
+ * rendered sends them — unrendered. `ModelServiceOptions.serializeBuilds`
1049
+ * closes it and defaults to `true`, so "every diagnostic is rendered" holds
1050
+ * by default and is an opt-out rather than a guarantee. Opting out accepts
1051
+ * unrendered diagnostics on exactly the terms it already accepts duplicates.
1052
+ *
1053
+ * Entries are REPLACED rather than mutated: `sendDiagnostics` passes the
1054
+ * array by reference and serialises later, so an in-place message mutation
1055
+ * reaches the wire even when it runs after the publisher — which would make
1056
+ * a test for the ordering pass in either state.
1057
+ */
1058
+ renderDiagnostics(document) {
1059
+ const diagnostics = document.diagnostics;
1060
+ if (!diagnostics || diagnostics.length === 0) {
1061
+ return;
1062
+ }
1063
+ let changed = false;
1064
+ // No try/catch: `renderDiagnostic` carries a no-throw contract, because an
1065
+ // error escaping this phase strands the document at `Validated` with
1066
+ // Langium's publisher never invoked.
1067
+ const rendered = diagnostics.map(diagnostic => {
1068
+ const text = this.messageRenderer.renderDiagnostic(diagnostic);
1069
+ // Against the message's STRING FORM, not the field. `renderDiagnostic`
1070
+ // answers a `string` by contract, while `Diagnostic.message` is
1071
+ // `string | MarkupContent` since LSP 3.17 — so comparing the answer
1072
+ // against the field never matches for a markup message, and a pass
1073
+ // that replaced on mismatch flattened every un-identified markup
1074
+ // diagnostic to its own plain text. That is silent data loss on the
1075
+ // path whose whole job is to leave such entries alone.
1076
+ if (text === Diagnostic.getMessageString(diagnostic)) {
1077
+ return diagnostic;
1078
+ }
1079
+ changed = true;
1080
+ return { ...diagnostic, message: text };
1081
+ });
1082
+ if (changed) {
1083
+ document.diagnostics = rendered;
1084
+ }
1085
+ }
721
1086
  // ============================================================
722
1087
  // Internal helpers
723
1088
  // ============================================================
724
- /** Dispatch a log line at the configured log level; a no-op when `logLevel === 'off'`. */
1089
+ /**
1090
+ * Dispatch a log line at the configured log level; a no-op when `logLevel ===
1091
+ * 'off'`.
1092
+ *
1093
+ * Held on the active session when it buffers, so the "was this build worth a
1094
+ * per-phase breakdown" decision — which needs the build's total duration, and
1095
+ * so cannot be taken by anything that runs while the lines are produced — is
1096
+ * deferred to {@link flushSession}.
1097
+ */
725
1098
  log(message) {
1099
+ const session = this.activeSession;
1100
+ if (session?.buffers) {
1101
+ session.bufferedLines.push(message);
1102
+ return;
1103
+ }
1104
+ this.emit(message);
1105
+ }
1106
+ /** Write a line out, bypassing session buffering. The single sink every framework log line reaches. */
1107
+ emit(message) {
726
1108
  this.tracer.logAt(this.logLevel, message);
727
1109
  }
728
1110
  }