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

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 (38) hide show
  1. package/lib/documents/hydranium-text-documents.d.ts.map +1 -1
  2. package/lib/documents/hydranium-text-documents.js +3 -13
  3. package/lib/documents/hydranium-text-documents.js.map +1 -1
  4. package/lib/langium/document-builder/document-builder.d.ts +140 -13
  5. package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
  6. package/lib/langium/document-builder/document-builder.js +218 -24
  7. package/lib/langium/document-builder/document-builder.js.map +1 -1
  8. package/lib/langium/model-service/model-service.d.ts +25 -28
  9. package/lib/langium/model-service/model-service.d.ts.map +1 -1
  10. package/lib/langium/model-service/model-service.js +25 -30
  11. package/lib/langium/model-service/model-service.js.map +1 -1
  12. package/lib/langium/module.d.ts +5 -6
  13. package/lib/langium/module.d.ts.map +1 -1
  14. package/lib/langium/module.js.map +1 -1
  15. package/lib/langium/workspace/hydranium-workspace-lock.d.ts +13 -0
  16. package/lib/langium/workspace/hydranium-workspace-lock.d.ts.map +1 -1
  17. package/lib/langium/workspace/hydranium-workspace-lock.js +18 -0
  18. package/lib/langium/workspace/hydranium-workspace-lock.js.map +1 -1
  19. package/lib/lsp/hydranium-document-update-handler.d.ts +24 -21
  20. package/lib/lsp/hydranium-document-update-handler.d.ts.map +1 -1
  21. package/lib/lsp/hydranium-document-update-handler.js +27 -28
  22. package/lib/lsp/hydranium-document-update-handler.js.map +1 -1
  23. package/lib/testing/make-test-services.d.ts.map +1 -1
  24. package/lib/testing/make-test-services.js +3 -2
  25. package/lib/testing/make-test-services.js.map +1 -1
  26. package/lib/testing/stub-document-builder.d.ts +12 -6
  27. package/lib/testing/stub-document-builder.d.ts.map +1 -1
  28. package/lib/testing/stub-document-builder.js +9 -1
  29. package/lib/testing/stub-document-builder.js.map +1 -1
  30. package/package.json +5 -5
  31. package/src/documents/hydranium-text-documents.ts +3 -12
  32. package/src/langium/document-builder/document-builder.ts +253 -28
  33. package/src/langium/model-service/model-service.ts +34 -41
  34. package/src/langium/module.ts +5 -6
  35. package/src/langium/workspace/hydranium-workspace-lock.ts +21 -0
  36. package/src/lsp/hydranium-document-update-handler.ts +28 -31
  37. package/src/testing/make-test-services.ts +3 -2
  38. package/src/testing/stub-document-builder.ts +16 -6
@@ -1 +1 @@
1
- {"version":3,"file":"make-test-services.js","sourceRoot":"","sources":["../../src/testing/make-test-services.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"make-test-services.js","sourceRoot":"","sources":["../../src/testing/make-test-services.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
- import { type DocumentBuilder, type DocumentState, type LangiumDocument, type URI } from '@hydranium/langium';
9
+ import { type DocumentBuilder, type DocumentState, type LangiumDocument, type URI, type WorkspaceLock } from '@hydranium/langium';
10
10
  import { type CancellationToken } from 'vscode-languageserver';
11
11
  import { type HydraniumDocumentBuilder } from '../langium/document-builder/document-builder.js';
12
12
  /** Recorded call to a stubbed {@link DocumentBuilder} method. */
@@ -53,13 +53,14 @@ export interface StubWaitUntilGate {
53
53
  * Picks the methods the framework reads from {@link DocumentBuilder} on
54
54
  * the slot path — the compiler enforces those signatures stay aligned.
55
55
  * `update`, `onDocumentPhase`, `onUpdate`, and `waitUntil` carry stubbed
56
- * implementations; `build`, `onBuildPhase`, and `resetToState` are
57
- * implemented as loud-failing `notSupported` throwers so production code
58
- * that goes through the cast at the bind site (i.e. accesses methods the
56
+ * implementations, and `scheduleUpdate` calls `update` in one lock write per
57
+ * call (see {@link makeStubDocumentBuilder}); `build`, `onBuildPhase`, and
58
+ * `resetToState` are implemented as loud-failing `notSupported` throwers
59
+ * so production code that goes through the cast at the bind site (i.e. accesses methods the
59
60
  * stub claims but doesn't meaningfully implement) fails with a clear
60
61
  * message instead of "undefined is not a function".
61
62
  */
62
- export interface StubDocumentBuilder extends Pick<DocumentBuilder, 'update' | 'onDocumentPhase' | 'onUpdate' | 'build' | 'onBuildPhase' | 'resetToState' | 'updateBuildOptions'>, Pick<HydraniumDocumentBuilder, 'onDocumentPhaseDelivered'> {
63
+ export interface StubDocumentBuilder extends Pick<DocumentBuilder, 'update' | 'onDocumentPhase' | 'onUpdate' | 'build' | 'onBuildPhase' | 'resetToState' | 'updateBuildOptions'>, Pick<HydraniumDocumentBuilder, 'onDocumentPhaseDelivered' | 'scheduleUpdate'> {
63
64
  /**
64
65
  * Single-overload stub of {@link DocumentBuilder.waitUntil}. Real has
65
66
  * two overloads (`(state, cancelToken?): Promise<void>` and
@@ -103,6 +104,11 @@ export interface StubDocumentBuilder extends Pick<DocumentBuilder, 'update' | 'o
103
104
  * want to observe rebuilt state should `set` the new root on the document
104
105
  * registry and then call `firePhase` to trigger the framework's post-build
105
106
  * read path.
107
+ *
108
+ * `workspaceLock` is the lock `scheduleUpdate` writes under, without the real
109
+ * builder's sharing: pass the services tree's own, so a caller's lock ordering
110
+ * and reentrancy behave as against the real builder. Without one,
111
+ * `scheduleUpdate` records the update unlocked.
106
112
  */
107
- export declare function makeStubDocumentBuilder(): StubDocumentBuilder;
113
+ export declare function makeStubDocumentBuilder(workspaceLock?: WorkspaceLock): StubDocumentBuilder;
108
114
  //# sourceMappingURL=stub-document-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stub-document-builder.d.ts","sourceRoot":"","sources":["../../src/testing/stub-document-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,KAAK,eAAe,EAGpB,KAAK,aAAa,EAElB,KAAK,eAAe,EACpB,KAAK,GAAG,EACV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,iDAAiD,CAAC;AAEhG,iEAAiE;AACjE,MAAM,WAAW,mBAAmB,CAAC,KAAK,SAAS,OAAO,EAAE;IACzD,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAC/B;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI,CAAC;IAChB,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,mBACd,SACG,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,iBAAiB,GAAG,UAAU,GAAG,OAAO,GAAG,cAAc,GAAG,cAAc,GAAG,oBAAoB,CAAC,EACnI,IAAI,CAAC,wBAAwB,EAAE,0BAA0B,CAAC;IAC7D;;;;;;;OAOG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC;IACrE,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACzE,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC,aAAa,EAAE,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC9F;;;;;;OAMG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClG,+DAA+D;IAC/D,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACnD;;;;;;OAMG;IACH,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtG;;;OAGG;IACH,iBAAiB,IAAI,iBAAiB,CAAC;IACvC,2EAA2E;IAC3E,KAAK,IAAI,IAAI,CAAC;CAChB;AAqBD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,IAAI,mBAAmB,CA4J7D"}
1
+ {"version":3,"file":"stub-document-builder.d.ts","sourceRoot":"","sources":["../../src/testing/stub-document-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EACJ,KAAK,eAAe,EAGpB,KAAK,aAAa,EAElB,KAAK,eAAe,EACpB,KAAK,GAAG,EACR,KAAK,aAAa,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,iDAAiD,CAAC;AAEhG,iEAAiE;AACjE,MAAM,WAAW,mBAAmB,CAAC,KAAK,SAAS,OAAO,EAAE;IACzD,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAC/B;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI,CAAC;IAChB,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,mBACd,SACG,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,iBAAiB,GAAG,UAAU,GAAG,OAAO,GAAG,cAAc,GAAG,cAAc,GAAG,oBAAoB,CAAC,EACnI,IAAI,CAAC,wBAAwB,EAAE,0BAA0B,GAAG,gBAAgB,CAAC;IAChF;;;;;;;OAOG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC;IACrE,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IACzE,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC,aAAa,EAAE,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAC9F;;;;;;OAMG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClG,+DAA+D;IAC/D,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACnD;;;;;;OAMG;IACH,cAAc,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,WAAW,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtG;;;OAGG;IACH,iBAAiB,IAAI,iBAAiB,CAAC;IACvC,2EAA2E;IAC3E,KAAK,IAAI,IAAI,CAAC;CAChB;AAqBD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,aAAa,CAAC,EAAE,aAAa,GAAG,mBAAmB,CA+J1F"}
@@ -31,8 +31,13 @@ function reraise(result) {
31
31
  * want to observe rebuilt state should `set` the new root on the document
32
32
  * registry and then call `firePhase` to trigger the framework's post-build
33
33
  * read path.
34
+ *
35
+ * `workspaceLock` is the lock `scheduleUpdate` writes under, without the real
36
+ * builder's sharing: pass the services tree's own, so a caller's lock ordering
37
+ * and reentrancy behave as against the real builder. Without one,
38
+ * `scheduleUpdate` records the update unlocked.
34
39
  */
35
- export function makeStubDocumentBuilder() {
40
+ export function makeStubDocumentBuilder(workspaceLock) {
36
41
  const phaseListeners = new Map();
37
42
  const deliveredListeners = new Map();
38
43
  const buildPhaseListeners = new Map();
@@ -56,6 +61,9 @@ export function makeStubDocumentBuilder() {
56
61
  async update(changed, deleted) {
57
62
  updateCalls.push({ args: [changed, deleted] });
58
63
  },
64
+ scheduleUpdate(changed, deleted) {
65
+ return workspaceLock ? workspaceLock.write(() => stub.update(changed, deleted)) : stub.update(changed, deleted);
66
+ },
59
67
  async waitUntil(state, uri) {
60
68
  waitUntilCalls.push({ args: [state, uri] });
61
69
  const gate = gates.shift();
@@ -1 +1 @@
1
- {"version":3,"file":"stub-document-builder.js","sourceRoot":"","sources":["../../src/testing/stub-document-builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stub-document-builder.js","sourceRoot":"","sources":["../../src/testing/stub-document-builder.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydranium/core",
3
- "version": "1.0.0-next.140",
3
+ "version": "1.0.0-next.148",
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.140",
108
- "@hydranium/protocol": "1.0.0-next.140",
107
+ "@hydranium/langium": "1.0.0-next.148",
108
+ "@hydranium/protocol": "1.0.0-next.148",
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.140",
121
- "@hydranium/protocol": "1.0.0-next.140",
120
+ "@hydranium/langium": "1.0.0-next.148",
121
+ "@hydranium/protocol": "1.0.0-next.148",
122
122
  "@playwright/test": "^1.40.0",
123
123
  "vscode-jsonrpc": "9.0.1",
124
124
  "vscode-languageserver": "~10.0.1",
@@ -50,6 +50,7 @@ import {
50
50
  type CanonicalUri,
51
51
  type LanguageClientUri,
52
52
  asLanguageClientUri,
53
+ cyrb53,
53
54
  DisposableCollection,
54
55
  type Stopwatch,
55
56
  type Tracer
@@ -318,18 +319,8 @@ function isFileNotFound(err: unknown, target: URI): boolean {
318
319
  * `node:crypto` (this module must stay runnable in browser hosts).
319
320
  */
320
321
  function contentHash(text: string): string {
321
- let h1 = 0xdeadbeef;
322
- let h2 = 0x41c6ce57;
323
- for (let index = 0; index < text.length; index++) {
324
- const code = text.charCodeAt(index);
325
- h1 = Math.imul(h1 ^ code, 2654435761);
326
- h2 = Math.imul(h2 ^ code, 1597334677);
327
- }
328
- h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
329
- h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
330
- h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
331
- h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
332
- return `${(4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(36)}:${text.length}`;
322
+ const { high, low } = cyrb53([text]);
323
+ return `${(4294967296 * (2097151 & high) + low).toString(36)}:${text.length}`;
333
324
  }
334
325
 
335
326
  /**
@@ -33,6 +33,7 @@ import { CST_REHYDRATION_RESET_STATE, isCstShed } from '../residency/cst-residen
33
33
  import { type ExtendedServiceRegistry } from '../service-registry.js';
34
34
  import { type ServerSharedServicesMinimal } from '../shared-services.js';
35
35
  import { type DocumentUriPolicy } from '../workspace/document-uri-policy.js';
36
+ import { HydraniumWorkspaceLock } from '../workspace/hydranium-workspace-lock.js';
36
37
  import { BuildSession, type BuildSessionContext } from './build-session.js';
37
38
  import { type LabeledPhaseListener, labelPhaseListener } from './labeled-phase-listener.js';
38
39
 
@@ -125,6 +126,28 @@ export interface DocumentBuilderOptions extends LogNameOptions {
125
126
  readonly refreshCrossDocumentComputedScopes?: boolean;
126
127
  }
127
128
 
129
+ /**
130
+ * One locked build {@link HydraniumDocumentBuilder.scheduleUpdate} queued,
131
+ * which later requests merge into while it waits and join while it runs.
132
+ */
133
+ export interface ScheduledUpdate {
134
+ /** URIs to build, by URI string. Merged requests change it while the write is queued; fixed once it runs. */
135
+ readonly changed: Map<string, URI>;
136
+ /** URIs to delete, by URI string, under the same rule as {@link changed}. */
137
+ readonly deleted: Map<string, URI>;
138
+ /** The latest build reason any merged request gave; staged through {@link HydraniumDocumentBuilder.markNextReason} when the write runs. */
139
+ reason?: string;
140
+ /** The lock's {@link HydraniumWorkspaceLock.writeCancellations} right after this write was queued. */
141
+ readonly cancellations: number;
142
+ /**
143
+ * Set when the write starts to run, so its presence marks a running build:
144
+ * the text version of each changed URI the text store holds at that point.
145
+ */
146
+ takenVersions?: Map<string, number>;
147
+ /** The lock's promise for this write: settles when the build completes or is cancelled. */
148
+ readonly promise: Promise<void>;
149
+ }
150
+
128
151
  /**
129
152
  * Extends Langium's {@link DefaultDocumentBuilder} with:
130
153
  *
@@ -134,6 +157,8 @@ export interface DocumentBuilderOptions extends LogNameOptions {
134
157
  * non-validating build from suppressing validation, and a
135
158
  * {@link shouldRelink} that never judges a document unaffected on an index
136
159
  * that does not describe it.
160
+ * - **Shared locked builds** ({@link scheduleUpdate}): build requests that
161
+ * coincide share one locked build instead of cancelling each other.
137
162
  * - **URI handling** (always on): directory-aware flattening and cascade
138
163
  * deletes in {@link update}, plus the CST-rehydration and cross-document
139
164
  * refresh resets in {@link resetToState}.
@@ -176,8 +201,10 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
176
201
  protected readonly uriPolicy: DocumentUriPolicy;
177
202
  protected readonly clock: Clock;
178
203
  protected readonly messageRenderer: MessageRenderer;
179
- /** The lock a re-queued build takes; see {@link requeueOrphaned}. */
204
+ /** The lock {@link scheduleUpdate} builds under. */
180
205
  protected readonly workspaceLock: WorkspaceLock;
206
+ /** The latest write {@link scheduleUpdate} queued, until it ends; see {@link ScheduledUpdate}. */
207
+ protected scheduledUpdate?: ScheduledUpdate;
181
208
  /** Narrower handle on the same registry as the inherited `serviceRegistry`, for {@link ExtendedServiceRegistry.registrations}. */
182
209
  protected readonly languageRegistry: ExtendedServiceRegistry;
183
210
  protected languageFileExtensions: string[] = [];
@@ -206,6 +233,17 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
206
233
  protected lastCancelledTraceId?: number;
207
234
  /** Registered through {@link onDocumentPhaseDelivered}, by phase. */
208
235
  protected readonly documentPhaseDeliveredListeners = new MultiMap<DocumentState, (document: LangiumDocument, version: number) => void>();
236
+ /**
237
+ * Called with `false` by {@link buildDocuments} once a build has run every
238
+ * phase, and with `true` by {@link checkWaitsOnceDrained} after a build that
239
+ * threw; see {@link awaitDocumentState}. A listener runs synchronously,
240
+ * inside the build or inside that check's lock read. A throw fails the
241
+ * finished build; in the read it skips the listeners after it and surfaces
242
+ * only as an unhandled rejection.
243
+ */
244
+ protected readonly buildEndedListeners = new Set<(drained: boolean) => void>();
245
+ /** Set while the lock read {@link checkWaitsOnceDrained} queued has not run. */
246
+ protected drainCheckQueued = false;
209
247
 
210
248
  constructor(services: ServerSharedServicesMinimal, options: DocumentBuilderOptions = {}) {
211
249
  super(services);
@@ -311,10 +349,15 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
311
349
  *
312
350
  * Replacing Langium's rejection with a wait makes the wait's liveness this
313
351
  * class's responsibility: a listener can only fire if some build is still
314
- * going to reach `state`. Both re-queue sites below exist for that, and they
315
- * differ only in when the orphaning is observed — {@link isOrphaned} at
316
- * registration time, the `onBuildPhase` branch for a build cancelled after
317
- * the wait was already armed. Re-queuing stops after
352
+ * going to reach `state`. So the wait re-queues the document whenever
353
+ * {@link isOrphaned} holds: once when it is armed, and again after every
354
+ * build that completes (see {@link buildDocuments}). The second check is
355
+ * the one a wait armed during a build needs, since only the build's end
356
+ * shows whether it left the document behind; Langium's `onBuildPhase` for
357
+ * `Validated` cannot serve, as it stays silent for a build that validates
358
+ * nothing, such as the workspace's initial one. A build that throws is
359
+ * checked once the lock drains instead (see {@link checkWaitsOnceDrained}):
360
+ * whatever cancelled it may build nothing. Re-queuing stops after
318
361
  * {@link MAX_STALLED_REQUEUES} builds that fail to advance the document, so a
319
362
  * document the builder will never carry to `state` degrades to a pending wait
320
363
  * plus a warning rather than an endless build loop.
@@ -389,15 +432,19 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
389
432
  }, 'awaitDocumentState.skipsValidation')
390
433
  )
391
434
  : Disposable.create(() => undefined);
392
- const buildDisposable = this.onBuildPhase(DocumentState.Validated, () => {
435
+ const buildEnded = (drained: boolean): void => {
393
436
  if (document.state >= state || (state === DocumentState.Validated && this.skipsValidation(document))) {
394
437
  cleanup();
395
438
  resolve(uri);
396
- } else {
397
- // Orphaned by a cancelled build — re-queue so the next build catches it up.
398
- requeue('cancelled build');
439
+ } else if (drained ? this.activeSession === undefined : this.isOrphaned(document, state)) {
440
+ // No build is left to carry the document to `state`. Either
441
+ // check fails only while an unlocked build still runs; that
442
+ // build carries the document or ends here too.
443
+ requeue(drained ? 'build ended early' : 'build ended short of the target');
399
444
  }
400
- });
445
+ };
446
+ this.buildEndedListeners.add(buildEnded);
447
+ const buildDisposable = Disposable.create(() => this.buildEndedListeners.delete(buildEnded));
401
448
  const cancelDisposable = cancelToken.onCancellationRequested(() => {
402
449
  cleanup();
403
450
  reject(OperationCancelled);
@@ -409,9 +456,9 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
409
456
  cancelDisposable.dispose();
410
457
  };
411
458
  // Orphaned BEFORE the wait was armed: the build that would have
412
- // advanced this document has already finished, so neither listener
413
- // above can ever fire. Re-queue now — the listeners are registered, so
414
- // the resulting build resolves this wait.
459
+ // advanced this document has already finished, so none of these
460
+ // listeners fires until some other build runs. Re-queue now — the listeners are
461
+ // registered, so the resulting build resolves this wait.
415
462
  if (this.isOrphaned(document, state)) {
416
463
  requeue('quiescent builder');
417
464
  }
@@ -452,7 +499,9 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
452
499
  * `currentState` still advances to `Validated` (the validation phase runs
453
500
  * over an empty document list). Any first read that wants diagnostics — a
454
501
  * one-shot data-head read, a CLI query, `ModelService.validated` — lands
455
- * here.
502
+ * here. A read that arrives while that build still runs finds `currentState`
503
+ * below `state`, so this holds for it only once the build has ended, which
504
+ * is why {@link awaitDocumentState} asks again then.
456
505
  */
457
506
  protected isOrphaned(document: LangiumDocument, state: DocumentState): boolean {
458
507
  return document.state < state && this.currentState >= state;
@@ -465,13 +514,16 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
465
514
  * logged rather than swallowed — it leaves the waiter pending until its own
466
515
  * cancellation token fires, which is worth a line in the log.
467
516
  *
468
- * The build is an ordinary {@link WorkspaceLock} write, entered from a read.
469
- * The read waits until no write runs or is queued, so the build neither runs
470
- * beside a locked one nor cancels the build a re-queue is fired from. Unlocked,
471
- * it runs beside a locked build that cannot cancel it: both validate the same
472
- * version and deliver it twice. Taking the write directly, without the read,
473
- * cancels the running build and skips the rest of its build-phase listeners.
474
- * The read must not await the write, which queues behind that read.
517
+ * The build is a {@link scheduleUpdate}, entered from a {@link WorkspaceLock}
518
+ * read. The read waits until no write runs or is queued, so the build neither
519
+ * runs beside a locked one nor cancels the build a re-queue is fired from.
520
+ * Unlocked, it runs beside a locked build that cannot cancel it: both
521
+ * validate the same version and deliver it twice. Taking the write directly,
522
+ * without the read, cancels the running build and skips the rest of its
523
+ * build-phase listeners. The read must not await the write, which queues
524
+ * behind that read. A build scheduled in the gap between the read batch
525
+ * starting and this action running is still queued, so the re-queue merges
526
+ * into it rather than cancelling it.
475
527
  *
476
528
  * A write that ran while the read waited may have carried the document to
477
529
  * `state` already, and a second build would deliver that version again; the
@@ -484,13 +536,185 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
484
536
  if (document.state >= state) {
485
537
  return;
486
538
  }
487
- this.workspaceLock
488
- .write(token => this.update([document.uri], [], token))
489
- .catch((err: unknown) => {
490
- if (!isOperationCancelled(err)) {
491
- tracer.error(`Re-queue build failed: ${err instanceof Error ? err.message : String(err)}`);
492
- }
493
- });
539
+ this.scheduleUpdate([document.uri], []).catch((err: unknown) => {
540
+ if (!isOperationCancelled(err)) {
541
+ tracer.error(`Re-queue build failed: ${err instanceof Error ? err.message : String(err)}`);
542
+ }
543
+ });
544
+ });
545
+ }
546
+
547
+ // ============================================================
548
+ // Scheduled updates — one locked build for requests that coincide
549
+ // ============================================================
550
+
551
+ /**
552
+ * Build `changed` and `deleted` under the {@link WorkspaceLock}, as
553
+ * `workspaceLock.write(token => update(changed, deleted, token))` would,
554
+ * except where the build this method queued last already carries the
555
+ * request. The promise settles when the build that carries the request
556
+ * ends, completed or cancelled, as the lock's own promise does.
557
+ *
558
+ * Without it, requests that coincide cancel each other: each lock write
559
+ * cancels the one before it, after Langium's `update` has already reset the
560
+ * documents, scanned the workspace for relinking and called every
561
+ * `onUpdate` listener.
562
+ *
563
+ * - **Queued, not yet running:** the request merges into it, so nothing is
564
+ * cancelled. The later of a change and a deletion of one URI wins, so
565
+ * each URI reaches `update` in one list only: a file deleted and written
566
+ * again before the build starts is a change.
567
+ * - **Running:** the request joins it only when {@link canJoinRunningUpdate}
568
+ * holds, which keeps an edit made while it runs cancelling it.
569
+ * - **Otherwise,** or when anything else has cancelled that write since it
570
+ * was queued, a new write, which cancels the running build as any lock
571
+ * write does.
572
+ *
573
+ * `reason` is staged through {@link markNextReason} when the write runs. The
574
+ * latest reason of a merged request wins, as it does in the update
575
+ * handler's own debounced burst; a joining request's reason is dropped, since
576
+ * the build it joins has already started. A request without one leaves a
577
+ * reason staged by the caller in place.
578
+ *
579
+ * A caller that must run code of its own inside the write action takes the
580
+ * lock itself, and forgoes the sharing. A lock that is not a
581
+ * {@link HydraniumWorkspaceLock} cannot report a cancelled queued write, so
582
+ * every request then takes its own write.
583
+ */
584
+ scheduleUpdate(changed: URI[], deleted: URI[], reason?: string): Promise<void> {
585
+ const lock = this.workspaceLock;
586
+ if (!(lock instanceof HydraniumWorkspaceLock)) {
587
+ return lock.write(token => {
588
+ if (reason !== undefined) {
589
+ this.markNextReason(reason);
590
+ }
591
+ return this.update(changed, deleted, token);
592
+ });
593
+ }
594
+ const scheduled = this.scheduledUpdate;
595
+ if (scheduled && scheduled.cancellations === lock.writeCancellations) {
596
+ if (!scheduled.takenVersions) {
597
+ for (const uri of changed) {
598
+ scheduled.changed.set(uri.toString(), uri);
599
+ scheduled.deleted.delete(uri.toString());
600
+ }
601
+ for (const uri of deleted) {
602
+ scheduled.deleted.set(uri.toString(), uri);
603
+ scheduled.changed.delete(uri.toString());
604
+ }
605
+ scheduled.reason = reason ?? scheduled.reason;
606
+ return scheduled.promise;
607
+ }
608
+ if (this.canJoinRunningUpdate(scheduled, changed, deleted)) {
609
+ return scheduled.promise;
610
+ }
611
+ }
612
+ // The action runs on a later tick, by which time `request` is assigned.
613
+ const promise = lock.write(token => this.runScheduledUpdate(request, token));
614
+ const request: ScheduledUpdate = {
615
+ changed: new Map(changed.map(uri => [uri.toString(), uri])),
616
+ deleted: new Map(deleted.map(uri => [uri.toString(), uri])),
617
+ reason,
618
+ cancellations: lock.writeCancellations,
619
+ promise
620
+ };
621
+ this.scheduledUpdate = request;
622
+ return promise;
623
+ }
624
+
625
+ /**
626
+ * Whether a request for `changed` and `deleted` is carried by the running
627
+ * `scheduled` build, whose write nothing has cancelled: the request deletes
628
+ * nothing, and each changed URI is one the build took, held by the text
629
+ * store at the version it had when the build started.
630
+ *
631
+ * Each condition keeps a request from joining a build that would miss its
632
+ * change. Text that moved since the start may be newer than what the build
633
+ * parsed; a URI the text store does not hold is read from its file, which
634
+ * may have changed after the build read it; and a deletion may concern a
635
+ * document the build has already built. A joined request is also absent
636
+ * from the running `update`'s `onUpdate` call, which reported the store as
637
+ * it was when the build started; at an unchanged version that is the store
638
+ * the request sees.
639
+ */
640
+ protected canJoinRunningUpdate(scheduled: ScheduledUpdate, changed: URI[], deleted: URI[]): boolean {
641
+ const taken = scheduled.takenVersions;
642
+ if (!taken || deleted.length > 0) {
643
+ return false;
644
+ }
645
+ return changed.every(uri => taken.has(uri.toString()) && taken.get(uri.toString()) === this.textDocuments?.get(uri)?.version);
646
+ }
647
+
648
+ /**
649
+ * The write action of a {@link scheduleUpdate}: freeze the request, record
650
+ * what it takes, stage its reason and run `update`. Releases
651
+ * {@link scheduledUpdate} inside the lock, whatever throws, so a request
652
+ * arriving after the build ends starts a write of its own rather than
653
+ * joining a build that is over.
654
+ */
655
+ protected async runScheduledUpdate(request: ScheduledUpdate, token: CancellationToken): Promise<void> {
656
+ try {
657
+ const taken = new Map<string, number>();
658
+ for (const [key, uri] of request.changed) {
659
+ const version = this.textDocuments?.get(uri)?.version;
660
+ if (version !== undefined) {
661
+ taken.set(key, version);
662
+ }
663
+ }
664
+ request.takenVersions = taken;
665
+ if (request.reason !== undefined) {
666
+ this.markNextReason(request.reason);
667
+ }
668
+ await this.update([...request.changed.values()], [...request.deleted.values()], token);
669
+ } finally {
670
+ if (this.scheduledUpdate === request) {
671
+ this.scheduledUpdate = undefined;
672
+ }
673
+ }
674
+ }
675
+
676
+ /**
677
+ * Tell the {@link buildEndedListeners} once every phase has run, which is
678
+ * the one point where a build that skipped a waited-on document can be seen
679
+ * to have ended. Langium's `onBuildPhase` for `Validated` is not: it stays
680
+ * silent when no document reached that phase, which is every build that
681
+ * validates nothing, the workspace's initial one among them.
682
+ *
683
+ * A build that throws, whether cancelled or failed, tells none of them
684
+ * here: `currentState` is then below `Validated`, so {@link isOrphaned}
685
+ * could hold for no wait. {@link checkWaitsOnceDrained} tells them instead.
686
+ */
687
+ protected override async buildDocuments(
688
+ documents: LangiumDocument[],
689
+ options: BuildOptions,
690
+ cancelToken: CancellationToken
691
+ ): Promise<void> {
692
+ await super.buildDocuments(documents, options, cancelToken);
693
+ for (const listener of [...this.buildEndedListeners]) {
694
+ listener(false);
695
+ }
696
+ }
697
+
698
+ /**
699
+ * After a build that threw, tell the {@link buildEndedListeners}, with
700
+ * `drained` set, from a {@link WorkspaceLock} read: it runs once no write
701
+ * runs or is queued, so no locked build is left that could still carry a
702
+ * waited-on document. A cancelled build is usually followed by its
703
+ * canceller's, but a write that builds nothing, such as a last-close revert
704
+ * that finds its document reopened, leaves a wait armed during the build
705
+ * with nothing to resolve it, and so does a build that fails. One read
706
+ * serves every build that throws before it runs.
707
+ */
708
+ protected checkWaitsOnceDrained(): void {
709
+ if (this.drainCheckQueued) {
710
+ return;
711
+ }
712
+ this.drainCheckQueued = true;
713
+ void this.workspaceLock.read(() => {
714
+ this.drainCheckQueued = false;
715
+ for (const listener of [...this.buildEndedListeners]) {
716
+ listener(true);
717
+ }
494
718
  });
495
719
  }
496
720
 
@@ -860,6 +1084,7 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
860
1084
  if (isOperationCancelled(err)) {
861
1085
  session.cancelled = true;
862
1086
  }
1087
+ this.checkWaitsOnceDrained();
863
1088
  throw err;
864
1089
  } finally {
865
1090
  this.endSession(session);