@hydranium/core 1.0.0-next.23 → 1.0.0-next.25
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/langium/document-builder/build-session.d.ts +93 -0
- package/lib/langium/document-builder/build-session.d.ts.map +1 -0
- package/lib/langium/document-builder/build-session.js +72 -0
- package/lib/langium/document-builder/build-session.js.map +1 -0
- package/lib/langium/document-builder/document-builder.d.ts +117 -6
- package/lib/langium/document-builder/document-builder.d.ts.map +1 -1
- package/lib/langium/document-builder/document-builder.js +230 -9
- package/lib/langium/document-builder/document-builder.js.map +1 -1
- package/lib/langium/document-builder/index.d.ts +1 -0
- package/lib/langium/document-builder/index.d.ts.map +1 -1
- package/lib/langium/document-builder/index.js +1 -0
- package/lib/langium/document-builder/index.js.map +1 -1
- package/lib/langium/module.d.ts +7 -0
- package/lib/langium/module.d.ts.map +1 -1
- package/lib/langium/module.js +5 -0
- package/lib/langium/module.js.map +1 -1
- package/lib/langium/residency/cst-residency-service.d.ts +1 -1
- package/lib/langium/residency/cst-residency-service.js +1 -1
- package/lib/langium/shared-services.d.ts +2 -0
- package/lib/langium/shared-services.d.ts.map +1 -1
- package/lib/langium/shared-services.js.map +1 -1
- package/lib/langium/validation/document-validator.d.ts +17 -4
- package/lib/langium/validation/document-validator.d.ts.map +1 -1
- package/lib/langium/validation/document-validator.js.map +1 -1
- package/lib/langium/workspace/document-uri-policy.d.ts +3 -4
- package/lib/langium/workspace/document-uri-policy.d.ts.map +1 -1
- package/lib/langium/workspace/document-uri-policy.js +3 -4
- package/lib/langium/workspace/document-uri-policy.js.map +1 -1
- package/lib/langium/workspace/langium-documents.d.ts +44 -17
- package/lib/langium/workspace/langium-documents.d.ts.map +1 -1
- package/lib/langium/workspace/langium-documents.js +45 -28
- package/lib/langium/workspace/langium-documents.js.map +1 -1
- package/package.json +5 -5
- package/src/langium/document-builder/build-session.ts +87 -0
- package/src/langium/document-builder/document-builder.ts +269 -9
- package/src/langium/document-builder/index.ts +1 -0
- package/src/langium/module.ts +11 -0
- package/src/langium/residency/cst-residency-service.ts +1 -1
- package/src/langium/shared-services.ts +6 -0
- package/src/langium/validation/document-validator.ts +17 -4
- package/src/langium/workspace/document-uri-policy.ts +3 -4
- package/src/langium/workspace/langium-documents.ts +55 -33
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-validator.js","sourceRoot":"","sources":["../../../src/langium/validation/document-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAgD,eAAe,EAAe,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACrI,OAAO,EAGJ,wBAAwB,EAO1B,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"document-validator.js","sourceRoot":"","sources":["../../../src/langium/validation/document-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -91,10 +91,9 @@ export declare class DefaultDocumentUriPolicy implements DocumentUriPolicy {
|
|
|
91
91
|
/**
|
|
92
92
|
* No filesystem access, so existence cannot be checked — every URI is
|
|
93
93
|
* treated as loadable and returned unchanged. A later load that misses
|
|
94
|
-
* therefore reaches the filesystem and throws, which
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* is what makes a missing file a placeholder rather than an error.
|
|
94
|
+
* therefore reaches the filesystem and throws, which reports the miss with
|
|
95
|
+
* the reason the read failed. Under a policy that CAN check, the miss is
|
|
96
|
+
* caught earlier and reported without one.
|
|
98
97
|
*/
|
|
99
98
|
loadUri(uri: URI | string): URI | undefined;
|
|
100
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-uri-policy.d.ts","sourceRoot":"","sources":["../../../src/langium/workspace/document-uri-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,WAAW,yBAAyB;IACvC,QAAQ,CAAC,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,SAAS,CAAC;CACvC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,EAAE,EAAE,yBAAyB,GAAG,GAAG,GAAG,SAAS,CAIxG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,WAAW,iBAAiB;IAC/B,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,YAAY,CAAC;IAC9C;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,iBAAiB;IAC/D,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,YAAY;IAM7C
|
|
1
|
+
{"version":3,"file":"document-uri-policy.d.ts","sourceRoot":"","sources":["../../../src/langium/workspace/document-uri-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,WAAW,yBAAyB;IACvC,QAAQ,CAAC,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,GAAG,SAAS,CAAC;CACvC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,EAAE,EAAE,EAAE,yBAAyB,GAAG,GAAG,GAAG,SAAS,CAIxG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,WAAW,iBAAiB;IAC/B,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,YAAY,CAAC;IAC9C;;;;OAIG;IACH,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,iBAAiB;IAC/D,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,YAAY;IAM7C;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,SAAS;CAG7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,yBAA0B,SAAQ,wBAAwB;IACpE,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,yBAAyB,CAAC;gBAErC,QAAQ,EAAE,2BAA2B;IAKxC,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,YAAY;IAQ7C,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,SAAS;CAStD"}
|
|
@@ -42,10 +42,9 @@ export class DefaultDocumentUriPolicy {
|
|
|
42
42
|
/**
|
|
43
43
|
* No filesystem access, so existence cannot be checked — every URI is
|
|
44
44
|
* treated as loadable and returned unchanged. A later load that misses
|
|
45
|
-
* therefore reaches the filesystem and throws, which
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* is what makes a missing file a placeholder rather than an error.
|
|
45
|
+
* therefore reaches the filesystem and throws, which reports the miss with
|
|
46
|
+
* the reason the read failed. Under a policy that CAN check, the miss is
|
|
47
|
+
* caught earlier and reported without one.
|
|
49
48
|
*/
|
|
50
49
|
loadUri(uri) {
|
|
51
50
|
return UriUtils.toUri(uri);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-uri-policy.js","sourceRoot":"","sources":["../../../src/langium/workspace/document-uri-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAqB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAY,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAexD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAiB,EAAE,EAA6B;IACpF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,IAAI,IAAI,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1F,CAAC;AAgDD;;;;;GAKG;AACH,MAAM,OAAO,wBAAwB;IAClC,YAAY,CAAC,GAAiB;QAC3B,yEAAyE;QACzE,6EAA6E;QAC7E,OAAO,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"document-uri-policy.js","sourceRoot":"","sources":["../../../src/langium/workspace/document-uri-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAqB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAY,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAexD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAiB,EAAE,EAA6B;IACpF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,IAAI,IAAI,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1F,CAAC;AAgDD;;;;;GAKG;AACH,MAAM,OAAO,wBAAwB;IAClC,YAAY,CAAC,GAAiB;QAC3B,yEAAyE;QACzE,6EAA6E;QAC7E,OAAO,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAiB;QACtB,OAAO,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,OAAO,yBAA0B,SAAQ,wBAAwB;IACjD,EAAE,CAA4B;IAEjD,YAAY,QAAqC;QAC9C,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,kBAAkB,CAAC;IACnD,CAAC;IAEQ,YAAY,CAAC,GAAiB;QACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,IAAI,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC;QAClD,yEAAyE;QACzE,uEAAuE;QACvE,OAAO,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACpF,CAAC;IAEQ,OAAO,CAAC,GAAiB;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC;YACrB,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnC,kEAAkE;QAClE,yEAAyE;QACzE,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACrD,CAAC;CACH"}
|
|
@@ -6,13 +6,22 @@
|
|
|
6
6
|
*
|
|
7
7
|
* SPDX-License-Identifier: MIT
|
|
8
8
|
********************************************************************************/
|
|
9
|
-
import { type
|
|
10
|
-
import { type AstNode, DefaultLangiumDocuments, type LangiumDocument, type URI } from '@hydranium/langium';
|
|
9
|
+
import { type AstNode, DefaultLangiumDocuments, type LangiumDocument, type LangiumDocuments, type URI } from '@hydranium/langium';
|
|
11
10
|
import { type ServerSharedServicesMinimal } from '../shared-services.js';
|
|
12
11
|
import { type DocumentUriPolicy } from './document-uri-policy.js';
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* The registry surface the framework adds on top of Langium's
|
|
14
|
+
* {@link LangiumDocuments}. Declared separately from the implementing class so
|
|
15
|
+
* the shared-services slot can narrow to it: the class takes the services tree
|
|
16
|
+
* as its constructor parameter, so naming the CLASS there would make that type
|
|
17
|
+
* depend on itself.
|
|
18
|
+
*/
|
|
19
|
+
export interface HydraniumDocumentRegistry extends LangiumDocuments {
|
|
20
|
+
createEmptyDocument(uri: URI): LangiumDocument<AstNode>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Default `LangiumDocuments` for `@hydranium/core` consumers, extending
|
|
24
|
+
* Langium's {@link DefaultLangiumDocuments} with:
|
|
16
25
|
*
|
|
17
26
|
* 1. **Identity via the {@link DocumentUriPolicy} seam.**
|
|
18
27
|
* `getOrCreateDocument` resolves the requested URI through the seam — the
|
|
@@ -22,26 +31,44 @@ import { type DocumentUriPolicy } from './document-uri-policy.js';
|
|
|
22
31
|
* `LangiumDocuments` override. The default resolves to the URI unchanged
|
|
23
32
|
* (`DefaultDocumentUriPolicy`), matching Langium's own keying.
|
|
24
33
|
*
|
|
25
|
-
* 2.
|
|
26
|
-
* document
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
34
|
+
* 2. **A load that finds nothing rejects**, rather than answering with a
|
|
35
|
+
* transient empty document. An empty AST validates clean, so a placeholder
|
|
36
|
+
* hands the caller something that silently is not the file, and a caller
|
|
37
|
+
* that wants a document for a URI with no content on disk cannot use one
|
|
38
|
+
* anyway: it is unregistered, so no build, index or save can see it, and
|
|
39
|
+
* `LangiumDocumentFactory.update` reads from disk. Content that legitimately
|
|
40
|
+
* lives outside the filesystem belongs in a virtual document, which carries
|
|
41
|
+
* real source text.
|
|
33
42
|
*/
|
|
34
|
-
export declare
|
|
43
|
+
export declare class HydraniumLangiumDocuments extends DefaultLangiumDocuments implements HydraniumDocumentRegistry {
|
|
35
44
|
protected readonly services: ServerSharedServicesMinimal;
|
|
36
45
|
/** Document-identity seam, shared with the text store, event filters, and builder. */
|
|
37
46
|
protected readonly uriPolicy: DocumentUriPolicy;
|
|
38
|
-
protected readonly tracer: Tracer;
|
|
39
47
|
constructor(services: ServerSharedServicesMinimal);
|
|
40
48
|
/**
|
|
41
|
-
* Build a transient
|
|
42
|
-
*
|
|
49
|
+
* Build a transient document for `uri` by parsing empty text with the
|
|
50
|
+
* grammar `uri` routes to, so the root is that language's entry type with
|
|
51
|
+
* every containment list initialised.
|
|
52
|
+
*
|
|
53
|
+
* For callers that need a document at a URI with no content on disk — the
|
|
54
|
+
* scope provider querying before a file exists is the case it was added for.
|
|
55
|
+
* `getOrCreateDocument` deliberately does NOT fall back to this: fabricating
|
|
56
|
+
* a document behind a caller that asked to LOAD one hands back something
|
|
57
|
+
* that silently is not the file, whereas calling this is a caller saying it
|
|
58
|
+
* wants a stand-in.
|
|
59
|
+
*
|
|
60
|
+
* The result is not registered, so nothing downstream can see it, and
|
|
61
|
+
* `LangiumDocumentFactory.update` would read from disk. It is a probe, not a
|
|
62
|
+
* document under construction; content that must survive belongs in a
|
|
63
|
+
* virtual document, which carries real source text.
|
|
64
|
+
*
|
|
65
|
+
* Building the root by hand instead needs the entry type name and a cast
|
|
66
|
+
* past the generated types, and leaves those lists `undefined`. A grammar
|
|
67
|
+
* whose entry rule opens with mandatory syntax yields a parse error here,
|
|
68
|
+
* and it is kept: `AstReflection.isComplete` is `false` for such a root
|
|
69
|
+
* however it is built, so the error states the same thing.
|
|
43
70
|
*/
|
|
44
|
-
|
|
71
|
+
createEmptyDocument(uri: URI): LangiumDocument<AstNode>;
|
|
45
72
|
getOrCreateDocument(uri: URI): Promise<LangiumDocument<AstNode>>;
|
|
46
73
|
}
|
|
47
74
|
//# sourceMappingURL=langium-documents.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langium-documents.d.ts","sourceRoot":"","sources":["../../../src/langium/workspace/langium-documents.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"langium-documents.d.ts","sourceRoot":"","sources":["../../../src/langium/workspace/langium-documents.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAE,KAAK,OAAO,EAAE,uBAAuB,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,KAAK,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAClI,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAChE,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;CAC1D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,yBAA0B,SAAQ,uBAAwB,YAAW,yBAAyB;uBAIhE,QAAQ,EAAE,2BAA2B;IAH7E,sFAAsF;IACtF,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;gBAER,QAAQ,EAAE,2BAA2B;IAK7E;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC;IAMxC,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;CAgCjF"}
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
********************************************************************************/
|
|
9
9
|
import { DefaultLangiumDocuments } from '@hydranium/langium';
|
|
10
10
|
/**
|
|
11
|
-
* Default `LangiumDocuments` for `@hydranium/core` consumers
|
|
12
|
-
*
|
|
11
|
+
* Default `LangiumDocuments` for `@hydranium/core` consumers, extending
|
|
12
|
+
* Langium's {@link DefaultLangiumDocuments} with:
|
|
13
13
|
*
|
|
14
14
|
* 1. **Identity via the {@link DocumentUriPolicy} seam.**
|
|
15
15
|
* `getOrCreateDocument` resolves the requested URI through the seam — the
|
|
@@ -19,25 +19,51 @@ import { DefaultLangiumDocuments } from '@hydranium/langium';
|
|
|
19
19
|
* `LangiumDocuments` override. The default resolves to the URI unchanged
|
|
20
20
|
* (`DefaultDocumentUriPolicy`), matching Langium's own keying.
|
|
21
21
|
*
|
|
22
|
-
* 2.
|
|
23
|
-
* document
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
22
|
+
* 2. **A load that finds nothing rejects**, rather than answering with a
|
|
23
|
+
* transient empty document. An empty AST validates clean, so a placeholder
|
|
24
|
+
* hands the caller something that silently is not the file, and a caller
|
|
25
|
+
* that wants a document for a URI with no content on disk cannot use one
|
|
26
|
+
* anyway: it is unregistered, so no build, index or save can see it, and
|
|
27
|
+
* `LangiumDocumentFactory.update` reads from disk. Content that legitimately
|
|
28
|
+
* lives outside the filesystem belongs in a virtual document, which carries
|
|
29
|
+
* real source text.
|
|
30
30
|
*/
|
|
31
|
-
export class
|
|
31
|
+
export class HydraniumLangiumDocuments extends DefaultLangiumDocuments {
|
|
32
32
|
services;
|
|
33
33
|
/** Document-identity seam, shared with the text store, event filters, and builder. */
|
|
34
34
|
uriPolicy;
|
|
35
|
-
tracer;
|
|
36
35
|
constructor(services) {
|
|
37
36
|
super(services);
|
|
38
37
|
this.services = services;
|
|
39
38
|
this.uriPolicy = services.workspace.DocumentUriPolicy;
|
|
40
|
-
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build a transient document for `uri` by parsing empty text with the
|
|
42
|
+
* grammar `uri` routes to, so the root is that language's entry type with
|
|
43
|
+
* every containment list initialised.
|
|
44
|
+
*
|
|
45
|
+
* For callers that need a document at a URI with no content on disk — the
|
|
46
|
+
* scope provider querying before a file exists is the case it was added for.
|
|
47
|
+
* `getOrCreateDocument` deliberately does NOT fall back to this: fabricating
|
|
48
|
+
* a document behind a caller that asked to LOAD one hands back something
|
|
49
|
+
* that silently is not the file, whereas calling this is a caller saying it
|
|
50
|
+
* wants a stand-in.
|
|
51
|
+
*
|
|
52
|
+
* The result is not registered, so nothing downstream can see it, and
|
|
53
|
+
* `LangiumDocumentFactory.update` would read from disk. It is a probe, not a
|
|
54
|
+
* document under construction; content that must survive belongs in a
|
|
55
|
+
* virtual document, which carries real source text.
|
|
56
|
+
*
|
|
57
|
+
* Building the root by hand instead needs the entry type name and a cast
|
|
58
|
+
* past the generated types, and leaves those lists `undefined`. A grammar
|
|
59
|
+
* whose entry rule opens with mandatory syntax yields a parse error here,
|
|
60
|
+
* and it is kept: `AstReflection.isComplete` is `false` for such a root
|
|
61
|
+
* however it is built, so the error states the same thing.
|
|
62
|
+
*/
|
|
63
|
+
createEmptyDocument(uri) {
|
|
64
|
+
// The two-argument overload is synchronous; passing a cancellation token
|
|
65
|
+
// selects the promise-returning one, which this contract cannot await.
|
|
66
|
+
return this.langiumDocumentFactory.fromString('', uri);
|
|
41
67
|
}
|
|
42
68
|
async getOrCreateDocument(uri) {
|
|
43
69
|
const resolved = this.uriPolicy.loadUri(uri);
|
|
@@ -58,28 +84,19 @@ export class AbstractHydraniumLangiumDocuments extends DefaultLangiumDocuments {
|
|
|
58
84
|
}
|
|
59
85
|
catch (error) {
|
|
60
86
|
// Load lost a race with a concurrent create — return that document.
|
|
87
|
+
// Checked before propagating, so a race is not reported as a
|
|
88
|
+
// missing file; cancellation carries no document and falls through.
|
|
61
89
|
const reentrant = this.getDocument(resolved);
|
|
62
90
|
if (reentrant) {
|
|
63
91
|
this.services.workspace.CstResidencyService.rehydrate(reentrant);
|
|
64
92
|
return reentrant;
|
|
65
93
|
}
|
|
66
|
-
|
|
67
|
-
// below still runs, because the file-not-found case is the ordinary
|
|
68
|
-
// one here and no provider-independent way to recognise it exists:
|
|
69
|
-
// Node's provider throws `ENOENT`, the in-memory one a bare `Error`,
|
|
70
|
-
// and the provider is a seam an adopter rebinds. Discriminating on
|
|
71
|
-
// message text across that seam would be a worse defect than the
|
|
72
|
-
// one it fixes. So the failure is TRACED rather than propagated —
|
|
73
|
-
// an unreadable-but-present file still degrades to an empty
|
|
74
|
-
// document, but it stops doing so silently.
|
|
75
|
-
this.tracer
|
|
76
|
-
.with(resolved.toString())
|
|
77
|
-
.debug(`Load failed, falling back to an empty document: ${error instanceof Error ? error.message : String(error)}`);
|
|
94
|
+
throw error;
|
|
78
95
|
}
|
|
79
96
|
}
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
97
|
+
// The seam reports no loadable content, so there is nothing to read and
|
|
98
|
+
// no error from a read to carry.
|
|
99
|
+
throw new Error(`No loadable content for ${uri.toString()}`);
|
|
83
100
|
}
|
|
84
101
|
}
|
|
85
102
|
//# sourceMappingURL=langium-documents.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langium-documents.js","sourceRoot":"","sources":["../../../src/langium/workspace/langium-documents.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;
|
|
1
|
+
{"version":3,"file":"langium-documents.js","sourceRoot":"","sources":["../../../src/langium/workspace/langium-documents.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,EAAgB,uBAAuB,EAAyD,MAAM,oBAAoB,CAAC;AAelI;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,yBAA0B,SAAQ,uBAAuB;IAI3B;IAHxC,sFAAsF;IACnE,SAAS,CAAoB;IAEhD,YAAwC,QAAqC;QAC1E,KAAK,CAAC,QAAQ,CAAC,CAAC;QADqB,aAAQ,GAAR,QAAQ,CAA6B;QAE1E,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,iBAAiB,CAAC;IACzD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,mBAAmB,CAAC,GAAQ;QACzB,yEAAyE;QACzE,uEAAuE;QACvE,OAAO,IAAI,CAAC,sBAAsB,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;IAEQ,KAAK,CAAC,mBAAmB,CAAC,GAAQ;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,QAAQ,EAAE,CAAC;YACZ,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAC5C,IAAI,QAAQ,EAAE,CAAC;gBACZ,iEAAiE;gBACjE,kEAAkE;gBAClE,sEAAsE;gBACtE,kEAAkE;gBAClE,uDAAuD;gBACvD,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,mBAAmB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;gBAChE,OAAO,QAAQ,CAAC;YACnB,CAAC;YACD,IAAI,CAAC;gBACF,4DAA4D;gBAC5D,OAAO,MAAM,KAAK,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;YACpD,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACvB,oEAAoE;gBACpE,6DAA6D;gBAC7D,oEAAoE;gBACpE,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAC7C,IAAI,SAAS,EAAE,CAAC;oBACb,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,mBAAmB,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;oBACjE,OAAO,SAAS,CAAC;gBACpB,CAAC;gBACD,MAAM,KAAK,CAAC;YACf,CAAC;QACJ,CAAC;QACD,wEAAwE;QACxE,iCAAiC;QACjC,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAChE,CAAC;CACH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hydranium/core",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.25",
|
|
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
|
"hydranium",
|
|
@@ -96,8 +96,8 @@
|
|
|
96
96
|
"diff": "^5.2.0"
|
|
97
97
|
},
|
|
98
98
|
"devDependencies": {
|
|
99
|
-
"@hydranium/langium": "1.0.0-next.
|
|
100
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
99
|
+
"@hydranium/langium": "1.0.0-next.25",
|
|
100
|
+
"@hydranium/protocol": "1.0.0-next.25",
|
|
101
101
|
"@playwright/test": "^1.40.0",
|
|
102
102
|
"@types/diff": "^5.2.0",
|
|
103
103
|
"rimraf": "^5.0.0",
|
|
@@ -109,8 +109,8 @@
|
|
|
109
109
|
"vscode-languageserver-types": "^3.17.5"
|
|
110
110
|
},
|
|
111
111
|
"peerDependencies": {
|
|
112
|
-
"@hydranium/langium": "1.0.0-next.
|
|
113
|
-
"@hydranium/protocol": "1.0.0-next.
|
|
112
|
+
"@hydranium/langium": "1.0.0-next.25",
|
|
113
|
+
"@hydranium/protocol": "1.0.0-next.25",
|
|
114
114
|
"@playwright/test": "^1.40.0",
|
|
115
115
|
"vscode-jsonrpc": "9.0.1",
|
|
116
116
|
"vscode-languageserver": "~10.0.1",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
|
|
10
|
+
import { type URI } from '@hydranium/langium';
|
|
11
|
+
|
|
12
|
+
/** What a build is about, handed to `HydraniumDocumentBuilder.createBuildSession`. */
|
|
13
|
+
export interface BuildSessionContext {
|
|
14
|
+
/**
|
|
15
|
+
* Which entry point opened the build: `'build'` is the workspace-wide pass
|
|
16
|
+
* (initialization), `'update'` an incremental rebuild. A subclass deriving
|
|
17
|
+
* cascade state skips the work for `'build'`, where every document is in the
|
|
18
|
+
* set already and `shouldRelink` is never consulted.
|
|
19
|
+
*/
|
|
20
|
+
readonly kind: 'build' | 'update';
|
|
21
|
+
/** Formatted description of what caused the build — a URI, a count summary. */
|
|
22
|
+
readonly trigger: string;
|
|
23
|
+
/** True when {@link trigger} already states the document count. */
|
|
24
|
+
readonly triggerCountsDocs: boolean;
|
|
25
|
+
/** URIs the build was asked to rebuild, already flattened and canonicalized. */
|
|
26
|
+
readonly changed: readonly URI[];
|
|
27
|
+
/** URIs the build was asked to delete, already expanded by `collectDeletedURIs`. Empty for a `build` call. */
|
|
28
|
+
readonly deleted: readonly URI[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* One rebuild, treated as a correlated unit.
|
|
33
|
+
*
|
|
34
|
+
* Everything a build phase needs to know about the build it belongs to — what
|
|
35
|
+
* triggered it, when it started, whether it was cancelled, and the log lines it
|
|
36
|
+
* has produced so far. A `HydraniumDocumentBuilder` opens one per `update` /
|
|
37
|
+
* `build` call and holds it for the duration, so a per-phase hook can reach
|
|
38
|
+
* build-wide facts that no per-line formatting hook could.
|
|
39
|
+
*
|
|
40
|
+
* **Identity is the object, not {@link traceId}.** `Tracer.time` short-circuits
|
|
41
|
+
* when its log level is suppressed and never calls `captureId`, so `traceId` is
|
|
42
|
+
* `undefined` on every build in a production configuration that logs above
|
|
43
|
+
* `debug`. A teardown guard keyed on the id would then compare `undefined`
|
|
44
|
+
* against `undefined` and let a preempted build clear its successor's state.
|
|
45
|
+
* Reference equality against `HydraniumDocumentBuilder.activeSession` holds in
|
|
46
|
+
* both configurations, which is why nothing here is keyed on the id.
|
|
47
|
+
*
|
|
48
|
+
* Adopters carrying build-scoped state of their own subclass this and return it
|
|
49
|
+
* from `HydraniumDocumentBuilder.createBuildSession`, narrowing with an
|
|
50
|
+
* `instanceof` where they read it back. That buys them the framework's
|
|
51
|
+
* preemption-correct teardown rather than a second copy of it.
|
|
52
|
+
*/
|
|
53
|
+
export class BuildSession {
|
|
54
|
+
/**
|
|
55
|
+
* `Tracer.time` correlation id, so a phase line and the build line share a
|
|
56
|
+
* `#N`. `undefined` until the timer starts, and permanently `undefined` when
|
|
57
|
+
* the timing line is suppressed.
|
|
58
|
+
*/
|
|
59
|
+
traceId?: number;
|
|
60
|
+
/** Set when the build ended in `OperationCancelled`, so the successor can tag itself "cancels #N". */
|
|
61
|
+
cancelled = false;
|
|
62
|
+
/** Phase-reached lines produced so far; the first one measures from {@link startMs} rather than from the previous phase. */
|
|
63
|
+
phasesLogged = 0;
|
|
64
|
+
/** Lines held back by {@link detailThresholdMs}; flushed or dropped when the build ends. */
|
|
65
|
+
readonly bufferedLines: string[] = [];
|
|
66
|
+
|
|
67
|
+
constructor(
|
|
68
|
+
/** Millisecond timestamp the build started at, on the same clock as `performance.now()`. */
|
|
69
|
+
readonly startMs: number,
|
|
70
|
+
/** What caused this build — a formatted URI, a count summary, or an adopter label. */
|
|
71
|
+
readonly trigger: string,
|
|
72
|
+
/** True when {@link trigger} already states the document count, so a phase line must not repeat it. */
|
|
73
|
+
readonly triggerCountsDocs: boolean,
|
|
74
|
+
/**
|
|
75
|
+
* Build duration at or above which the buffered lines are emitted; `0`
|
|
76
|
+
* disables buffering entirely. Captured per session so the flush decision
|
|
77
|
+
* uses the same value the buffering decision did, even if the underlying
|
|
78
|
+
* observable changed mid-build.
|
|
79
|
+
*/
|
|
80
|
+
readonly detailThresholdMs: number
|
|
81
|
+
) {}
|
|
82
|
+
|
|
83
|
+
/** True while lines are being held rather than emitted as they are produced. */
|
|
84
|
+
get buffers(): boolean {
|
|
85
|
+
return this.detailThresholdMs > 0;
|
|
86
|
+
}
|
|
87
|
+
}
|