@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.
- package/lib/command/hydranium-glsp-recording-command.d.ts +21 -21
- package/lib/command/hydranium-glsp-recording-command.d.ts.map +1 -1
- package/lib/command/hydranium-glsp-recording-command.js +24 -25
- package/lib/command/hydranium-glsp-recording-command.js.map +1 -1
- package/lib/state/abstract-hydranium-glsp-state.d.ts +65 -42
- package/lib/state/abstract-hydranium-glsp-state.d.ts.map +1 -1
- package/lib/state/abstract-hydranium-glsp-state.js +58 -41
- package/lib/state/abstract-hydranium-glsp-state.js.map +1 -1
- package/lib/state/full-text-hydranium-glsp-state.d.ts +3 -3
- package/lib/state/full-text-hydranium-glsp-state.d.ts.map +1 -1
- package/lib/state/full-text-hydranium-glsp-state.js +4 -3
- package/lib/state/full-text-hydranium-glsp-state.js.map +1 -1
- package/lib/state/reconcile-source-model-write.d.ts +7 -6
- package/lib/state/reconcile-source-model-write.d.ts.map +1 -1
- package/lib/state/reconcile-source-model-write.js +4 -4
- package/lib/state/reconcile-source-model-write.js.map +1 -1
- package/lib/state/reconciling-multi-document-glsp-state.d.ts +15 -14
- package/lib/state/reconciling-multi-document-glsp-state.d.ts.map +1 -1
- package/lib/state/reconciling-multi-document-glsp-state.js +21 -20
- package/lib/state/reconciling-multi-document-glsp-state.js.map +1 -1
- package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts +3 -3
- package/lib/state/reconciling-transfer-hydranium-glsp-state.d.ts.map +1 -1
- package/lib/state/reconciling-transfer-hydranium-glsp-state.js +6 -6
- package/lib/state/reconciling-transfer-hydranium-glsp-state.js.map +1 -1
- package/lib/storage/hydranium-glsp-storage.d.ts +10 -10
- package/lib/storage/hydranium-glsp-storage.d.ts.map +1 -1
- package/lib/storage/hydranium-glsp-storage.js.map +1 -1
- package/lib/validation/diagnostic-markers.d.ts +2 -2
- package/lib/validation/diagnostic-markers.d.ts.map +1 -1
- package/lib/validation/diagnostic-markers.js.map +1 -1
- package/lib/validation/hydranium-glsp-model-validator.d.ts.map +1 -1
- package/lib/validation/hydranium-glsp-model-validator.js +1 -1
- package/lib/validation/hydranium-glsp-model-validator.js.map +1 -1
- package/package.json +7 -7
- package/src/command/hydranium-glsp-recording-command.ts +25 -25
- package/src/state/abstract-hydranium-glsp-state.ts +85 -51
- package/src/state/full-text-hydranium-glsp-state.ts +5 -4
- package/src/state/reconcile-source-model-write.ts +10 -9
- package/src/state/reconciling-multi-document-glsp-state.ts +22 -21
- package/src/state/reconciling-transfer-hydranium-glsp-state.ts +7 -7
- package/src/storage/hydranium-glsp-storage.ts +10 -10
- package/src/validation/diagnostic-markers.ts +2 -2
- 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
|
|
75
|
-
* {@link AbstractHydraniumGlspState.
|
|
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>,
|
|
144
|
-
return reconcileSourceModelWrite<MultiDocumentSourceModel<TPrimary>>(model,
|
|
145
|
-
persist: async (candidate,
|
|
146
|
-
const { root } = await this.persist(candidate,
|
|
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
|
-
* `
|
|
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>,
|
|
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,
|
|
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
|
|
198
|
-
protected async persistPrimary(model: TPrimary,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
219
|
+
basedOn: this.secondaryBasedOn(uri)
|
|
220
220
|
});
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
|
-
*
|
|
225
|
-
*
|
|
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
|
|
229
|
-
* ({@link AbstractHydraniumGlspState.
|
|
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
|
|
240
|
-
return
|
|
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,
|
|
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,
|
|
92
|
-
persist: async (candidate,
|
|
93
|
-
const { root } = await this.persist(candidate,
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 `
|
|
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
|
|
80
|
+
const diagnostics = document.diagnostics as AstDiagnostic[] | undefined;
|
|
81
81
|
if (!diagnostics || diagnostics.length === 0) {
|
|
82
82
|
continue;
|
|
83
83
|
}
|