@hydranium/core 1.0.0-next.387 → 1.0.0-next.390

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.
@@ -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.387",
3
+ "version": "1.0.0-next.390",
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
  "ast",
@@ -87,8 +87,8 @@
87
87
  "diff": "^5.2.0"
88
88
  },
89
89
  "devDependencies": {
90
- "@hydranium/langium": "1.0.0-next.387",
91
- "@hydranium/protocol": "1.0.0-next.387",
90
+ "@hydranium/langium": "1.0.0-next.390",
91
+ "@hydranium/protocol": "1.0.0-next.390",
92
92
  "@playwright/test": "^1.55.0",
93
93
  "@types/diff": "^5.2.0",
94
94
  "fake-indexeddb": "^6.2.5",
@@ -101,8 +101,8 @@
101
101
  "vscode-languageserver-types": "3.18.0"
102
102
  },
103
103
  "peerDependencies": {
104
- "@hydranium/langium": "1.0.0-next.387",
105
- "@hydranium/protocol": "1.0.0-next.387",
104
+ "@hydranium/langium": "1.0.0-next.390",
105
+ "@hydranium/protocol": "1.0.0-next.390",
106
106
  "@playwright/test": "^1.40.0",
107
107
  "vscode-jsonrpc": "^9.0.0",
108
108
  "vscode-languageserver": "~10.0.1",
@@ -414,6 +414,15 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
414
414
  return this.insideBuildUpdate;
415
415
  }
416
416
 
417
+ /**
418
+ * Whether the build calling the `onUpdate` listeners overlaps another build
419
+ * that still runs, such as an update outside the lock while a locked build
420
+ * runs. A listener reads this before its first await.
421
+ */
422
+ overlapsRunningBuild(): boolean {
423
+ return this.runningBuilds > 1;
424
+ }
425
+
417
426
  /**
418
427
  * Resolves once no build is in progress, however each ended, and never
419
428
  * rejects. It is `undefined` between builds. During a lock read no locked
@@ -1088,8 +1097,11 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
1088
1097
  * Treating an un-indexed document as affected is the conservative reading and
1089
1098
  * costs nothing in steady state — documents sit at `Validated` there, so this
1090
1099
  * branch is only taken for documents an interrupted build left behind.
1100
+ *
1101
+ * Public so a service that requests a build can skip one that would relink
1102
+ * nothing.
1091
1103
  */
1092
- protected override shouldRelink(document: LangiumDocument, changedUris: Set<string>): boolean {
1104
+ override shouldRelink(document: LangiumDocument, changedUris: Set<string>): boolean {
1093
1105
  if (document.state < DocumentState.IndexedReferences) {
1094
1106
  return true;
1095
1107
  }
@@ -1210,6 +1222,10 @@ export class HydraniumDocumentBuilder extends DefaultDocumentBuilder {
1210
1222
  * re-parsed AST would otherwise lack them. The caller
1211
1223
  * runs inside the `Linked` build phase; the build carries the document
1212
1224
  * through IndexedReferences + Validation (and their `onDocumentPhase`) next.
1225
+ *
1226
+ * Only this document is relinked. The documents that reference it still
1227
+ * hold nodes a re-parse replaced, or names a mutation changed, so the
1228
+ * caller has to relink them in a later build.
1213
1229
  */
1214
1230
  async reparseAndRelink(document: LangiumDocument, cancelToken: CancellationToken = CancellationToken.None): Promise<void> {
1215
1231
  const languageServices = this.serviceRegistry.getServices(document.uri);
@@ -7,14 +7,16 @@
7
7
  * SPDX-License-Identifier: MIT
8
8
  ********************************************************************************/
9
9
 
10
- import { isPromiseLike, Logger, type ProfileSession, type Tracer } from '@hydranium/protocol';
10
+ import { describeErrorWithStack, isPromiseLike, Logger, type ProfileSession, textHash, type Tracer } from '@hydranium/protocol';
11
11
  import {
12
12
  type AstNode,
13
13
  AstUtils,
14
14
  DocumentState,
15
15
  interruptAndCheck,
16
+ isMultiReference,
16
17
  isOperationCancelled,
17
18
  type LangiumDocument,
19
+ type URI,
18
20
  UriUtils
19
21
  } from '@hydranium/langium';
20
22
  import { CancellationToken, type Disposable } from 'vscode-languageserver';
@@ -94,6 +96,13 @@ export namespace IntegrityService {
94
96
  * `textDocument` still mirrors disk — read the repair off the AST, or from
95
97
  * the staged content the next open consumes.
96
98
  *
99
+ * A `Linked`-phase repair settles the documents that reference the repaired
100
+ * one in the same build against what the repair replaced. A build after it
101
+ * relinks them, and they settle again. Past `maxRelinkChain` such builds in
102
+ * a row they keep stale links. A closed `'editor'` document whose CST was
103
+ * shed re-reads its unrepaired text in that build and repeats the repair,
104
+ * so they resolve a renamed element by its old name until the next change.
105
+ *
97
106
  * Invariant: integrity rules only register at Parsed or Linked, so the build
98
107
  * is guaranteed post-integrity once it advances past `onBuildPhase(Linked)`
99
108
  * into `IndexedReferences`. {@link IntegrityService.register} enforces this at
@@ -111,6 +120,9 @@ export namespace IntegrityService {
111
120
  */
112
121
  const silentWriteReported = new WeakSet<ServerSharedServices>();
113
122
 
123
+ /** The running builds that relink dependents, per server, because a repair in one language can extend a chain in another. */
124
+ const runningRelinksByServer = new WeakMap<ServerSharedServices, Set<DependentRelink>>();
125
+
114
126
  /**
115
127
  * Construction options for {@link DefaultIntegrityService}. All fields
116
128
  * optional — the defaults reproduce the framework's behaviour exactly.
@@ -124,6 +136,26 @@ export interface IntegrityServiceOptions extends LogNameOptions {
124
136
  * to `'silent'`. See {@link IntegritySyncMode}.
125
137
  */
126
138
  readonly syncMode?: IntegritySyncMode;
139
+ /**
140
+ * How many builds in a row may relink the dependents of `Linked`-phase
141
+ * repairs before a repair only warns. A rule that settles within it gets
142
+ * every build it needs. An integer of at least 1, 3 by default.
143
+ */
144
+ readonly maxRelinkChain?: number;
145
+ }
146
+
147
+ /** The text a `Linked`-phase repair was computed from, and the text it produced. */
148
+ export interface IntegrityRepairTexts {
149
+ readonly from: string;
150
+ readonly to: string;
151
+ }
152
+
153
+ /** The build a repaired document has requested to relink its dependents. */
154
+ export interface DependentRelink {
155
+ /** The repaired document, whose dependents the build relinks. */
156
+ readonly document: LangiumDocument;
157
+ /** Its place in a chain of such builds, each requested by a repair of a document the one before relinked. */
158
+ readonly chain: number;
127
159
  }
128
160
 
129
161
  /**
@@ -173,6 +205,25 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
173
205
  * Linked-phase correction (see {@link resyncDocument}).
174
206
  */
175
207
  protected readonly documentBuilder: HydraniumDocumentBuilder;
208
+ /** Each document's {@link DependentRelink}, from its request until it settles. See {@link relinkDependents}. */
209
+ protected readonly relinkBuilds = new WeakMap<LangiumDocument, DependentRelink>();
210
+ /** The {@link replayKey} of the latest repair of each document that {@link stagesRepairs}, with the hash of the text it came from. */
211
+ protected readonly replayKeys = new WeakMap<LangiumDocument, { readonly key: string; readonly fromHash: string }>();
212
+ /** The relinks the check inside the lock found needed, until their build starts. */
213
+ protected readonly scheduledRelinks = new WeakSet<DependentRelink>();
214
+ /** The relinks of every language whose build runs, from its start until it settles or the next build starts. */
215
+ protected readonly runningRelinks: Set<DependentRelink>;
216
+ /** The rules of a repair while a relink's build waited or ran. Only one while it ran gets the next build in the chain. */
217
+ protected readonly repairedAgain = new WeakMap<DependentRelink, readonly IntegrityRule[]>();
218
+ /**
219
+ * Marks {@link scheduledRelinks} running as their builds start. Set on the
220
+ * first request only so it subscribes once, and never disposed.
221
+ */
222
+ protected relinkStarts?: Disposable;
223
+ /** See {@link IntegrityServiceOptions.maxRelinkChain}. */
224
+ protected readonly maxRelinkChain: number;
225
+ /** The rules that mutated each document in its latest `Linked`-phase {@link enforceIntegrity} pass, so a warning can name them. */
226
+ protected readonly mutatingRules = new WeakMap<LangiumDocument, Set<IntegrityRule>>();
176
227
 
177
228
  constructor(
178
229
  protected readonly services: ServerLanguageServices,
@@ -183,6 +234,14 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
183
234
  this.tracer = services.shared.Tracer.for(options.logName ?? 'Integrity').trace('instantiated');
184
235
  this.documentBuilder = services.shared.workspace.DocumentBuilder;
185
236
  this.syncMode = options.syncMode ?? 'silent';
237
+ this.maxRelinkChain = options.maxRelinkChain ?? 3;
238
+ if (!Number.isInteger(this.maxRelinkChain) || this.maxRelinkChain < 1) {
239
+ throw new RangeError(
240
+ `The integrity service's maxRelinkChain option must be an integer of at least 1, not ${this.maxRelinkChain}.`
241
+ );
242
+ }
243
+ this.runningRelinks = runningRelinksByServer.get(services.shared) ?? new Set();
244
+ runningRelinksByServer.set(services.shared, this.runningRelinks);
186
245
 
187
246
  // Read the language's IntegrityRuleContribution group and let each
188
247
  // contribution register one or many rules through this service (the
@@ -216,6 +275,10 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
216
275
  // debug threshold is active, so the production `info` path allocates
217
276
  // nothing and the hot rule loop stays a single const-branch off `session`.
218
277
  const session = Logger.isLevelEnabled('debug') ? this.tracer.profile(`integrity ${DocumentState[phase]}`) : undefined;
278
+ // Before any skip or cancel, so text with parse errors also ends a replay.
279
+ for (const document of documents) {
280
+ this.forgetReplayOfOtherText(document);
281
+ }
219
282
  for (const document of documents) {
220
283
  if (this.isStructurallyBroken(document)) {
221
284
  this.tracer.with(document.uri.toString()).debug('Parse errors: rules skipped until the document parses');
@@ -322,6 +385,13 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
322
385
  if (rulesByType.size === 0) {
323
386
  return false;
324
387
  }
388
+ // Read only after a Linked-phase repair. Cleared when such a pass starts, so
389
+ // one that records nothing leaves no older record to be named in its place.
390
+ const recorded = phase > DocumentState.Parsed;
391
+ if (recorded) {
392
+ this.mutatingRules.delete(document);
393
+ }
394
+ let mutating: Set<IntegrityRule> | undefined;
325
395
  const relativeUri = this.services.shared.workspace.WorkspaceManager.wsRelativePath(document.uri);
326
396
  for (const node of AstUtils.streamAst(document.parseResult.value)) {
327
397
  for (const rule of rulesByType.get(node.$type) ?? NO_RULES) {
@@ -333,6 +403,12 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
333
403
  : rule.enforce(node, document, ruleLogger);
334
404
  const mutated = isPromiseLike(enforced) ? await enforced : enforced;
335
405
  if (mutated) {
406
+ if (recorded && mutating === undefined) {
407
+ // Only a mutating pass leads to a read, so a pass that mutates nothing allocates nothing.
408
+ mutating = new Set();
409
+ this.mutatingRules.set(document, mutating);
410
+ }
411
+ mutating?.add(rule);
336
412
  ruleLogger.trace(`[${relativeUri}] Mutated ${this.formatNode(node)}`);
337
413
  }
338
414
  changed = mutated || changed;
@@ -409,6 +485,8 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
409
485
  // mutation in place would answer questions about this document from a
410
486
  // parse of text nobody has. Reconciling discards it by re-reading the
411
487
  // store, which is what the document now has to agree with.
488
+ //
489
+ // No dependents are relinked, because the edit's build relinks them.
412
490
  await this.reconcileDocument(document, cancelToken);
413
491
  return;
414
492
  }
@@ -423,6 +501,16 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
423
501
 
424
502
  await this.syncCorrections(textDocument, parsedFrom);
425
503
 
504
+ if (document.state > DocumentState.Parsed) {
505
+ // Requested before the in-place relink, and an error logged, so neither skips the other.
506
+ try {
507
+ this.relinkDependents(document, { from: parsedFrom, to: newText });
508
+ } catch (error: unknown) {
509
+ this.tracer
510
+ .with(document.uri.toString())
511
+ .error(`Relinking the dependents of a repaired document failed. ${describeErrorWithStack(error)}`);
512
+ }
513
+ }
426
514
  await this.reconcileDocument(document, cancelToken);
427
515
 
428
516
  // Re-version against the REPAIRED text, after either branch. The repair is
@@ -483,6 +571,232 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
483
571
  }
484
572
  }
485
573
 
574
+ /**
575
+ * Relink the documents that reference `document` in a build after this
576
+ * one, where it counts as changed. They were linked before the repair, so
577
+ * they hold nodes a re-parse replaced, or names the repair changed.
578
+ *
579
+ * Each document has at most one such build, its {@link DependentRelink},
580
+ * which goes through four steps.
581
+ *
582
+ * 1. Requested here, and kept in {@link relinkBuilds}. A repair while it
583
+ * waits is relinked by it.
584
+ * 2. Checked inside the lock, which skips it unless {@link isRelinkNeeded}.
585
+ * A needed one joins {@link scheduledRelinks}. While a build outside the
586
+ * lock runs, it is requested again once that build ends.
587
+ * 3. Started, when `onUpdate` reports its build. An explicit `build` starts
588
+ * none. It is in {@link runningRelinks} until it settles or the next
589
+ * build starts that overlaps no other. A repair while it runs is kept in
590
+ * {@link repairedAgain} and gets the next build.
591
+ * 4. Settled, when its request resolves, which drops it from every record.
592
+ *
593
+ * A repair of a document that a running build relinked continues that
594
+ * build's chain, which {@link maxRelinkChain} bounds. A repair with the
595
+ * {@link replayKey} of the one before requests none.
596
+ */
597
+ protected relinkDependents(document: LangiumDocument, repair: IntegrityRepairTexts): void {
598
+ const key = this.replayKey(document, repair);
599
+ if (key !== undefined && this.replayKeys.get(document)?.key === key) {
600
+ return;
601
+ }
602
+ const rules = [...(this.mutatingRules.get(document) ?? [])];
603
+ const pending = this.relinkBuilds.get(document);
604
+ let requested = true;
605
+ if (pending !== undefined) {
606
+ this.repairedAgain.set(pending, rules);
607
+ } else {
608
+ const causes = [...this.runningRelinks].filter(relink => this.isRelinkedBy(document, relink.document));
609
+ requested = this.relinkInChain(document, Math.max(0, ...causes.map(relink => relink.chain)) + 1, rules);
610
+ }
611
+ // Recorded after the request, so one that throws or is past the bound leaves no key that skips the next repair.
612
+ if (key !== undefined && requested) {
613
+ this.replayKeys.set(document, { key, fromHash: textHash(repair.from) });
614
+ }
615
+ }
616
+
617
+ /**
618
+ * Whether a build where `changed` counts as changed relinks `document`, as
619
+ * Langium's `shouldRelink` decides. Read from the document's links, because
620
+ * its build has dropped its indexed references until it indexes again.
621
+ */
622
+ protected isRelinkedBy(document: LangiumDocument, changed: LangiumDocument): boolean {
623
+ const target = changed.uri.toString();
624
+ return document.references.some(reference => {
625
+ const descriptions = isMultiReference(reference)
626
+ ? reference.items.map(item => item.$nodeDescription)
627
+ : [reference.$nodeDescription];
628
+ return reference.error !== undefined || descriptions.some(description => description?.documentUri.toString() === target);
629
+ });
630
+ }
631
+
632
+ /**
633
+ * Request the build at `chain`, or warn naming `rules` past
634
+ * {@link maxRelinkChain} and drop the {@link replayKey} of `document`.
635
+ * Returns whether it requested the build.
636
+ */
637
+ protected relinkInChain(document: LangiumDocument, chain: number, rules: readonly IntegrityRule[]): boolean {
638
+ if (chain > this.maxRelinkChain) {
639
+ this.replayKeys.delete(document);
640
+ const builds = this.maxRelinkChain === 1 ? '1 build in a row that relinks' : `${this.maxRelinkChain} builds in a row that relink`;
641
+ this.tracer
642
+ .with(document.uri.toString())
643
+ .warn(
644
+ `${this.formatRules(rules)} repaired documents in ${builds} their dependents, ` +
645
+ 'which keep stale links until the next change. A rule that does not settle causes this, ' +
646
+ 'or documents with linking errors that rules repair one after another.'
647
+ );
648
+ return false;
649
+ }
650
+ this.requestRelink(document, chain);
651
+ return true;
652
+ }
653
+
654
+ /** Request the build {@link relinkInChain} decided on, skipped unless {@link isRelinkNeeded}. */
655
+ protected requestRelink(document: LangiumDocument, chain: number): void {
656
+ const uri = document.uri;
657
+ this.relinkStarts ??= this.documentBuilder.onUpdate(changed => this.startRelinks(changed));
658
+ const relink: DependentRelink = { document, chain };
659
+ let unlockedBuild: Promise<void> | undefined;
660
+ const stillNeeded = (): boolean => {
661
+ // No locked build runs during the lock read, so one seen here runs outside the lock.
662
+ unlockedBuild = this.documentBuilder.activeBuild;
663
+ if (unlockedBuild !== undefined) {
664
+ return false;
665
+ }
666
+ // Marked before the deferral check, so a deferred relink stays scheduled until it settles.
667
+ const needed = this.isRelinkNeeded(document);
668
+ if (needed) {
669
+ this.scheduledRelinks.add(relink);
670
+ }
671
+ return needed;
672
+ };
673
+ const settled = (): void => {
674
+ this.relinkBuilds.delete(document);
675
+ this.scheduledRelinks.delete(relink);
676
+ this.runningRelinks.delete(relink);
677
+ };
678
+ const requestFailed = (error: unknown): void => {
679
+ // No build relinks the dependents, so the same repair again needs one.
680
+ this.replayKeys.delete(document);
681
+ this.tracer
682
+ .with(uri.toString())
683
+ .error(`Requesting the next build that relinks the dependents of a repaired document failed. ${describeErrorWithStack(error)}`);
684
+ };
685
+ const request = this.services.shared.workspace.VersionSyncService.requestRecoveryBuild(uri, { stillNeeded });
686
+ // Recorded after the request, so one that throws leaves no relink that never settles.
687
+ this.relinkBuilds.set(document, relink);
688
+ request
689
+ .then(
690
+ () => {
691
+ if (unlockedBuild !== undefined) {
692
+ // Requested again once that build ends, so the two never overlap. A repair meanwhile waits for it.
693
+ unlockedBuild
694
+ .then(() => {
695
+ settled();
696
+ this.requestRelink(document, chain);
697
+ })
698
+ .catch(requestFailed);
699
+ return;
700
+ }
701
+ // A build that never started, or was cancelled and not run again,
702
+ // leaves the repair to the build that held it back. Read before the
703
+ // next build's `onUpdate` clears it, which the lock's queue orders later.
704
+ const ran = this.runningRelinks.has(relink);
705
+ settled();
706
+ const again = this.repairedAgain.get(relink);
707
+ if (ran && again !== undefined) {
708
+ this.relinkInChain(document, chain + 1, again);
709
+ }
710
+ },
711
+ (error: unknown) => {
712
+ settled();
713
+ // The dependents were not relinked, so the same repair again needs its build.
714
+ this.replayKeys.delete(document);
715
+ this.tracer
716
+ .with(uri.toString())
717
+ .error(`The build that relinks the dependents of a repaired document failed. ${describeErrorWithStack(error)}`);
718
+ }
719
+ )
720
+ .catch(requestFailed);
721
+ }
722
+
723
+ /** Mark the {@link scheduledRelinks} of the `changed` documents running, as the build that carries them starts. */
724
+ protected startRelinks(changed: readonly URI[]): void {
725
+ // An explicit build relinks no dependents and ends no build it overlaps.
726
+ if (this.documentBuilder.isInsideBuildUpdate()) {
727
+ return;
728
+ }
729
+ // The build before this one ended, done or cancelled, so its relinks run no more,
730
+ // unless this one overlaps it, as a reentrant build does.
731
+ // The set is shared by every language. Each service clears only its own,
732
+ // because another's listener may already have marked this build's relinks running.
733
+ if (!this.documentBuilder.overlapsRunningBuild()) {
734
+ for (const relink of this.runningRelinks) {
735
+ if (this.relinkBuilds.get(relink.document) === relink) {
736
+ this.runningRelinks.delete(relink);
737
+ }
738
+ }
739
+ }
740
+ const documents = this.services.shared.workspace.LangiumDocuments;
741
+ for (const uri of changed) {
742
+ const document = documents.getDocument(uri);
743
+ const relink = document === undefined ? undefined : this.relinkBuilds.get(document);
744
+ if (relink !== undefined && this.scheduledRelinks.delete(relink)) {
745
+ // The starting build relinks any repair while it waited.
746
+ this.repairedAgain.delete(relink);
747
+ this.runningRelinks.add(relink);
748
+ }
749
+ }
750
+ }
751
+
752
+ /**
753
+ * Whether a build where `document` counts as changed relinks another
754
+ * document, by the builder's own test, which counts a document a cancelled
755
+ * build left unindexed as relinked.
756
+ */
757
+ protected isRelinkNeeded(document: LangiumDocument): boolean {
758
+ const documents = this.services.shared.workspace.LangiumDocuments;
759
+ const changed = new Set([document.uri.toString()]);
760
+ return (
761
+ documents.getDocument(document.uri) === document &&
762
+ documents.all.some(other => other !== document && this.documentBuilder.shouldRelink(other, changed))
763
+ );
764
+ }
765
+
766
+ /**
767
+ * Whether `document` stages its repairs rather than writing them, as a
768
+ * closed document does in `'editor'` mode, so its source keeps the
769
+ * unrepaired text.
770
+ */
771
+ protected stagesRepairs(document: LangiumDocument): boolean {
772
+ return this.syncMode === 'editor' && !this.textDocuments.isOpen(document.textDocument.uri);
773
+ }
774
+
775
+ /**
776
+ * What tells `repair` made again from the same text apart from a new one,
777
+ * for a document that {@link stagesRepairs}, which reads the unrepaired
778
+ * text again in every build that re-parses it after its CST was shed. Such
779
+ * a repair requests no build, because the build that re-parsed the
780
+ * document relinked its dependents, and another would replay it again.
781
+ * Hashes, so the record keeps no text the shed freed.
782
+ */
783
+ protected replayKey(document: LangiumDocument, repair: IntegrityRepairTexts): string | undefined {
784
+ return this.stagesRepairs(document) ? textHash(repair.from) + textHash(repair.to) : undefined;
785
+ }
786
+
787
+ /**
788
+ * Drop the {@link replayKey} of `document` once it was parsed from other
789
+ * text than its repair came from, such as a save and a revert. A repair of
790
+ * that text again is a new one, whose dependents need their build.
791
+ */
792
+ protected forgetReplayOfOtherText(document: LangiumDocument): void {
793
+ const recorded = this.replayKeys.get(document);
794
+ const parsedFrom = document.parseResult.value.$cstNode?.root.fullText;
795
+ if (recorded !== undefined && parsedFrom !== undefined && recorded.fromHash !== textHash(parsedFrom)) {
796
+ this.replayKeys.delete(document);
797
+ }
798
+ }
799
+
486
800
  /**
487
801
  * Propagate a correction to disk and/or the editor. The corrected text is
488
802
  * already in the synced store (in-place in {@link resyncDocument}); this
@@ -633,7 +947,7 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
633
947
 
634
948
  /**
635
949
  * Format a rule's name for inclusion in user-facing diagnostic strings
636
- * (error messages thrown during registration). Defaults to
950
+ * (error messages thrown during registration, and log lines). Defaults to
637
951
  * `label ?? id`. Adopters override when their error reporting needs
638
952
  * richer rule context.
639
953
  */
@@ -641,6 +955,15 @@ export class DefaultIntegrityService<TRoot extends AstNode = AstNode> implements
641
955
  return rule.label ?? rule.id;
642
956
  }
643
957
 
958
+ /** `rules` as the subject of a log line, or a rule left unnamed when an override recorded none. */
959
+ protected formatRules(rules: readonly IntegrityRule[]): string {
960
+ if (rules.length === 0) {
961
+ return 'An integrity rule';
962
+ }
963
+ const names = rules.map(rule => `'${this.formatRule(rule)}'`).join(', ');
964
+ return rules.length === 1 ? `Integrity rule ${names}` : `Integrity rules ${names}`;
965
+ }
966
+
644
967
  /**
645
968
  * Format an AST node for the per-mutation `trace` log line. Returns
646
969
  * `'TypeName'` or `"TypeName('label')"` when the node carries a string
@@ -56,6 +56,8 @@ export class HydraniumWorkspaceLock extends DefaultWorkspaceLock {
56
56
  protected readonly tracer: Tracer;
57
57
  protected readonly stalledWriteWarnMs: number;
58
58
  protected runningWrite?: RunningWrite;
59
+ /** Writes queued and not yet started. */
60
+ protected queuedWrites = 0;
59
61
 
60
62
  constructor(services: Pick<ServerSharedServicesMinimal, 'Clock' | 'Tracer'>, options: HydraniumWorkspaceLockOptions = {}) {
61
63
  super();
@@ -65,19 +67,28 @@ export class HydraniumWorkspaceLock extends DefaultWorkspaceLock {
65
67
  }
66
68
 
67
69
  /**
68
- * How many calls have cancelled the lock's latest write: every `write`,
69
- * which cancels the write before it, and every `cancelWrite`. A caller that
70
- * reads this right after queuing a write, and reads the same number later,
71
- * holds a write nothing has cancelled, whether it is still queued or
72
- * already running. Its token answers that only once the write runs, since
73
- * a queued write has not been handed it yet.
70
+ * How many calls to `write` or `cancelWrite` came while a write was still
71
+ * queued or running. A caller that reads this right after queuing a
72
+ * write, and reads the same number later, holds a write nothing has
73
+ * cancelled, whether it is still queued or already running. Its token
74
+ * answers that only once the write runs, since a queued write has not been
75
+ * handed it yet. A different number read after the caller's write ended
76
+ * does not prove that write was cancelled, since a later write that
77
+ * cancels another queued or running write counts too. A caller that
78
+ * retries on it builds once more than it needs to.
74
79
  */
75
80
  get writeCancellations(): number {
76
81
  return this.cancelledWriteCount;
77
82
  }
78
83
 
79
84
  override write(action: (token: CancellationToken) => MaybePromise<void>): Promise<void> {
80
- return super.write(token => runInWriteLockScope(() => this.runWrite(action, token)));
85
+ const queued = super.write(token => {
86
+ this.queuedWrites--;
87
+ return runInWriteLockScope(() => this.runWrite(action, token));
88
+ });
89
+ // Counted after Langium's `write` has cancelled the write before it, so the new write does not count itself.
90
+ this.queuedWrites++;
91
+ return queued;
81
92
  }
82
93
 
83
94
  /** Run `action` as {@link runningWrite}, so a cancellation while it runs can time it. */
@@ -96,11 +107,15 @@ export class HydraniumWorkspaceLock extends DefaultWorkspaceLock {
96
107
 
97
108
  /**
98
109
  * Counted here rather than in {@link write}, because Langium's `write` cancels
99
- * through this method too. A write running when this is called has been
100
- * cancelled, by this call or an earlier one, so its timer starts here.
110
+ * through this method too. A write that has ended is not counted, so the
111
+ * next write does not count as cancelling it. A write running when this is
112
+ * called has been cancelled, by this call or an earlier one, so its timer
113
+ * starts here.
101
114
  */
102
115
  override cancelWrite(): void {
103
- this.cancelledWriteCount++;
116
+ if (this.queuedWrites > 0 || this.runningWrite !== undefined) {
117
+ this.cancelledWriteCount++;
118
+ }
104
119
  const running = this.runningWrite;
105
120
  if (running && !running.stallTimer && this.stalledWriteWarnMs > 0) {
106
121
  running.stallTimer = this.clock.setTimer(() => this.reportStalledWrite(), this.stalledWriteWarnMs);
@@ -89,6 +89,8 @@ export interface StubDocumentBuilder
89
89
  | 'finalBuildState'
90
90
  | 'onBuildEnded'
91
91
  | 'isInsideBuildUpdate'
92
+ | 'overlapsRunningBuild'
93
+ | 'shouldRelink'
92
94
  | 'activeBuild'
93
95
  | 'markNextReason'
94
96
  | 'shouldValidateDocument'
@@ -193,6 +195,9 @@ export function makeStubDocumentBuilder(workspaceLock?: WorkspaceLock): StubDocu
193
195
  shouldValidateDocument: () => Boolean(stub.updateBuildOptions.validation),
194
196
  // The stub never runs a build of its own.
195
197
  isInsideBuildUpdate: () => false,
198
+ overlapsRunningBuild: () => false,
199
+ // It builds no document, so a change relinks none.
200
+ shouldRelink: () => false,
196
201
  activeBuild: undefined,
197
202
  markNextReason: () => undefined,
198
203
  get updateCalls() {