@hydranium/core 1.0.0-next.282 → 1.0.0-next.284
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/documents/ast-document-manager.d.ts +5 -3
- package/lib/documents/ast-document-manager.d.ts.map +1 -1
- package/lib/documents/ast-document-manager.js +3 -2
- package/lib/documents/ast-document-manager.js.map +1 -1
- package/lib/langium/model-service/client-session.d.ts +5 -3
- package/lib/langium/model-service/client-session.d.ts.map +1 -1
- package/lib/langium/model-service/client-session.js.map +1 -1
- package/lib/langium/model-service/model-service.d.ts +12 -2
- package/lib/langium/model-service/model-service.d.ts.map +1 -1
- package/lib/langium/model-service/model-service.js +12 -5
- package/lib/langium/model-service/model-service.js.map +1 -1
- package/lib/langium/naming/name-provider.d.ts +15 -1
- package/lib/langium/naming/name-provider.d.ts.map +1 -1
- package/lib/langium/naming/name-provider.js +6 -1
- package/lib/langium/naming/name-provider.js.map +1 -1
- package/lib/langium/trivia/document-ending-preserver.d.ts +11 -3
- package/lib/langium/trivia/document-ending-preserver.d.ts.map +1 -1
- package/lib/langium/trivia/document-ending-preserver.js +20 -6
- package/lib/langium/trivia/document-ending-preserver.js.map +1 -1
- package/lib/langium/trivia/trivia-preserver.d.ts +3 -1
- package/lib/langium/trivia/trivia-preserver.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts +11 -10
- package/lib/langium/workspace/hydranium-workspace-manager.d.ts.map +1 -1
- package/lib/langium/workspace/hydranium-workspace-manager.js +13 -13
- package/lib/langium/workspace/hydranium-workspace-manager.js.map +1 -1
- package/lib/langium/workspace/virtual-document.d.ts +6 -6
- package/lib/langium/workspace/virtual-document.js +6 -6
- package/lib/testing/node/lsp-server-connection.d.ts +7 -1
- package/lib/testing/node/lsp-server-connection.d.ts.map +1 -1
- package/lib/testing/node/lsp-server-connection.js +4 -0
- package/lib/testing/node/lsp-server-connection.js.map +1 -1
- package/lib/testing/run-update-pipeline.d.ts +4 -2
- package/lib/testing/run-update-pipeline.d.ts.map +1 -1
- package/lib/testing/run-update-pipeline.js.map +1 -1
- package/package.json +5 -5
- package/src/documents/ast-document-manager.ts +5 -3
- package/src/langium/model-service/client-session.ts +5 -3
- package/src/langium/model-service/model-service.ts +16 -6
- package/src/langium/naming/name-provider.ts +15 -1
- package/src/langium/trivia/document-ending-preserver.ts +22 -6
- package/src/langium/trivia/trivia-preserver.ts +3 -1
- package/src/langium/workspace/hydranium-workspace-manager.ts +13 -13
- package/src/langium/workspace/virtual-document.ts +6 -6
- package/src/testing/node/lsp-server-connection.ts +7 -1
- package/src/testing/run-update-pipeline.ts +4 -2
|
@@ -221,6 +221,11 @@ export interface NameProvider extends LangiumNameProvider {
|
|
|
221
221
|
* walk traverses through the document root: if the root has its own
|
|
222
222
|
* name, that name IS part of the qualified form. Adopters who want a
|
|
223
223
|
* different boundary override this method.
|
|
224
|
+
*
|
|
225
|
+
* May answer as of its first read since the last build: the default
|
|
226
|
+
* memoises per built node, so a name changed in memory after that read
|
|
227
|
+
* reads as the old one until its document rebuilds. A node in no document
|
|
228
|
+
* is named afresh on every call.
|
|
224
229
|
*/
|
|
225
230
|
getDocumentQualifiedName(node?: AstNode): string | undefined;
|
|
226
231
|
|
|
@@ -236,6 +241,10 @@ export interface NameProvider extends LangiumNameProvider {
|
|
|
236
241
|
* string) or no project owns the URI — in those cases project-
|
|
237
242
|
* qualified equals document-qualified, and
|
|
238
243
|
* `HydraniumScopeComputation` skips the multi-tier emit.
|
|
244
|
+
*
|
|
245
|
+
* The default memoises it as it does {@link getDocumentQualifiedName}, and
|
|
246
|
+
* for a node in no document answers the document-qualified name: there is
|
|
247
|
+
* no project to prefix.
|
|
239
248
|
*/
|
|
240
249
|
getProjectQualifiedName(node?: AstNode): string | undefined;
|
|
241
250
|
|
|
@@ -326,6 +335,9 @@ export class DefaultNameProvider implements NameProvider {
|
|
|
326
335
|
* document's lifetime. Evicted automatically when Langium rebuilds
|
|
327
336
|
* the document.
|
|
328
337
|
*
|
|
338
|
+
* So a built node renamed in memory reads its old name until the rebuild,
|
|
339
|
+
* as {@link NameProvider.getDocumentQualifiedName} warns.
|
|
340
|
+
*
|
|
329
341
|
* Only cached when the computed result is a defined string — `undefined`
|
|
330
342
|
* results re-evaluate on each call (cheap path: `getOwnName` returns
|
|
331
343
|
* undefined for unnamed nodes, no chain walk needed).
|
|
@@ -334,7 +346,9 @@ export class DefaultNameProvider implements NameProvider {
|
|
|
334
346
|
/**
|
|
335
347
|
* Per-document memo for {@link getProjectQualifiedName} results.
|
|
336
348
|
* Sibling of {@link documentQualifiedNameCache}; covers the additional
|
|
337
|
-
* project-reference-name lookup the project-qualified variant performs
|
|
349
|
+
* project-reference-name lookup the project-qualified variant performs,
|
|
350
|
+
* and goes stale on an in-memory rename the same way, as
|
|
351
|
+
* {@link NameProvider.getDocumentQualifiedName} warns.
|
|
338
352
|
*
|
|
339
353
|
* **Project-rename caveat.** If `Project.referenceName` mutates without
|
|
340
354
|
* a corresponding document rebuild, cached entries become stale. Adopters
|
|
@@ -18,20 +18,31 @@ import { type TriviaPreserver } from './trivia-preserver.js';
|
|
|
18
18
|
* restyle.** A document may legitimately end in blank lines, or deliberately in
|
|
19
19
|
* none, and a write that changed one field is not the place to decide it should
|
|
20
20
|
* not. A serializer cannot make the call either — it has no prior file to
|
|
21
|
-
* consult — which is why the decision lives on the write path.
|
|
21
|
+
* consult — which is why the decision lives on the write path. Only a blank
|
|
22
|
+
* document, created or existing, has no ending to keep and takes
|
|
23
|
+
* {@link defaultEnding}.
|
|
22
24
|
*
|
|
23
25
|
* Line terminators are matched to the body the serializer emits, which is the
|
|
24
26
|
* one thing here that cannot be preserved: keeping them verbatim leaves a
|
|
25
|
-
* rewritten CRLF document with its only CRLFs at the very end.
|
|
27
|
+
* rewritten CRLF document with its only CRLFs at the very end. A body with no
|
|
28
|
+
* line break has none to match, so its ending stays as the file had it.
|
|
26
29
|
*/
|
|
27
30
|
export class DocumentEndingPreserver implements TriviaPreserver<string> {
|
|
31
|
+
/** Default {@link defaultEnding}. */
|
|
32
|
+
static readonly DEFAULT_ENDING = '\n';
|
|
33
|
+
|
|
28
34
|
readonly id = 'document-ending';
|
|
29
35
|
readonly label = 'Document Ending';
|
|
30
36
|
|
|
37
|
+
/** The ending of a blank document, which a created one is: it has none of its own to keep. */
|
|
38
|
+
protected readonly defaultEnding: string = DocumentEndingPreserver.DEFAULT_ENDING;
|
|
39
|
+
|
|
31
40
|
extract(document: LangiumDocument): string {
|
|
32
41
|
const source = document.textDocument.getText();
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
if (source.trim().length === 0) {
|
|
43
|
+
return this.defaultEnding;
|
|
44
|
+
}
|
|
45
|
+
return source.slice(source.trimEnd().length);
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
/**
|
|
@@ -40,10 +51,15 @@ export class DocumentEndingPreserver implements TriviaPreserver<string> {
|
|
|
40
51
|
* tell a blank line the serializer emitted as part of a value from one that
|
|
41
52
|
* is merely the end of the file, so it removes both and restores the ending
|
|
42
53
|
* the old document had. A document whose final value just gained trailing
|
|
43
|
-
* blank lines therefore loses them.
|
|
54
|
+
* blank lines therefore loses them. An override also takes over matching the
|
|
55
|
+
* ending's line terminators to the body's.
|
|
44
56
|
*/
|
|
45
57
|
apply(serialized: string, trivia: string): string {
|
|
46
|
-
|
|
58
|
+
const body = serialized.trimEnd();
|
|
59
|
+
// The body's first line break, since a value may hold the other kind; with none
|
|
60
|
+
// there is no terminator to match, so the ending stays as extracted.
|
|
61
|
+
const terminator = /\r?\n/.exec(body)?.[0];
|
|
62
|
+
return body + (terminator === undefined ? trivia : trivia.replace(/\r?\n/g, terminator));
|
|
47
63
|
}
|
|
48
64
|
}
|
|
49
65
|
|
|
@@ -26,7 +26,9 @@ import { type RegistryItem } from '../../util/registry.js';
|
|
|
26
26
|
export interface TriviaPreserver<T = unknown> extends RegistryItem {
|
|
27
27
|
/**
|
|
28
28
|
* Take this preserver's trivia off `document`, while it still holds the text
|
|
29
|
-
* the write is about to replace.
|
|
29
|
+
* the write is about to replace. For a document being created, or one the
|
|
30
|
+
* store holds no text for, `document` is parsed from empty text, so its root
|
|
31
|
+
* may carry parser errors; a preserver answers its default for it.
|
|
30
32
|
*/
|
|
31
33
|
extract(document: LangiumDocument): T;
|
|
32
34
|
|
|
@@ -257,17 +257,18 @@ export class HydraniumWorkspaceManager extends DefaultWorkspaceManager {
|
|
|
257
257
|
* Warn when a seeded additional document's URI matches no registered
|
|
258
258
|
* language.
|
|
259
259
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
* this: the document exists, so no lookup errors, it is simply inert.
|
|
260
|
+
* Every per-language service is resolved per URI, so the workspace cannot
|
|
261
|
+
* load such a document: adding it throws, and the startup build fails
|
|
262
|
+
* with an error naming only the extension, not the document. This
|
|
263
|
+
* line names the document and the fix first.
|
|
265
264
|
*
|
|
266
|
-
* The usual cause is a `
|
|
267
|
-
* `virtualUri
|
|
268
|
-
* nothing to match on —
|
|
269
|
-
*
|
|
270
|
-
*
|
|
265
|
+
* The usual cause is a `fromModel` or `fromStringInLanguage` document
|
|
266
|
+
* under a `virtualUri` with no file extension: `virtualUri(contributor)`
|
|
267
|
+
* yields `virtual:<contributor>`, and routing has nothing to match on —
|
|
268
|
+
* there is no extension, and a virtual document is never open in
|
|
269
|
+
* `TextDocuments`, so the declared-languageId rung cannot serve it either.
|
|
270
|
+
* A `fromString` document under such a URI never gets here: its parse
|
|
271
|
+
* throws first. The convention is to end the URI with a registered
|
|
271
272
|
* extension by passing it as the last segment,
|
|
272
273
|
* `virtualUri(contributor, 'name.<ext>')`.
|
|
273
274
|
*/
|
|
@@ -277,9 +278,8 @@ export class HydraniumWorkspaceManager extends DefaultWorkspaceManager {
|
|
|
277
278
|
}
|
|
278
279
|
const extensions = this.languageRegistry.all.flatMap(language => language.LanguageMetaData.fileExtensions);
|
|
279
280
|
this.tracer.warn(
|
|
280
|
-
`Additional document '${document.uri.toString()}' matches no registered language, so
|
|
281
|
-
|
|
282
|
-
`contribute nothing to scopes. End its URI with a registered file extension (${extensions.join(', ')}); ` +
|
|
281
|
+
`Additional document '${document.uri.toString()}' matches no registered language, so the workspace ` +
|
|
282
|
+
`cannot load it and the startup build fails. End its URI with a registered file extension (${extensions.join(', ')}); ` +
|
|
283
283
|
'for a virtual document that means passing it as the last segment, `virtualUri(contributor, "name.<ext>")`.'
|
|
284
284
|
);
|
|
285
285
|
}
|
|
@@ -45,12 +45,12 @@ export const VIRTUAL_SCHEME = 'virtual';
|
|
|
45
45
|
* `virtual:<contributor>`, which has no extension and so matches no registered
|
|
46
46
|
* language — a trap. Every per-language service is resolved per URI, and a
|
|
47
47
|
* virtual document is never open in `TextDocuments`, so the declared-languageId
|
|
48
|
-
* lookup rung cannot serve it either
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
48
|
+
* lookup rung cannot serve it either, and the startup build fails. A
|
|
49
|
+
* `fromString` document throws while parsing; a `fromModel` or
|
|
50
|
+
* `fromStringInLanguage` document is built, and throws as it is added to the
|
|
51
|
+
* workspace, after `HydraniumWorkspaceManager` warns that it matches no
|
|
52
|
+
* language. Pass the extension as the last segment,
|
|
53
|
+
* `virtualUri(contributor, 'name.<ext>')`.
|
|
54
54
|
*
|
|
55
55
|
* # Relationship to the `$synthetic` node marker
|
|
56
56
|
*
|
|
@@ -155,7 +155,9 @@ export interface LspServerConnection extends Harness {
|
|
|
155
155
|
* Seam: the server-side `Connection`, NOT yet attached to a server. The caller
|
|
156
156
|
* composes the services tree onto it (`createXxxServices({ connection })`) and
|
|
157
157
|
* calls `startLanguageServer(services.shared)`, which wires the LSP handlers
|
|
158
|
-
* and calls `connection.listen()
|
|
158
|
+
* and calls `connection.listen()`; a caller recording only applied edits
|
|
159
|
+
* skips `startLanguageServer` but still calls `listen()`, as
|
|
160
|
+
* {@link makeLspServerConnection} says.
|
|
159
161
|
*/
|
|
160
162
|
readonly serverConnection: Connection;
|
|
161
163
|
/** Seam: the raw client connection over the in-memory wire; escape hatch for unwrapped requests. */
|
|
@@ -333,6 +335,10 @@ function makeCountingStreamPair(): {
|
|
|
333
335
|
* (it owns the `createServices` callback + `startLanguageServer` + `buildWorkspace`);
|
|
334
336
|
* use this primitive directly when the CALLER must own the tree — e.g. to attach
|
|
335
337
|
* additional protocol heads (data-server, GLSP) onto the same `shared` services.
|
|
338
|
+
*
|
|
339
|
+
* To record only {@link LspServerConnection.appliedEdits}, skip
|
|
340
|
+
* `startLanguageServer`, call `serverConnection.listen()`, and open through
|
|
341
|
+
* `TextDocuments.notifyDidOpenTextDocument`.
|
|
336
342
|
*/
|
|
337
343
|
export function makeLspServerConnection(): LspServerConnection {
|
|
338
344
|
const pair = makeCountingStreamPair();
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Driver for the structured-write transform chain — the rewrite registry plus
|
|
12
|
-
* the serializer —
|
|
12
|
+
* the serializer — answering the bare serializer output, without the trivia
|
|
13
|
+
* and `ModelService` overrides a session's write applies: call
|
|
14
|
+
* `ModelService.modelToText` with a canonical URI for that text.
|
|
13
15
|
*
|
|
14
16
|
* # Why a driver and not a documented recipe
|
|
15
17
|
*
|
|
@@ -91,7 +93,7 @@ export interface UpdatePipelineResult<TTransfer extends TransferElement = Transf
|
|
|
91
93
|
* surface but it makes the precise assertion unavailable.
|
|
92
94
|
*/
|
|
93
95
|
readonly rewritten: TTransfer;
|
|
94
|
-
/** The
|
|
96
|
+
/** The serializer's text, without the trivia a session's `update` carries over. */
|
|
95
97
|
readonly text: string;
|
|
96
98
|
}
|
|
97
99
|
|