@hydranium/core 1.0.0-next.36 → 1.0.0-next.38

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 (95) hide show
  1. package/lib/documents/ast-document-manager.d.ts +42 -5
  2. package/lib/documents/ast-document-manager.d.ts.map +1 -1
  3. package/lib/documents/ast-document-manager.js +3 -7
  4. package/lib/documents/ast-document-manager.js.map +1 -1
  5. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  6. package/lib/documents/hydranium-text-documents.js +29 -8
  7. package/lib/documents/hydranium-text-documents.js.map +1 -1
  8. package/lib/documents/language-client-text-shadow.d.ts +24 -1
  9. package/lib/documents/language-client-text-shadow.d.ts.map +1 -1
  10. package/lib/documents/language-client-text-shadow.js +36 -2
  11. package/lib/documents/language-client-text-shadow.js.map +1 -1
  12. package/lib/documents/self-save-registry.d.ts +37 -6
  13. package/lib/documents/self-save-registry.d.ts.map +1 -1
  14. package/lib/documents/self-save-registry.js +28 -15
  15. package/lib/documents/self-save-registry.js.map +1 -1
  16. package/lib/index.d.ts +1 -0
  17. package/lib/index.d.ts.map +1 -1
  18. package/lib/index.js +4 -0
  19. package/lib/index.js.map +1 -1
  20. package/lib/langium/bootstrap.js +8 -8
  21. package/lib/langium/bootstrap.js.map +1 -1
  22. package/lib/langium/document-builder/build-pipeline-integration.d.ts +13 -1
  23. package/lib/langium/document-builder/build-pipeline-integration.d.ts.map +1 -1
  24. package/lib/langium/document-builder/build-pipeline-integration.js +1 -1
  25. package/lib/langium/document-builder/build-pipeline-integration.js.map +1 -1
  26. package/lib/langium/document-builder/document-builder.d.ts +2 -2
  27. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  28. package/lib/langium/document-builder/document-builder.js.map +1 -1
  29. package/lib/langium/integration-services.d.ts +1 -1
  30. package/lib/langium/model-service/model-service.d.ts +64 -5
  31. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  32. package/lib/langium/model-service/model-service.js +5 -73
  33. package/lib/langium/model-service/model-service.js.map +1 -1
  34. package/lib/langium/module.d.ts +58 -16
  35. package/lib/langium/module.d.ts.map +1 -1
  36. package/lib/langium/module.js +16 -16
  37. package/lib/langium/module.js.map +1 -1
  38. package/lib/langium/residency/cst-residency-service.d.ts +30 -8
  39. package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
  40. package/lib/langium/residency/cst-residency-service.js +13 -56
  41. package/lib/langium/residency/cst-residency-service.js.map +1 -1
  42. package/lib/langium/shared-services.d.ts +3 -3
  43. package/lib/langium/shared-services.d.ts.map +1 -1
  44. package/lib/langium/transfer/transfer-encoder.d.ts +29 -1
  45. package/lib/langium/transfer/transfer-encoder.d.ts.map +1 -1
  46. package/lib/langium/transfer/transfer-encoder.js +1 -1
  47. package/lib/langium/transfer/transfer-encoder.js.map +1 -1
  48. package/lib/locale/server-locale.d.ts +15 -1
  49. package/lib/locale/server-locale.d.ts.map +1 -1
  50. package/lib/locale/server-locale.js +1 -1
  51. package/lib/locale/server-locale.js.map +1 -1
  52. package/lib/lsp/hydranium-document-update-handler.js +1 -1
  53. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  54. package/lib/messages/renderer.d.ts +23 -4
  55. package/lib/messages/renderer.d.ts.map +1 -1
  56. package/lib/messages/renderer.js +1 -1
  57. package/lib/messages/renderer.js.map +1 -1
  58. package/lib/testing/make-noop-shared-services.d.ts +4 -4
  59. package/lib/testing/make-noop-shared-services.d.ts.map +1 -1
  60. package/lib/testing/make-noop-shared-services.js +4 -4
  61. package/lib/testing/make-noop-shared-services.js.map +1 -1
  62. package/lib/testing/make-test-services.d.ts +9 -9
  63. package/lib/testing/make-test-services.d.ts.map +1 -1
  64. package/lib/testing/make-test-services.js +6 -6
  65. package/lib/testing/make-test-services.js.map +1 -1
  66. package/lib/testing/stub-ast-document-manager.d.ts +3 -4
  67. package/lib/testing/stub-ast-document-manager.d.ts.map +1 -1
  68. package/lib/testing/stub-ast-document-manager.js +2 -5
  69. package/lib/testing/stub-ast-document-manager.js.map +1 -1
  70. package/lib/testing/stub-model-service.d.ts +1 -1
  71. package/lib/testing/stub-model-service.d.ts.map +1 -1
  72. package/lib/testing/stub-model-service.js +2 -2
  73. package/lib/testing/stub-model-service.js.map +1 -1
  74. package/package.json +5 -5
  75. package/src/documents/ast-document-manager.ts +49 -9
  76. package/src/documents/hydranium-text-documents.ts +29 -8
  77. package/src/documents/language-client-text-shadow.ts +38 -2
  78. package/src/documents/self-save-registry.ts +52 -13
  79. package/src/index.ts +4 -0
  80. package/src/langium/bootstrap.ts +8 -8
  81. package/src/langium/document-builder/build-pipeline-integration.ts +14 -1
  82. package/src/langium/document-builder/document-builder.ts +2 -2
  83. package/src/langium/integration-services.ts +1 -1
  84. package/src/langium/model-service/model-service.ts +78 -7
  85. package/src/langium/module.ts +71 -24
  86. package/src/langium/residency/cst-residency-service.ts +35 -11
  87. package/src/langium/shared-services.ts +3 -3
  88. package/src/langium/transfer/transfer-encoder.ts +32 -1
  89. package/src/locale/server-locale.ts +16 -1
  90. package/src/lsp/hydranium-document-update-handler.ts +1 -1
  91. package/src/messages/renderer.ts +24 -4
  92. package/src/testing/make-noop-shared-services.ts +6 -6
  93. package/src/testing/make-test-services.ts +15 -15
  94. package/src/testing/stub-ast-document-manager.ts +5 -10
  95. package/src/testing/stub-model-service.ts +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"stub-ast-document-manager.d.ts","sourceRoot":"","sources":["../../src/testing/stub-ast-document-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,OAAO,EAAkC,MAAM,oBAAoB,CAAC;AAElF,OAAO,EAEJ,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EACjC,MAAM,sCAAsC,CAAC;AAE9C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,sBAAsB,CAAC,IAAI,SAAS,OAAO,EAAE,WAAW,GAAG,OAAO,CAAE,SAAQ,IAAI,CAC9F,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC,EACnC,MAAM,GACN,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,UAAU,GACV,QAAQ,GACR,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,gBAAgB,GAChB,UAAU,CACd;IACE,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,uBAAuB,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,MAAM,CAAC;IAEnF;;;;;;OAMG;IACH,mBAAmB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5C;AAED,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,OAAO,EAAE,WAAW,GAAG,OAAO,EACnF,aAAa,EAAE,0BAA0B,EACzC,UAAU,EAAE,IAAI,CAAC,0BAA0B,EAAE,WAAW,CAAC,EACzD,SAAS,CAAC,EAAE,oBAAoB,CAAC,IAAI,EAAE,WAAW,CAAC,GACnD,sBAAsB,CAAC,IAAI,EAAE,WAAW,CAAC,CAiH3C"}
1
+ {"version":3,"file":"stub-ast-document-manager.d.ts","sourceRoot":"","sources":["../../src/testing/stub-ast-document-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,OAAO,EAAkC,MAAM,oBAAoB,CAAC;AAElF,OAAO,EAEJ,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EACjC,MAAM,sCAAsC,CAAC;AAE9C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,sBAAsB,CAAC,IAAI,SAAS,OAAO,EAAE,WAAW,GAAG,OAAO,CAAE,SAAQ,IAAI,CAC9F,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC,EACnC,MAAM,GACN,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,UAAU,GACV,QAAQ,GACR,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,kBAAkB,CACtB;IACE,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,uBAAuB,CAAC,IAAI,EAAE,WAAW,CAAC,GAAG,MAAM,CAAC;IAEnF;;;;;;OAMG;IACH,mBAAmB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5C;AAED,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,OAAO,EAAE,WAAW,GAAG,OAAO,EACnF,aAAa,EAAE,0BAA0B,EACzC,UAAU,EAAE,IAAI,CAAC,0BAA0B,EAAE,WAAW,CAAC,EACzD,SAAS,CAAC,EAAE,oBAAoB,CAAC,IAAI,EAAE,WAAW,CAAC,GACnD,sBAAsB,CAAC,IAAI,EAAE,WAAW,CAAC,CA8G3C"}
@@ -114,11 +114,8 @@ export function makeStubAstDocumentManager(textDocuments, fileSystem, documents)
114
114
  getDocument(uri) {
115
115
  return documents?.getDocument(UriUtils.toUri(uri));
116
116
  },
117
- isDirectChange() {
118
- return notSupported('isDirectChange');
119
- },
120
- async readFile(uri) {
121
- return notSupported(`readFile(${uri})`);
117
+ isTriggeringEdit() {
118
+ return notSupported('isTriggeringEdit');
122
119
  }
123
120
  };
124
121
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stub-ast-document-manager.js","sourceRoot":"","sources":["../../src/testing/stub-ast-document-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stub-ast-document-manager.js","sourceRoot":"","sources":["../../src/testing/stub-ast-document-manager.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@
8
8
  ********************************************************************************/
9
9
  import type { TransferDiagnostic, TransferElement } from '@hydranium/protocol';
10
10
  import type { AstNode } from '@hydranium/langium';
11
- import { ModelService, type ModelServiceOptions } from '../langium/model-service/model-service.js';
11
+ import { type ModelService, type ModelServiceOptions } from '../langium/model-service/model-service.js';
12
12
  import type { ServerSharedServices } from '../langium/module.js';
13
13
  /** The type of the model-service test double produced by {@link makeStubModelService}. */
14
14
  export type StubModelService<TAst extends AstNode, TDiagnostic extends TransferDiagnostic = TransferDiagnostic, TTransfer extends TransferElement = TransferElement> = ModelService<TAst, TDiagnostic, TTransfer>;
@@ -1 +1 @@
1
- {"version":3,"file":"stub-model-service.d.ts","sourceRoot":"","sources":["../../src/testing/stub-model-service.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AACnG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AA2BjE,0FAA0F;AAC1F,MAAM,MAAM,gBAAgB,CACzB,IAAI,SAAS,OAAO,EACpB,WAAW,SAAS,kBAAkB,GAAG,kBAAkB,EAC3D,SAAS,SAAS,eAAe,GAAG,eAAe,IAClD,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;AAE/C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CACjC,IAAI,SAAS,OAAO,EACpB,WAAW,SAAS,kBAAkB,GAAG,kBAAkB,EAC3D,SAAS,SAAS,eAAe,GAAG,eAAe,EAEnD,QAAQ,EAAE,oBAAoB,EAC9B,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,KAAK,MAAM,EACnD,OAAO,CAAC,EAAE,mBAAmB,GAC7B,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,CAEhD"}
1
+ {"version":3,"file":"stub-model-service.d.ts","sourceRoot":"","sources":["../../src/testing/stub-model-service.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAuB,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAC7H,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AA2BjE,0FAA0F;AAC1F,MAAM,MAAM,gBAAgB,CACzB,IAAI,SAAS,OAAO,EACpB,WAAW,SAAS,kBAAkB,GAAG,kBAAkB,EAC3D,SAAS,SAAS,eAAe,GAAG,eAAe,IAClD,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;AAE/C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CACjC,IAAI,SAAS,OAAO,EACpB,WAAW,SAAS,kBAAkB,GAAG,kBAAkB,EAC3D,SAAS,SAAS,eAAe,GAAG,eAAe,EAEnD,QAAQ,EAAE,oBAAoB,EAC9B,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,KAAK,MAAM,EACnD,OAAO,CAAC,EAAE,mBAAmB,GAC7B,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,CAEhD"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
- import { ModelService } from '../langium/model-service/model-service.js';
9
+ import { DefaultModelService } from '../langium/model-service/model-service.js';
10
10
  /**
11
11
  * Implementation detail of {@link makeStubModelService}. A {@link ModelService}
12
12
  * subclass that takes `serialize` as a constructor callback — the test stub
@@ -14,7 +14,7 @@ import { ModelService } from '../langium/model-service/model-service.js';
14
14
  * path would fail. Construct it through the factory; the {@link StubModelService}
15
15
  * type alias is the public type name.
16
16
  */
17
- class StubModelServiceImpl extends ModelService {
17
+ class StubModelServiceImpl extends DefaultModelService {
18
18
  serializeFn;
19
19
  constructor(services, serializeFn, options) {
20
20
  super(services, options);
@@ -1 +1 @@
1
- {"version":3,"file":"stub-model-service.js","sourceRoot":"","sources":["../../src/testing/stub-model-service.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAIlF,OAAO,EAAE,YAAY,EAA4B,MAAM,2CAA2C,CAAC;AAGnG;;;;;;GAMG;AACH,MAAM,oBAIJ,SAAQ,YAA0C;IAG3B;IAFtB,YACG,QAA8B,EACX,WAAqD,EACxE,OAA6B;QAE7B,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAHN,gBAAW,GAAX,WAAW,CAA0C;IAI3E,CAAC;IAEkB,SAAS,CAAC,GAAW,EAAE,IAAe;QACtD,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;CACH;AASD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,oBAAoB,CAKjC,QAA8B,EAC9B,SAAmD,EACnD,OAA6B;IAE7B,OAAO,IAAI,oBAAoB,CAA+B,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC/F,CAAC"}
1
+ {"version":3,"file":"stub-model-service.js","sourceRoot":"","sources":["../../src/testing/stub-model-service.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAIlF,OAAO,EAAE,mBAAmB,EAA+C,MAAM,2CAA2C,CAAC;AAG7H;;;;;;GAMG;AACH,MAAM,oBAIJ,SAAQ,mBAAiD;IAGlC;IAFtB,YACG,QAA8B,EACX,WAAqD,EACxE,OAA6B;QAE7B,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAHN,gBAAW,GAAX,WAAW,CAA0C;IAI3E,CAAC;IAEkB,SAAS,CAAC,GAAW,EAAE,IAAe;QACtD,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;CACH;AASD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,oBAAoB,CAKjC,QAA8B,EAC9B,SAAmD,EACnD,OAA6B;IAE7B,OAAO,IAAI,oBAAoB,CAA+B,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAC/F,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydranium/core",
3
- "version": "1.0.0-next.36",
3
+ "version": "1.0.0-next.38",
4
4
  "description": "Foundational runtime of the hydranium framework: AST coordination layer (services, integrity, AST extension, serialization, multi-client documents) plus the LSP textual head at the /lsp subpath. Consumed by protocol-head packages (@hydranium/data-server, @hydranium/glsp-server).",
5
5
  "keywords": [
6
6
  "hydranium",
@@ -104,8 +104,8 @@
104
104
  "diff": "^5.2.0"
105
105
  },
106
106
  "devDependencies": {
107
- "@hydranium/langium": "1.0.0-next.36",
108
- "@hydranium/protocol": "1.0.0-next.36",
107
+ "@hydranium/langium": "1.0.0-next.38",
108
+ "@hydranium/protocol": "1.0.0-next.38",
109
109
  "@playwright/test": "^1.40.0",
110
110
  "@types/diff": "^5.2.0",
111
111
  "rimraf": "^5.0.0",
@@ -117,8 +117,8 @@
117
117
  "vscode-languageserver-types": "^3.17.5"
118
118
  },
119
119
  "peerDependencies": {
120
- "@hydranium/langium": "1.0.0-next.36",
121
- "@hydranium/protocol": "1.0.0-next.36",
120
+ "@hydranium/langium": "1.0.0-next.38",
121
+ "@hydranium/protocol": "1.0.0-next.38",
122
122
  "@playwright/test": "^1.40.0",
123
123
  "vscode-jsonrpc": "9.0.1",
124
124
  "vscode-languageserver": "~10.0.1",
@@ -166,6 +166,51 @@ export type AstDocumentSavedEvent<TAst extends AstNode, TDiagnostic> = TransferS
166
166
  /** Construction options for {@link AstDocumentManager}. */
167
167
  export type AstDocumentManagerOptions = LogNameOptions;
168
168
 
169
+ /**
170
+ * The document slot the model server and the integrity service talk to:
171
+ * open/close/update/save plus the AST-typed event streams, with the LSP
172
+ * plumbing hidden.
173
+ *
174
+ * Generic over `<TAst extends AstNode, TDiagnostic>` so each consumer projects
175
+ * its own AST root type and diagnostic shape into the emitted
176
+ * {@link AstDocument} envelopes.
177
+ *
178
+ * **URI contract.** Every URI-accepting member takes a URI in *any* spelling
179
+ * (canonical, symlinked, `..`/case-divergent) and canonicalizes internally
180
+ * through the {@link DocumentUriPolicy}, so a caller never has to canonicalize
181
+ * first and an implementation may not require it.
182
+ */
183
+ export interface AstDocumentManager<TAst extends AstNode, TDiagnostic = unknown> {
184
+ open(args: OpenModelArgs): Promise<Disposable>;
185
+ close(args: CloseModelArgs): Promise<void>;
186
+ isOpen(uri: string): boolean;
187
+
188
+ /** Apply `text` as `clientId`'s edit. Resolves to the resulting text-document version. */
189
+ update(uri: string, text: string, clientId: string): Promise<number>;
190
+ save(uri: string, clientId: string): Promise<void>;
191
+
192
+ onUpdate(uri: string, listener: (event: AstDocumentUpdatedEvent<TAst, TDiagnostic>) => void): Disposable;
193
+ onSave(uri: string, listener: (event: AstDocumentSavedEvent<TAst, TDiagnostic>) => void | Promise<void>): Disposable;
194
+ onClientClosed(uri: string, clientId: string, listener: () => void): Disposable;
195
+
196
+ getDocument(uri: string): LangiumDocument | undefined;
197
+
198
+ /** Client id that authored the document's current version, or `undefined` for a framework-internal build. */
199
+ getAuthor(document: LangiumDocument): string | undefined;
200
+
201
+ /**
202
+ * Whether `uri` is one of the URIs the most recent build request named, as
203
+ * opposed to a document that build swept in as a dependent. Deletions are
204
+ * excluded: they never reach this layer, since a deleted document is
205
+ * removed before the build set is computed.
206
+ *
207
+ * The answer refers to the latest request, which may still be building, and
208
+ * it is retained between builds — so outside a build it describes whichever
209
+ * one ran last.
210
+ */
211
+ isTriggeringEdit(uri: string): boolean;
212
+ }
213
+
169
214
  /**
170
215
  * Higher-level facade over {@link HydraniumTextDocuments} that speaks the
171
216
  * model-server protocol (open/close/update/save with the transfer-model event
@@ -186,7 +231,7 @@ export type AstDocumentManagerOptions = LogNameOptions;
186
231
  * resolution (the egress `applyEditToLanguageClient`) stay on
187
232
  * {@link HydraniumTextDocuments}, their owner.
188
233
  */
189
- export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = unknown> {
234
+ export class DefaultAstDocumentManager<TAst extends AstNode, TDiagnostic = unknown> implements AstDocumentManager<TAst, TDiagnostic> {
190
235
  protected lastUpdate?: UpdateInfo;
191
236
 
192
237
  protected readonly textDocuments: HydraniumTextDocuments<TextDocument>;
@@ -334,7 +379,7 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
334
379
  * concrete value (the `onUpdate` event's `sourceClientId`, the data-server's
335
380
  * `resolveSourceClientId`).
336
381
  */
337
- getAuthor(document: LangiumDocument<AstNode>): string | undefined {
382
+ getAuthor(document: LangiumDocument): string | undefined {
338
383
  return this.textDocuments.getAuthor(document.textDocument.uri, document.textDocument.version);
339
384
  }
340
385
 
@@ -414,8 +459,7 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
414
459
  return !!this.textDocuments.get(uri);
415
460
  }
416
461
 
417
- /** True if the URI was in the directly-changed set of the most recent build (not just affected). */
418
- isDirectChange(uri: string): boolean {
462
+ isTriggeringEdit(uri: string): boolean {
419
463
  const canonical = this.uriPolicy.canonicalUri(uri);
420
464
  return this.lastUpdate?.changed.some(changed => this.uriPolicy.canonicalUri(changed) === canonical) ?? false;
421
465
  }
@@ -437,11 +481,7 @@ export class AstDocumentManager<TAst extends AstNode = AstNode, TDiagnostic = un
437
481
  version = 0,
438
482
  text?: string
439
483
  ): Promise<TextDocumentItem> {
440
- return { uri, languageId, version, text: text ?? (await this.readFile(uri)) };
441
- }
442
-
443
- async readFile(uri: string): Promise<string> {
444
- return this.fileSystemProvider.readFile(UriUtils.toUri(uri));
484
+ return { uri, languageId, version, text: text ?? (await this.fileSystemProvider.readFile(UriUtils.toUri(uri))) };
445
485
  }
446
486
 
447
487
  /**
@@ -644,18 +644,20 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
644
644
  if (this.isOpenInClient(uri, clientId)) {
645
645
  // Already open for this client under this canonical identity. If this is a
646
646
  // NEW client-facing URI for the same file (a second tab reached via a
647
- // divergent path, e.g. a symlink and its real path), record it and baseline
648
- // its shadow so outbound edits reach this tab too — but do NOT re-fire
649
- // open/rebuild; the document is already live.
647
+ // divergent path, e.g. a symlink and its real path), record it so outbound
648
+ // edits reach this tab too — but do NOT re-fire open/rebuild; the document
649
+ // is already live.
650
650
  if (clientId === LANGUAGE_CLIENT_ID) {
651
651
  const clientFacing = this.toLanguageClientUri(td.uri);
652
652
  const record = this.__documents.get(uri);
653
653
  if (record && !record.languageClientUris?.has(clientFacing)) {
654
654
  (record.languageClientUris ??= new Set<LanguageClientUri>()).add(clientFacing);
655
- const open = this.__syncedDocuments.get(uri);
656
- if (open) {
657
- this.__shadow.set(clientFacing, open.getText());
658
- }
655
+ // This tab's own buffer, NOT the synced text: a second tab is a second
656
+ // client model, read from disk, so a server-authored write already
657
+ // applied to the first tab leaves it BEHIND the synced document. Keying
658
+ // a diff to the synced text here addresses lines this tab does not have
659
+ // and drops content it does.
660
+ this.__shadow.setOpenedText(clientFacing, td.text);
659
661
  }
660
662
  }
661
663
  return;
@@ -694,7 +696,18 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
694
696
  // Baseline the shadow to what Monaco just opened so the next outbound
695
697
  // applyEditToLanguageClient diffs against the right starting point. Keyed by the
696
698
  // language-client URI (what Monaco holds), not the canonical document key.
697
- this.__shadow.set(this.toLanguageClientUri(td.uri), document.getText());
699
+ //
700
+ // The CLIENT's declared text, never the synced document: staged content
701
+ // consumed above leaves the two different, and a baseline asserting the
702
+ // client already holds the staged text suppresses the one sync that would
703
+ // deliver it. Skipped entirely when a push is already outstanding for this
704
+ // URI — an `applyEdit` to a closed file has the client open from disk and
705
+ // apply afterwards, so that shadow records what it is about to hold and
706
+ // disk text would key the next diff to a buffer nobody has.
707
+ const clientFacing = this.toLanguageClientUri(td.uri);
708
+ if (this.__shadow.get(clientFacing) === undefined) {
709
+ this.__shadow.set(clientFacing, td.text);
710
+ }
698
711
  }
699
712
  const toFire = Object.freeze({ document, clientId });
700
713
  this.__onDidOpen.fire(toFire);
@@ -706,6 +719,14 @@ export class HydraniumTextDocuments<T extends TextDocument = TextDocument> exten
706
719
  `Attach client: ${clientId} joined existing document (version ${document.version}, ` +
707
720
  `now open in: ${[...existingClients, clientId].join(', ')})`
708
721
  );
722
+ if (clientId === LANGUAGE_CLIENT_ID) {
723
+ // Monaco's own buffer, which on this path is NOT the synced text — another
724
+ // client opened the document and may already have changed it. Recorded as
725
+ // an equality-only baseline (never a diff one, see `setOpenedText`) so the
726
+ // refresh below does not push a full replace of content Monaco already
727
+ // holds, which would dirty the file on open.
728
+ this.__shadow.setOpenedText(this.toLanguageClientUri(td.uri), td.text);
729
+ }
709
730
  this.refreshContent(uri, clientId);
710
731
  }
711
732
  }
@@ -57,12 +57,23 @@ export function isFullReplace(edits: readonly TextEdit[]): boolean {
57
57
  * - {@link computeEdits} after each outgoing server→client sync (updates the shadow optimistically).
58
58
  * - {@link set} when the server receives a `didChange` from the language client (the client now
59
59
  * holds that text; we shouldn't resend it).
60
+ * - {@link setOpenedText} when the client opens a document the server did not baseline for it —
61
+ * an equality-only baseline, since that text is a snapshot rather than a tracked one.
60
62
  * - {@link invalidate} when the client rejects an edit (`applied=false`) or a doc is closed —
61
63
  * the shadow is now stale and the next sync must fall back to a full replace.
62
64
  */
63
65
  export class LanguageClientTextShadow {
64
66
  protected shadow = new Map<string, string>();
65
67
 
68
+ /**
69
+ * Per URI, the text the client declared it opened the document with, held
70
+ * only until a real baseline exists. Consulted by {@link computeEdits} when
71
+ * there is no shadow, and dropped on {@link invalidate}: a snapshot that
72
+ * outlived what the client holds would let the equality check skip an edit
73
+ * the client still needs.
74
+ */
75
+ protected opened = new Map<string, string>();
76
+
66
77
  /**
67
78
  * @param onFallback invoked when {@link computeEdits} falls back to a full-range replace due to
68
79
  * apply-verify mismatch. Useful for logging regressions.
@@ -84,6 +95,20 @@ export class LanguageClientTextShadow {
84
95
  this.shadow.set(uri, text);
85
96
  }
86
97
 
98
+ /**
99
+ * Record the buffer the language client declared at `didOpen`, for a URI
100
+ * with no baseline yet.
101
+ *
102
+ * Deliberately NOT a {@link set}: this text is only ever compared for
103
+ * EQUALITY, never diffed against. A line-keyed diff is position-dependent,
104
+ * so keying one to a `didOpen` snapshot the client may have moved past
105
+ * splices its buffer — the failure {@link computeEdits}'s full-replace
106
+ * fallback exists to avoid.
107
+ */
108
+ setOpenedText(uri: string, text: string): void {
109
+ this.opened.set(uri, text);
110
+ }
111
+
87
112
  /**
88
113
  * The text the language client is believed to hold, or `undefined` when
89
114
  * there is no baseline (first sync, or after an {@link invalidate}).
@@ -102,6 +127,11 @@ export class LanguageClientTextShadow {
102
127
  /** Forget the shadow for a URI; next {@link computeEdits} falls back to a full-document replace. */
103
128
  invalidate(uri: string): void {
104
129
  this.shadow.delete(uri);
130
+ // Drops the opened snapshot too, and this is the only place that has to:
131
+ // `computeEdits` clears it whenever it consults it, so the sole way it can
132
+ // outlive what the client holds is surviving under a live shadow until that
133
+ // shadow is dropped here.
134
+ this.opened.delete(uri);
105
135
  }
106
136
 
107
137
  /**
@@ -109,7 +139,9 @@ export class LanguageClientTextShadow {
109
139
  * the tracked shadow to `newText`. Updates the shadow to `newText` so subsequent calls diff
110
140
  * against it.
111
141
  *
112
- * - Empty array when the shadow already equals `newText` (caller should skip the RPC).
142
+ * - Empty array when the shadow already equals `newText` (caller should skip the RPC),
143
+ * or when there is no shadow and the client opened the document with exactly `newText`
144
+ * (see {@link setOpenedText}) — pushing it anyway dirties the client's buffer on open.
113
145
  * - Full-range replace when there is no shadow (first sync or post-{@link invalidate}).
114
146
  * - Line-level diff otherwise. Before returning, we locally reconstruct `newText` by applying
115
147
  * the diff to `old`; if that doesn't match, we fall back to a full-range replace and notify
@@ -124,7 +156,11 @@ export class LanguageClientTextShadow {
124
156
  this.shadow.set(uri, newText);
125
157
  const fullReplace: TextEdit = { range: FULL_RANGE, newText };
126
158
  if (old === undefined) {
127
- return [fullReplace];
159
+ // The opened snapshot has served its purpose either way: the shadow above
160
+ // is now the baseline, so leaving it would only risk a later stale match.
161
+ const openedText = this.opened.get(uri);
162
+ this.opened.delete(uri);
163
+ return openedText === newText ? [] : [fullReplace];
128
164
  }
129
165
  const edits = diffToEdits(old, newText);
130
166
  const verifyDoc = this.documents.create(uri, 'plaintext', 0, old);
@@ -22,10 +22,32 @@ export interface SelfSaveRegistryOptions {
22
22
  /**
23
23
  * Tracks `mtime` of files written by the server itself so the echo
24
24
  * `didChangeWatchedFiles` event from the OS file watcher can be suppressed,
25
- * avoiding redundant rebuilds after each save. Entries are matched by mtime
26
- * (not by a time window) and expire after a TTL.
25
+ * avoiding redundant rebuilds after each save.
27
26
  */
28
- export class SelfSaveRegistry {
27
+ export interface SelfSaveRegistry {
28
+ /**
29
+ * Record a self-write, and drop any registration already past its TTL.
30
+ *
31
+ * Keyed by `fsPath` rather than URI string, because the two sides of a
32
+ * suppression see different URI forms for one file.
33
+ */
34
+ register(fsPath: string, mtimeMs: number): void;
35
+
36
+ /**
37
+ * Whether `fsPath` carries a live registration at exactly `mtimeMs`.
38
+ *
39
+ * A registration expires after the TTL, so a write recorded longer ago than
40
+ * that answers `false` — the caller's decision holds only while the echo it
41
+ * suppresses could still be in flight. A query never mutates the registry.
42
+ */
43
+ isRegistered(fsPath: string, mtimeMs: number): boolean;
44
+ }
45
+
46
+ /**
47
+ * Compares entries by mtime rather than by a time window, so the echo of a
48
+ * save whose content is unchanged is still recognised.
49
+ */
50
+ export class DefaultSelfSaveRegistry implements SelfSaveRegistry {
29
51
  protected readonly entries = new Map<string, { mtimeMs: number; registeredAt: number }>();
30
52
  /**
31
53
  * Read time through the shared {@link Clock} rather than `Date.now()`: the
@@ -59,23 +81,40 @@ export class SelfSaveRegistry {
59
81
  return isCaseInsensitiveFileSystem() ? fsPath.toLowerCase() : fsPath;
60
82
  }
61
83
 
62
- /** Record a self-write. Key by `fsPath` (not URI string) to avoid URI-form mismatches. */
63
84
  register(fsPath: string, mtimeMs: number): void {
85
+ this.evictExpired();
64
86
  this.entries.set(this.key(fsPath), { mtimeMs, registeredAt: this.clock.now() });
65
87
  }
66
88
 
67
- /** True if `mtimeMs` matches a recent self-write. Evicts stale entries. Not consumed on match. */
68
- matches(fsPath: string, mtimeMs: number): boolean {
69
- const key = this.key(fsPath);
70
- const entry = this.entries.get(key);
71
- if (!entry) {
72
- return false;
73
- }
74
- if (this.clock.now() - entry.registeredAt > this.ttlMs) {
75
- this.entries.delete(key);
89
+ isRegistered(fsPath: string, mtimeMs: number): boolean {
90
+ const entry = this.entries.get(this.key(fsPath));
91
+ if (!entry || this.isExpired(entry.registeredAt)) {
76
92
  return false;
77
93
  }
78
94
  // Accept mtime equality (some filesystems have 1-second precision).
79
95
  return entry.mtimeMs === mtimeMs;
80
96
  }
97
+
98
+ /**
99
+ * Drop every expired registration.
100
+ *
101
+ * On the WRITE path, so the query stays free of side effects. Evicting from
102
+ * the query instead reaches only the keys something happens to ask about,
103
+ * and a path written once and never queried again is then retained for the
104
+ * life of the process. Bounded by the paths self-written inside one TTL
105
+ * window, so the sweep is over a handful of entries and sits behind a file
106
+ * write either way.
107
+ */
108
+ protected evictExpired(): void {
109
+ for (const [key, entry] of this.entries) {
110
+ if (this.isExpired(entry.registeredAt)) {
111
+ this.entries.delete(key);
112
+ }
113
+ }
114
+ }
115
+
116
+ /** Strictly greater, so an entry aged exactly to the TTL is still eligible. */
117
+ protected isExpired(registeredAt: number): boolean {
118
+ return this.clock.now() - registeredAt > this.ttlMs;
119
+ }
81
120
  }
package/src/index.ts CHANGED
@@ -36,5 +36,9 @@ export * from './langium/service-registry.js';
36
36
  export * from './langium/language-types.js';
37
37
  export * from './launcher/index.js';
38
38
  export * from './locale/index.js';
39
+ // The renderer only. The `./messages` subpath additionally enumerates this
40
+ // package's own declarations, which the root barrel already re-exports through
41
+ // the modules that raise them.
42
+ export * from './messages/renderer.js';
39
43
  export * from './documents/index.js';
40
44
  export * from './util/index.js';
@@ -12,15 +12,15 @@ import { type LangiumSharedCoreServices } from '@hydranium/langium';
12
12
  import { type LangiumServices, type LangiumSharedServices } from '@hydranium/langium/lsp';
13
13
  import type { ServerSharedServicesMinimal } from './shared-services.js';
14
14
  import { collectProducibleTypes } from './language-types.js';
15
- import { AstDocumentManager } from '../documents/ast-document-manager.js';
15
+ import { DefaultAstDocumentManager } from '../documents/ast-document-manager.js';
16
16
  import { HydraniumTextDocuments } from '../documents/hydranium-text-documents.js';
17
- import { BuildPipelineIntegration } from './document-builder/build-pipeline-integration.js';
17
+ import { DefaultBuildPipelineIntegration } from './document-builder/build-pipeline-integration.js';
18
18
  import { HydraniumDocumentBuilder } from './document-builder/document-builder.js';
19
- import { ModelService } from './model-service/model-service.js';
19
+ import { DefaultModelService } from './model-service/model-service.js';
20
20
  import { HydraniumScopeComputation } from './scope/hydranium-scope-computation.js';
21
21
  import { HydraniumScopeProvider } from './scope/hydranium-scope-provider.js';
22
22
  import { processEnv } from '../util/environment.js';
23
- import { TransferEncoder } from './transfer/transfer-encoder.js';
23
+ import { DefaultTransferEncoder } from './transfer/transfer-encoder.js';
24
24
  import { HydraniumWorkspaceManager } from './workspace/hydranium-workspace-manager.js';
25
25
 
26
26
  /**
@@ -206,17 +206,17 @@ const STRICT_SHARED_SLOTS: readonly StrictSlot[] = [
206
206
  {
207
207
  side: 'shared',
208
208
  path: 'workspace.AstDocumentManager',
209
- base: AstDocumentManager,
209
+ base: DefaultAstDocumentManager,
210
210
  get: s => nested(s, 'workspace', 'AstDocumentManager')
211
211
  },
212
212
  {
213
213
  side: 'shared',
214
214
  path: 'workspace.BuildPipelineIntegration',
215
- base: BuildPipelineIntegration,
215
+ base: DefaultBuildPipelineIntegration,
216
216
  get: s => nested(s, 'workspace', 'BuildPipelineIntegration')
217
217
  },
218
- { side: 'shared', path: 'model.TransferEncoder', base: TransferEncoder, get: s => nested(s, 'model', 'TransferEncoder') },
219
- { side: 'shared', path: 'model.ModelService', base: ModelService, get: s => nested(s, 'model', 'ModelService') }
218
+ { side: 'shared', path: 'model.TransferEncoder', base: DefaultTransferEncoder, get: s => nested(s, 'model', 'TransferEncoder') },
219
+ { side: 'shared', path: 'model.ModelService', base: DefaultModelService, get: s => nested(s, 'model', 'ModelService') }
220
220
  ];
221
221
 
222
222
  const STRICT_LANGUAGE_SLOTS: readonly StrictSlot[] = [
@@ -65,6 +65,19 @@ export interface BuildPipelineIntegrationOptions {
65
65
  readonly integrityPriority?: number;
66
66
  }
67
67
 
68
+ /**
69
+ * The build-pipeline slot. Empty because nothing calls this service — it wires
70
+ * itself in its constructor and every seam it offers is `protected`.
71
+ *
72
+ * Typing the slot by the class instead would stop an adopter's subclass
73
+ * satisfying it, since a class carries its `protected` members into the check
74
+ * and they compare by declaration rather than by shape. The cost of the empty
75
+ * shape is that any non-null value satisfies it, so a mis-bound slot compiles
76
+ * and the framework's phase listeners are then never attached.
77
+ */
78
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- the slot has no public member to name
79
+ export interface BuildPipelineIntegration {}
80
+
68
81
  /**
69
82
  * Wires the framework's build-time features (integrity rules, AST
70
83
  * enrichment) into Langium's document-build pipeline. The single shared
@@ -90,7 +103,7 @@ export interface BuildPipelineIntegrationOptions {
90
103
  * Eagerly constructed (via `DEFAULT_EAGER_SERVICES`) so the listeners are
91
104
  * attached before the first build cycle.
92
105
  */
93
- export class BuildPipelineIntegration {
106
+ export class DefaultBuildPipelineIntegration implements BuildPipelineIntegration {
94
107
  constructor(
95
108
  protected readonly services: ServerSharedServices,
96
109
  options: BuildPipelineIntegrationOptions = {}
@@ -26,7 +26,7 @@ import {
26
26
  // restating it.
27
27
  import { CancellationToken, Diagnostic } from 'vscode-languageserver-protocol';
28
28
  import { type LogNameOptions } from '../diagnostics/logger.js';
29
- import type { ServerMessageRenderer } from '../../messages/renderer.js';
29
+ import type { MessageRenderer } from '../../messages/renderer.js';
30
30
  import { CST_REHYDRATION_RESET_STATE, isCstShed } from '../residency/cst-residency-service.js';
31
31
  import { type ExtendedServiceRegistry } from '../service-registry.js';
32
32
  import { type ServerSharedServicesMinimal } from '../shared-services.js';
@@ -158,7 +158,7 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
158
158
  protected readonly phaseDetailMs: ObservableValue<number>;
159
159
  protected readonly uriPolicy: DocumentUriPolicy;
160
160
  protected readonly clock: Clock;
161
- protected readonly messageRenderer: ServerMessageRenderer;
161
+ protected readonly messageRenderer: MessageRenderer;
162
162
  /** Narrower handle on the same registry as the inherited `serviceRegistry`, for {@link ExtendedServiceRegistry.registrations}. */
163
163
  protected readonly languageRegistry: ExtendedServiceRegistry;
164
164
  protected languageFileExtensions: string[] = [];
@@ -104,7 +104,7 @@ export interface IntegrationServicesOptions<
104
104
  *
105
105
  * Both differences are the point, and neither is served by
106
106
  * {@link extra}. Most framework shared services are constructed with
107
- * no options (`ModelService: services => new ModelService(services)`),
107
+ * no options (`ModelService: services => new DefaultModelService(services)`),
108
108
  * so rebinding the slot is the only way to boot one configured
109
109
  * differently; a caller that needs that is usually a test, and the slot
110
110
  * it needs is as often a framework ADDITION (`Clock`, `Tracer`,