@hydranium/glsp-server 1.0.0-next.79 → 1.0.0-next.85

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 (43) hide show
  1. package/lib/command/hydranium-glsp-recording-command.d.ts +21 -21
  2. package/lib/command/hydranium-glsp-recording-command.d.ts.map +1 -1
  3. package/lib/command/hydranium-glsp-recording-command.js +24 -25
  4. package/lib/command/hydranium-glsp-recording-command.js.map +1 -1
  5. package/lib/state/abstract-hydranium-glsp-state.d.ts +65 -42
  6. package/lib/state/abstract-hydranium-glsp-state.d.ts.map +1 -1
  7. package/lib/state/abstract-hydranium-glsp-state.js +58 -41
  8. package/lib/state/abstract-hydranium-glsp-state.js.map +1 -1
  9. package/lib/state/full-text-hydranium-glsp-state.d.ts +3 -3
  10. package/lib/state/full-text-hydranium-glsp-state.d.ts.map +1 -1
  11. package/lib/state/full-text-hydranium-glsp-state.js +4 -3
  12. package/lib/state/full-text-hydranium-glsp-state.js.map +1 -1
  13. package/lib/state/reconcile-source-model-write.d.ts +7 -6
  14. package/lib/state/reconcile-source-model-write.d.ts.map +1 -1
  15. package/lib/state/reconcile-source-model-write.js +4 -4
  16. package/lib/state/reconcile-source-model-write.js.map +1 -1
  17. package/lib/state/reconciling-multi-document-glsp-state.d.ts +15 -14
  18. package/lib/state/reconciling-multi-document-glsp-state.d.ts.map +1 -1
  19. package/lib/state/reconciling-multi-document-glsp-state.js +21 -20
  20. package/lib/state/reconciling-multi-document-glsp-state.js.map +1 -1
  21. package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts +3 -3
  22. package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts.map +1 -1
  23. package/lib/state/reconciling-transfer-hydranium-glsp-state.js +6 -6
  24. package/lib/state/reconciling-transfer-hydranium-glsp-state.js.map +1 -1
  25. package/lib/storage/hydranium-glsp-storage.d.ts +10 -10
  26. package/lib/storage/hydranium-glsp-storage.d.ts.map +1 -1
  27. package/lib/storage/hydranium-glsp-storage.js.map +1 -1
  28. package/lib/validation/diagnostic-markers.d.ts +2 -2
  29. package/lib/validation/diagnostic-markers.d.ts.map +1 -1
  30. package/lib/validation/diagnostic-markers.js.map +1 -1
  31. package/lib/validation/hydranium-glsp-model-validator.d.ts.map +1 -1
  32. package/lib/validation/hydranium-glsp-model-validator.js +1 -1
  33. package/lib/validation/hydranium-glsp-model-validator.js.map +1 -1
  34. package/package.json +7 -7
  35. package/src/command/hydranium-glsp-recording-command.ts +25 -25
  36. package/src/state/abstract-hydranium-glsp-state.ts +85 -51
  37. package/src/state/full-text-hydranium-glsp-state.ts +5 -4
  38. package/src/state/reconcile-source-model-write.ts +10 -9
  39. package/src/state/reconciling-multi-document-glsp-state.ts +22 -21
  40. package/src/state/reconciling-transfer-hydranium-glsp-state.ts +7 -7
  41. package/src/storage/hydranium-glsp-storage.ts +10 -10
  42. package/src/validation/diagnostic-markers.ts +2 -2
  43. package/src/validation/hydranium-glsp-model-validator.ts +3 -3
@@ -10,7 +10,7 @@
10
10
  import { type JsonModelState } from '@eclipse-glsp/server';
11
11
  import { injectable } from 'inversify';
12
12
  import { type AstNode } from '@hydranium/langium';
13
- import { type TransferElement } from '@hydranium/protocol';
13
+ import { type BasedOn, type TransferElement } from '@hydranium/protocol';
14
14
  import { AbstractHydraniumGlspState } from './abstract-hydranium-glsp-state.js';
15
15
  import { reconcileSourceModelWrite } from './reconcile-source-model-write.js';
16
16
 
@@ -71,8 +71,8 @@ export interface MultiDocumentSourceModel<TPrimary extends TransferElement = Tra
71
71
  * than reconciled. Gating them too would need a per-document reconcile whose
72
72
  * outcomes can disagree (merge one, conflict another) with no way to un-write the
73
73
  * merged one — the atomicity problem again, one layer up. An adopter that wants
74
- * a coarser check overrides {@link secondaryBaseVersion} to return the version
75
- * {@link AbstractHydraniumGlspState.capturedVersionOf} already holds, and takes
74
+ * a coarser check overrides {@link secondaryBasedOn} to return the version
75
+ * {@link AbstractHydraniumGlspState.snapshotVersionOf} already holds, and takes
76
76
  * on the partial-write window named there.
77
77
  */
78
78
  @injectable()
@@ -140,10 +140,10 @@ export class ReconcilingMultiDocumentGlspState<TRoot extends AstNode, TPrimary e
140
140
  * `ConflictError` from the primary, reconcile via the injected policy exactly
141
141
  * as the single-document state does — the orchestration is shared.
142
142
  */
143
- async updateSourceModel(model: MultiDocumentSourceModel<TPrimary>, version?: number): Promise<void> {
144
- return reconcileSourceModelWrite<MultiDocumentSourceModel<TPrimary>>(model, version, {
145
- persist: async (candidate, baseVersion) => {
146
- const { root } = await this.persist(candidate, baseVersion);
143
+ async updateSourceModel(model: MultiDocumentSourceModel<TPrimary>, basedOn: BasedOn = this.basedOn): Promise<void> {
144
+ return reconcileSourceModelWrite<MultiDocumentSourceModel<TPrimary>>(model, basedOn, {
145
+ persist: async (candidate, candidateBasedOn) => {
146
+ const { root } = await this.persist(candidate, candidateBasedOn);
147
147
  this.setSourceRoot(this._sourceUri, root);
148
148
  },
149
149
  refetch: () => this.refetch(),
@@ -156,11 +156,11 @@ export class ReconcilingMultiDocumentGlspState<TRoot extends AstNode, TPrimary e
156
156
 
157
157
  /**
158
158
  * Write hook — secondaries first (ungated), primary last (gated on
159
- * `baseVersion`). The order is the failure-mode choice documented on the
159
+ * `basedOn`). The order is the failure-mode choice documented on the
160
160
  * class, not incidental: override this when the primary is the document the
161
161
  * others REFERENCE, since then this order writes the references first.
162
162
  */
163
- protected async persist(model: MultiDocumentSourceModel<TPrimary>, baseVersion?: number): Promise<{ root: TRoot }> {
163
+ protected async persist(model: MultiDocumentSourceModel<TPrimary>, basedOn: BasedOn): Promise<{ root: TRoot }> {
164
164
  for (const [uri, secondary] of Object.entries(model.secondaries)) {
165
165
  if (this.hasChanged(this.baseline?.secondaries[uri], secondary)) {
166
166
  await this.persistSecondary(uri, secondary);
@@ -171,7 +171,7 @@ export class ReconcilingMultiDocumentGlspState<TRoot extends AstNode, TPrimary e
171
171
  // captured rather than round-tripping the document for no reason.
172
172
  return { root: this._sourceRoot };
173
173
  }
174
- return this.persistPrimary(model.primary, baseVersion);
174
+ return this.persistPrimary(model.primary, basedOn);
175
175
  }
176
176
 
177
177
  /**
@@ -194,19 +194,19 @@ export class ReconcilingMultiDocumentGlspState<TRoot extends AstNode, TPrimary e
194
194
  return baseline === undefined || JSON.stringify(baseline) !== JSON.stringify(candidate);
195
195
  }
196
196
 
197
- /** Write the primary document, opting into the conflict gate when `baseVersion` is given. */
198
- protected async persistPrimary(model: TPrimary, baseVersion?: number): Promise<{ root: TRoot }> {
197
+ /** Write the primary document, opting into the conflict gate unless `basedOn` is `'anything'`. */
198
+ protected async persistPrimary(model: TPrimary, basedOn: BasedOn): Promise<{ root: TRoot }> {
199
199
  const document = await this.sharedServices.model.ModelService.update({
200
200
  uri: this._sourceUri,
201
201
  model,
202
202
  clientId: this.clientId,
203
- baseVersion
203
+ basedOn
204
204
  });
205
205
  return document as unknown as { root: TRoot };
206
206
  }
207
207
 
208
208
  /**
209
- * Write one secondary document, gated on whatever {@link secondaryBaseVersion}
209
+ * Write one secondary document, gated on whatever {@link secondaryBasedOn}
210
210
  * returns. Override alongside {@link persistPrimary} when the round-trip
211
211
  * differs per document role — a layout file and a semantic file need not
212
212
  * share a serializer.
@@ -216,17 +216,18 @@ export class ReconcilingMultiDocumentGlspState<TRoot extends AstNode, TPrimary e
216
216
  uri,
217
217
  model,
218
218
  clientId: this.clientId,
219
- baseVersion: this.secondaryBaseVersion(uri)
219
+ basedOn: this.secondaryBasedOn(uri)
220
220
  });
221
221
  }
222
222
 
223
223
  /**
224
- * Based-on version for a secondary write. Default `undefined` — ungated, for
225
- * the reason given under the conflict-gate note on the class doc.
224
+ * What a secondary write declares it was based on. Default `'anything'` —
225
+ * ungated, because gating the whole set needs a per-document reconcile whose
226
+ * outcomes can disagree, with no way to un-write an already-merged sibling.
226
227
  *
227
228
  * **The hook exists so that opting in is an override rather than a
228
- * reimplementation.** The state already captures what such a check needs
229
- * ({@link AbstractHydraniumGlspState.capturedVersionOf}), and returning that
229
+ * reimplementation.** The state already holds what such a check needs
230
+ * ({@link AbstractHydraniumGlspState.snapshotVersionOf}), and returning that
230
231
  * value here is the whole opt-in; without it the only place to put the
231
232
  * comparison is a copy of {@link persistSecondary}, which then has to be kept
232
233
  * in step with the framework's write call.
@@ -236,8 +237,8 @@ export class ReconcilingMultiDocumentGlspState<TRoot extends AstNode, TPrimary e
236
237
  * the reconcile then drops the edit those writes stay. Weigh that against the
237
238
  * default, which lets a foreign edit to a secondary be overwritten silently.
238
239
  */
239
- protected secondaryBaseVersion(_uri: string): number | undefined {
240
- return undefined;
240
+ protected secondaryBasedOn(_uri: string): BasedOn {
241
+ return 'anything';
241
242
  }
242
243
 
243
244
  /**
@@ -10,7 +10,7 @@
10
10
  import { type JsonModelState } from '@eclipse-glsp/server';
11
11
  import { injectable } from 'inversify';
12
12
  import { type AstNode } from '@hydranium/langium';
13
- import { type TransferElement } from '@hydranium/protocol';
13
+ import { type BasedOn, type TransferElement } from '@hydranium/protocol';
14
14
  import { AbstractHydraniumGlspState } from './abstract-hydranium-glsp-state.js';
15
15
  import { reconcileSourceModelWrite } from './reconcile-source-model-write.js';
16
16
 
@@ -84,13 +84,13 @@ export class ReconcilingTransferHydraniumGlspState<TRoot extends AstNode, TSourc
84
84
  * (force = last-writer-wins, reconciling = field-level merge) shared with
85
85
  * undo / redo.
86
86
  */
87
- async updateSourceModel(model: TSourceModel, version?: number): Promise<void> {
87
+ async updateSourceModel(model: TSourceModel, basedOn: BasedOn = this.basedOn): Promise<void> {
88
88
  // Orchestration lives in `reconcileSourceModelWrite` so the multi-document
89
89
  // state gets the identical conflict handling; this method supplies only
90
90
  // the single-document meaning of persist / project.
91
- return reconcileSourceModelWrite<TSourceModel>(model, version, {
92
- persist: async (candidate, baseVersion) => {
93
- const { root } = await this.persist(candidate, baseVersion);
91
+ return reconcileSourceModelWrite<TSourceModel>(model, basedOn, {
92
+ persist: async (candidate, candidateBasedOn) => {
93
+ const { root } = await this.persist(candidate, candidateBasedOn);
94
94
  this.setSourceRoot(this._sourceUri, root);
95
95
  },
96
96
  refetch: () => this.refetch(),
@@ -108,12 +108,12 @@ export class ReconcilingTransferHydraniumGlspState<TRoot extends AstNode, TSourc
108
108
  * round-trip differs override this; the orchestration in
109
109
  * {@link updateSourceModel} is unchanged.
110
110
  */
111
- protected async persist(model: TSourceModel, baseVersion?: number): Promise<{ root: TRoot }> {
111
+ protected async persist(model: TSourceModel, basedOn: BasedOn): Promise<{ root: TRoot }> {
112
112
  const document = await this.sharedServices.model.ModelService.update({
113
113
  uri: this._sourceUri,
114
114
  model,
115
115
  clientId: this.clientId,
116
- baseVersion
116
+ basedOn
117
117
  });
118
118
  return document as unknown as { root: TRoot };
119
119
  }
@@ -225,7 +225,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
225
225
  protected resubmitDebouncer!: Debouncer;
226
226
 
227
227
  /** Latest event document awaiting the debounced resubmit; read by {@link flushResubmit} (last write wins). */
228
- protected pendingResubmitDocument?: AstDocument<AstNode, unknown>;
228
+ protected pendingResubmitDocument?: AstDocument<AstNode>;
229
229
 
230
230
  @postConstruct()
231
231
  protected init(): void {
@@ -376,7 +376,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
376
376
  * drag this client authors — the guard above gates the resubmit, not the
377
377
  * markers.
378
378
  */
379
- protected handleSecondaryUpdated(uri: string, event: AstDocumentUpdatedEvent<AstNode, unknown>): void {
379
+ protected handleSecondaryUpdated(uri: string, event: AstDocumentUpdatedEvent<AstNode>): void {
380
380
  if (this.disposeIfStale(uri)) {
381
381
  return;
382
382
  }
@@ -402,7 +402,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
402
402
  * arrives pre-validation with an empty array — and diagnostics are the reason
403
403
  * {@link doUpdateAndSubmit} takes a separate event document at all.
404
404
  */
405
- protected currentPrimaryDocument(): AstDocument<AstNode, unknown> | undefined {
405
+ protected currentPrimaryDocument(): AstDocument<AstNode> | undefined {
406
406
  const document = this.sharedServices.model.ModelService.getDocument(this.state.sourceUri);
407
407
  return document ? AstDocument.from(document) : undefined;
408
408
  }
@@ -415,7 +415,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
415
415
  * diagnostic markers for every update — including own edits, which skip
416
416
  * the resubmit but can still change diagnostics.
417
417
  */
418
- protected async handleModelUpdated(rootUri: string, event: AstDocumentUpdatedEvent<AstNode, unknown>): Promise<void> {
418
+ protected async handleModelUpdated(rootUri: string, event: AstDocumentUpdatedEvent<AstNode>): Promise<void> {
419
419
  if (this.disposeIfStale(rootUri)) {
420
420
  return;
421
421
  }
@@ -447,7 +447,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
447
447
  * {@link resubmitDebouncer}, so a burst of rebuilds runs
448
448
  * {@link doUpdateAndSubmit} once with the most recent document.
449
449
  */
450
- protected scheduleUpdateAndSubmit(document: AstDocument<AstNode, unknown>): void {
450
+ protected scheduleUpdateAndSubmit(document: AstDocument<AstNode>): void {
451
451
  this.pendingResubmitDocument = document;
452
452
  this.resubmitDebouncer.schedule();
453
453
  }
@@ -474,7 +474,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
474
474
  * mark our stack clean so the editor doesn't prompt to re-save identical
475
475
  * content. Our own saves already settle the stack through the save flow.
476
476
  */
477
- protected handleModelSaved(event: AstDocumentSavedEvent<AstNode, unknown>): void {
477
+ protected handleModelSaved(event: AstDocumentSavedEvent<AstNode>): void {
478
478
  if (this.state.clientId !== event.sourceClientId) {
479
479
  this.commandStack.saveIsDone();
480
480
  }
@@ -488,7 +488,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
488
488
  * pre-validation, and this path re-settles precisely to escape a transient
489
489
  * mid-rebuild snapshot.
490
490
  */
491
- protected async doUpdateAndSubmit(rootUri: string, eventDocument: AstDocument<AstNode, unknown>): Promise<Action[]> {
491
+ protected async doUpdateAndSubmit(rootUri: string, eventDocument: AstDocument<AstNode>): Promise<Action[]> {
492
492
  // Settle-gate the capture: never setSourceRoot off the event's possibly-transient
493
493
  // snapshot — the event can arrive while a re-entered document is mid-rebuild.
494
494
  const document = await this.sharedServices.model.ModelService.settled(rootUri);
@@ -526,7 +526,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
526
526
  * flip {@link AbstractHydraniumGlspState.editMode}, and — only on a transition —
527
527
  * return the {@link onParseErrorChanged} actions. No transition → no actions.
528
528
  */
529
- protected refreshEditMode(document: AstDocument<AstNode, unknown>): Action[] {
529
+ protected refreshEditMode(document: AstDocument<AstNode>): Action[] {
530
530
  const broken = this.isStructurallyBroken(document);
531
531
  const previousEditMode = this.state.editMode;
532
532
  this.state.editMode = broken ? EditMode.READONLY : EditMode.EDITABLE;
@@ -543,7 +543,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
543
543
  * carrying a Langium `lexing-error` / `parsing-error` code. Adopters override
544
544
  * to compose additional break reasons.
545
545
  */
546
- protected isStructurallyBroken(document: AstDocument<AstNode, unknown>): boolean {
546
+ protected isStructurallyBroken(document: AstDocument<AstNode>): boolean {
547
547
  return document.diagnostics.some(diagnostic => isStructuralDiagnostic(diagnostic));
548
548
  }
549
549
 
@@ -577,7 +577,7 @@ export class HydraniumGlspStorage<TRoot extends AstNode, TSourceModel = string>
577
577
  * the validation clear a moment later. Measured on a browser host, polling the
578
578
  * overlay: no element, empty, the sentence, empty again.
579
579
  */
580
- protected onParseErrorChanged(_document: AstDocument<AstNode, unknown>, broken: boolean): Action[] {
580
+ protected onParseErrorChanged(_document: AstDocument<AstNode>, broken: boolean): Action[] {
581
581
  return [
582
582
  SetEditModeAction.create(broken ? EditMode.READONLY : EditMode.EDITABLE),
583
583
  broken
@@ -10,7 +10,7 @@
10
10
  import { type Marker, MarkerKind } from '@eclipse-glsp/protocol';
11
11
  import { type AstNode } from '@hydranium/langium';
12
12
  import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-types';
13
- import { type TransferLspDiagnostic } from '@hydranium/core';
13
+ import { type AstDiagnostic } from '@hydranium/core';
14
14
 
15
15
  /**
16
16
  * The model-shaped lookups the diagnostic→marker translation needs, injected so
@@ -48,7 +48,7 @@ export interface DiagnosticMarkerLookups {
48
48
  * `MarkersReason` so a fresh `SetMarkersAction` replaces the previous markers
49
49
  * (an empty result clears them when all errors are fixed).
50
50
  */
51
- export function diagnosticsToMarkers(diagnostics: readonly TransferLspDiagnostic[], lookups: DiagnosticMarkerLookups): Marker[] {
51
+ export function diagnosticsToMarkers(diagnostics: readonly AstDiagnostic[], lookups: DiagnosticMarkerLookups): Marker[] {
52
52
  const markers: Marker[] = [];
53
53
  for (const diagnostic of diagnostics) {
54
54
  const kind = markerKind(diagnostic.severity);
@@ -12,7 +12,7 @@ import { type GModelElement, ModelState, type ModelValidator } from '@eclipse-gl
12
12
  import { inject, injectable } from 'inversify';
13
13
  import { type AstNode } from '@hydranium/langium';
14
14
  import { URI } from '@hydranium/langium';
15
- import { type ServerSharedServices, type TransferLspDiagnostic } from '@hydranium/core';
15
+ import { type ServerSharedServices, type AstDiagnostic } from '@hydranium/core';
16
16
  import { type AbstractHydraniumGlspState } from '../state/abstract-hydranium-glsp-state.js';
17
17
  import { HydraniumTypes } from '../state/hydranium-shared-core-services.js';
18
18
  import { type DiagnosticMarkerLookups, diagnosticsToMarkers } from './diagnostic-markers.js';
@@ -75,9 +75,9 @@ export class HydraniumGlspModelValidator<TRoot extends AstNode = AstNode> implem
75
75
  continue;
76
76
  }
77
77
  // Diagnostics are produced by the framework's `HydraniumDocumentValidator`,
78
- // which emits `TransferLspDiagnostic` (LSP `Diagnostic` + `element` path).
78
+ // which emits `AstDiagnostic` (LSP `Diagnostic` + `element` path).
79
79
  // Parser/lexer diagnostics carry no `element`; the lookup drops them.
80
- const diagnostics = document.diagnostics as TransferLspDiagnostic[] | undefined;
80
+ const diagnostics = document.diagnostics as AstDiagnostic[] | undefined;
81
81
  if (!diagnostics || diagnostics.length === 0) {
82
82
  continue;
83
83
  }