@hydranium/core 1.0.0-next.95 → 1.0.0-next.97
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/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/langium/integrity/integrity-service.d.ts.map +1 -1
- package/lib/langium/integrity/integrity-service.js +8 -1
- package/lib/langium/integrity/integrity-service.js.map +1 -1
- package/lib/langium/language-module.d.ts +25 -0
- package/lib/langium/language-module.d.ts.map +1 -1
- package/lib/langium/language-module.js +14 -0
- package/lib/langium/language-module.js.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +30 -18
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +41 -1
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/residency/cst-residency-service.d.ts +4 -6
- package/lib/langium/residency/cst-residency-service.d.ts.map +1 -1
- package/lib/langium/residency/cst-residency-service.js.map +1 -1
- package/lib/langium/serialization/abstract-serializer.d.ts +11 -20
- package/lib/langium/serialization/abstract-serializer.d.ts.map +1 -1
- package/lib/langium/serialization/abstract-serializer.js +12 -21
- package/lib/langium/serialization/abstract-serializer.js.map +1 -1
- package/lib/langium/trivia/comment-preserver.d.ts +423 -0
- package/lib/langium/trivia/comment-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/comment-preserver.js +906 -0
- package/lib/langium/trivia/comment-preserver.js.map +1 -0
- package/lib/langium/trivia/document-ending-preserver.d.ts +43 -0
- package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/document-ending-preserver.js +48 -0
- package/lib/langium/trivia/document-ending-preserver.js.map +1 -0
- package/lib/langium/trivia/index.d.ts +14 -0
- package/lib/langium/trivia/index.d.ts.map +1 -0
- package/lib/langium/trivia/index.js +14 -0
- package/lib/langium/trivia/index.js.map +1 -0
- package/lib/langium/trivia/trivia-contribution.d.ts +37 -0
- package/lib/langium/trivia/trivia-contribution.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-contribution.js +10 -0
- package/lib/langium/trivia/trivia-contribution.js.map +1 -0
- package/lib/langium/trivia/trivia-preserver.d.ts +50 -0
- package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-preserver.js +10 -0
- package/lib/langium/trivia/trivia-preserver.js.map +1 -0
- package/lib/langium/trivia/trivia-service.d.ts +70 -0
- package/lib/langium/trivia/trivia-service.d.ts.map +1 -0
- package/lib/langium/trivia/trivia-service.js +56 -0
- package/lib/langium/trivia/trivia-service.js.map +1 -0
- package/lib/testing/node/scratch-workspace.js +2 -2
- package/package.json +5 -5
- package/src/index.ts +1 -0
- package/src/langium/integrity/integrity-service.ts +9 -1
- package/src/langium/language-module.ts +38 -0
- package/src/langium/model-service/model-service.ts +58 -19
- package/src/langium/residency/cst-residency-service.ts +4 -6
- package/src/langium/serialization/abstract-serializer.ts +13 -32
- package/src/langium/trivia/comment-preserver.ts +1100 -0
- package/src/langium/trivia/document-ending-preserver.ts +55 -0
- package/src/langium/trivia/index.ts +14 -0
- package/src/langium/trivia/trivia-contribution.ts +39 -0
- package/src/langium/trivia/trivia-preserver.ts +54 -0
- package/src/langium/trivia/trivia-service.ts +99 -0
- package/src/testing/node/scratch-workspace.ts +2 -2
|
@@ -134,6 +134,10 @@ export interface ModelServiceOptions extends LogNameOptions {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
|
+
* The seam every non-LSP head talks to: the data server, the GLSP head and an
|
|
138
|
+
* adopter's own services reach documents through this slot rather than through
|
|
139
|
+
* the workspace stores.
|
|
140
|
+
*
|
|
137
141
|
* In-process facade over the framework's document plumbing
|
|
138
142
|
* (`HydraniumTextDocuments`, `LangiumDocuments`,
|
|
139
143
|
* `DocumentBuilder`, `WritableFileSystemProvider`). Owns the
|
|
@@ -192,24 +196,6 @@ export interface ModelServiceOptions extends LogNameOptions {
|
|
|
192
196
|
* by URI happens here so consumers can subscribe per-document without
|
|
193
197
|
* implementing the URI gate at each callsite.
|
|
194
198
|
*
|
|
195
|
-
* **Generic parameters.**
|
|
196
|
-
* - `TAst` — the AST root type each consumer expects on the returned
|
|
197
|
-
* {@link AstDocument}. Constrained to {@link AstNode}.
|
|
198
|
-
* - `TDiagnostic` — the AST-layer diagnostic: whatever the build left on
|
|
199
|
-
* `LangiumDocument.diagnostics`, carried through on the returned
|
|
200
|
-
* {@link AstDocument}. Defaults to {@link AstDiagnostic}.
|
|
201
|
-
* **Not the `TransferEncoder`'s parameter of the same name**, which is
|
|
202
|
-
* that encoder's OUTPUT and so names the wire shape. This one names its
|
|
203
|
-
* input, and an adopter binds the two to different types.
|
|
204
|
-
* - `TTransfer` — transfer-model root accepted by `update` / `save`
|
|
205
|
-
* args. Constrained to {@link TransferElement}. Defaults to the
|
|
206
|
-
* structural base.
|
|
207
|
-
*/
|
|
208
|
-
/**
|
|
209
|
-
* The seam every non-LSP head talks to: the data server, the GLSP head and an
|
|
210
|
-
* adopter's own services reach documents through this slot rather than through
|
|
211
|
-
* the workspace stores.
|
|
212
|
-
*
|
|
213
199
|
* **Two families, and the distinction matters more than the names suggest.**
|
|
214
200
|
* `waitFor*` is a pure wait — it never triggers a build, so a caller waiting on
|
|
215
201
|
* a document no build has touched waits until something else builds it.
|
|
@@ -232,6 +218,19 @@ export interface ModelServiceOptions extends LogNameOptions {
|
|
|
232
218
|
* direction is the safe one: the alternative permits stale reads silently. A
|
|
233
219
|
* member taking a phase as a PARAMETER cannot judge statically and so returns
|
|
234
220
|
* `TDiagnostic`, leaving the choice to the caller.
|
|
221
|
+
*
|
|
222
|
+
* **Generic parameters.**
|
|
223
|
+
* - `TAst` — the AST root type each consumer expects on the returned
|
|
224
|
+
* {@link AstDocument}. Constrained to {@link AstNode}.
|
|
225
|
+
* - `TDiagnostic` — the AST-layer diagnostic: whatever the build left on
|
|
226
|
+
* `LangiumDocument.diagnostics`, carried through on the returned
|
|
227
|
+
* {@link AstDocument}. Defaults to {@link AstDiagnostic}.
|
|
228
|
+
* **Not the `TransferEncoder`'s parameter of the same name**, which is
|
|
229
|
+
* that encoder's OUTPUT and so names the wire shape. This one names its
|
|
230
|
+
* input, and an adopter binds the two to different types.
|
|
231
|
+
* - `TTransfer` — transfer-model root accepted by `update` / `save`
|
|
232
|
+
* args. Constrained to {@link TransferElement}. Defaults to the
|
|
233
|
+
* structural base.
|
|
235
234
|
*/
|
|
236
235
|
export interface ModelService<
|
|
237
236
|
TAst extends AstNode,
|
|
@@ -1175,7 +1174,47 @@ export class DefaultModelService<
|
|
|
1175
1174
|
return model;
|
|
1176
1175
|
}
|
|
1177
1176
|
const rewritten = await this.rewriteModel(uri, model, cancelToken);
|
|
1178
|
-
|
|
1177
|
+
const target = UriUtils.toUri(uri);
|
|
1178
|
+
const trivia = this.services.ServiceRegistry?.getServices(target)?.trivia?.TriviaService;
|
|
1179
|
+
// Extracted BEFORE serializing, so it reads the document the write is
|
|
1180
|
+
// about to replace rather than whatever a concurrent build left behind.
|
|
1181
|
+
let document = this.services.workspace.LangiumDocuments.getDocument(target);
|
|
1182
|
+
if (trivia !== undefined && document === undefined) {
|
|
1183
|
+
const source = await this.textToTakeTriviaFrom(uri, target);
|
|
1184
|
+
if (source !== undefined) {
|
|
1185
|
+
document = this.services.workspace.LangiumDocumentFactory.fromString(source, target);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
const extracted = trivia !== undefined && document !== undefined ? trivia.extract(document) : undefined;
|
|
1189
|
+
const serialized = await this.serialize(uri, rewritten);
|
|
1190
|
+
return extracted === undefined ? serialized : trivia!.apply(serialized, extracted, target);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* The text a write into an unloaded document should take its trivia from —
|
|
1195
|
+
* the open editor's if one holds it, the file's otherwise, and `undefined`
|
|
1196
|
+
* when neither can supply it.
|
|
1197
|
+
*
|
|
1198
|
+
* **A read that fails answers `undefined` rather than throwing.** `getDocument`
|
|
1199
|
+
* is a store read, so an ordinary create answers nothing here and must still
|
|
1200
|
+
* write; letting a permission error or a path that turned into a directory
|
|
1201
|
+
* escape would fail the user's write instead, having found nothing to preserve
|
|
1202
|
+
* in a file the write is about to replace anyway. Losing the comments is
|
|
1203
|
+
* recoverable, and traced; losing the write is neither.
|
|
1204
|
+
*/
|
|
1205
|
+
protected async textToTakeTriviaFrom(uri: string, target: URI): Promise<string | undefined> {
|
|
1206
|
+
const open = this.services.workspace.TextDocuments.get(uri)?.getText();
|
|
1207
|
+
if (open !== undefined) {
|
|
1208
|
+
return open;
|
|
1209
|
+
}
|
|
1210
|
+
const fileSystem = this.services.workspace.FileSystemProvider;
|
|
1211
|
+
try {
|
|
1212
|
+
return (await fileSystem.exists(target)) ? await fileSystem.readFile(target) : undefined;
|
|
1213
|
+
} catch (err: unknown) {
|
|
1214
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1215
|
+
this.tracer.withUri(uri).debug(`Could not read the file to take trivia from; writing as emitted. ${detail}`);
|
|
1216
|
+
return undefined;
|
|
1217
|
+
}
|
|
1179
1218
|
}
|
|
1180
1219
|
|
|
1181
1220
|
/**
|
|
@@ -185,8 +185,10 @@ function rehydrateCst(document: LangiumDocument, factory: LangiumDocumentFactory
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
188
|
+
* Keeps a document's CST available to the readers that need one, shedding it
|
|
189
|
+
* for documents that have gone idle and re-grafting on demand.
|
|
190
|
+
*
|
|
191
|
+
* Each build refreshes
|
|
190
192
|
* the idle window of the documents in its batch; when a closed, sheddable
|
|
191
193
|
* document's window elapses, every AST node's `$cstNode` and every reference's
|
|
192
194
|
* `$refNode` are nulled, and the CST root, `Range`s and `Position`s become
|
|
@@ -231,10 +233,6 @@ function rehydrateCst(document: LangiumDocument, factory: LangiumDocumentFactory
|
|
|
231
233
|
* `NameProvider.getNameNode` chokepoint, the comment provider, and
|
|
232
234
|
* `HydraniumLangiumDocuments.getOrCreateDocument`.
|
|
233
235
|
*/
|
|
234
|
-
/**
|
|
235
|
-
* Keeps a document's CST available to the readers that need one, shedding it
|
|
236
|
-
* for documents that have gone idle and re-grafting on demand.
|
|
237
|
-
*/
|
|
238
236
|
export interface CstResidencyService {
|
|
239
237
|
/**
|
|
240
238
|
* Ensure `document` has a CST, re-parsing its retained text if it was shed.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { type MaybePromise, type Tracer, type TransferElement } from '@hydranium/protocol';
|
|
11
11
|
import { type AstNode, type AstReflection, type GenericAstNode, isAstNode } from '@hydranium/langium';
|
|
12
12
|
import { type LogNameOptions } from '../diagnostics/logger.js';
|
|
13
13
|
import { type HydraniumLanguageServices } from '../language-module.js';
|
|
@@ -66,16 +66,6 @@ export interface AbstractSerializerOptions extends LogNameOptions {
|
|
|
66
66
|
* the extra property carries a value.
|
|
67
67
|
*/
|
|
68
68
|
readonly referenceWrapperTypes?: ReadonlyMap<string, string>;
|
|
69
|
-
/**
|
|
70
|
-
* Whether {@link AbstractSerializer.serializeAst} trims trailing whitespace
|
|
71
|
-
* from the final output (leading whitespace is always trimmed). Defaults to
|
|
72
|
-
* `true` — the conventional "no trailing blank lines" document shape. Set to
|
|
73
|
-
* `false` for grammars whose values may legitimately END the document with
|
|
74
|
-
* blank lines that must survive a round-trip — e.g. a multi-line block-scalar
|
|
75
|
-
* property (see `YamlSerializerOptions.blockScalarProperties`),
|
|
76
|
-
* where a trailing-trim would silently eat user-typed blank lines.
|
|
77
|
-
*/
|
|
78
|
-
readonly trimTrailingWhitespace?: boolean;
|
|
79
69
|
}
|
|
80
70
|
|
|
81
71
|
/**
|
|
@@ -139,29 +129,20 @@ export abstract class AbstractSerializer<
|
|
|
139
129
|
* Entry point for AST-shape inputs. Typically the grammar root, but any
|
|
140
130
|
* AST node works — the walker is recursive and shape-agnostic.
|
|
141
131
|
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
132
|
+
* **Emits the model and nothing else — do not trim here.** Trailing
|
|
133
|
+
* whitespace belongs to the author, and a write path holding the prior
|
|
134
|
+
* document restores it from there; trimming at this end deletes it before
|
|
135
|
+
* that restore sees it. A serializer has no prior document to consult, so it
|
|
136
|
+
* cannot tell a deliberate blank line from an accidental one.
|
|
137
|
+
*
|
|
138
|
+
* **Nothing trims what a subclass emits**, at either end. Emitting leading
|
|
139
|
+
* whitespace therefore puts it in the file, where no write path removes it.
|
|
140
|
+
*
|
|
141
|
+
* Returns whatever `serializeNode` returns, so a fully sync subclass keeps a
|
|
142
|
+
* bare `string` with no promise allocation.
|
|
146
143
|
*/
|
|
147
144
|
serializeAst(model: TAst): MaybePromise<string> {
|
|
148
|
-
|
|
149
|
-
if (isPromiseLike(result)) {
|
|
150
|
-
return result.then(text => this.trimSerialized(text));
|
|
151
|
-
}
|
|
152
|
-
return this.trimSerialized(result);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Trim the final serialized output. Leading whitespace is always removed;
|
|
157
|
-
* trailing whitespace is removed unless {@link AbstractSerializerOptions.trimTrailingWhitespace}
|
|
158
|
-
* is `false` (which preserves document-ending blank lines, e.g. inside a block scalar).
|
|
159
|
-
*/
|
|
160
|
-
protected trimSerialized(text: string | undefined): string {
|
|
161
|
-
if (text === undefined) {
|
|
162
|
-
return '';
|
|
163
|
-
}
|
|
164
|
-
return this.options.trimTrailingWhitespace === false ? text.trimStart() : text.trim();
|
|
145
|
+
return this.serializeNode(model, 0);
|
|
165
146
|
}
|
|
166
147
|
|
|
167
148
|
/**
|