@likec4/language-server 1.23.1 → 1.24.0
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/dist/LikeC4FileSystem.d.ts +1 -0
- package/dist/LikeC4FileSystem.js +7 -0
- package/dist/Rpc.js +10 -7
- package/dist/ast.d.ts +13 -29
- package/dist/ast.js +3 -70
- package/dist/bundled.mjs +2466 -2641
- package/dist/generated/ast.d.ts +36 -8
- package/dist/generated/ast.js +44 -2
- package/dist/generated/grammar.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/likec4lib.d.ts +2 -0
- package/dist/likec4lib.js +3 -0
- package/dist/lsp/CodeLensProvider.js +7 -4
- package/dist/lsp/CompletionProvider.js +20 -2
- package/dist/lsp/DocumentLinkProvider.d.ts +3 -3
- package/dist/lsp/DocumentLinkProvider.js +14 -5
- package/dist/lsp/DocumentSymbolProvider.d.ts +1 -1
- package/dist/lsp/DocumentSymbolProvider.js +5 -2
- package/dist/lsp/HoverProvider.js +20 -7
- package/dist/lsp/SemanticTokenProvider.js +18 -1
- package/dist/model/builder/MergedExtends.d.ts +12 -0
- package/dist/model/builder/MergedExtends.js +67 -0
- package/dist/model/builder/MergedSpecification.d.ts +29 -0
- package/dist/model/builder/MergedSpecification.js +203 -0
- package/dist/model/builder/buildModel.d.ts +3 -0
- package/dist/model/builder/buildModel.js +194 -0
- package/dist/model/deployments-index.d.ts +6 -56
- package/dist/model/deployments-index.js +59 -137
- package/dist/model/fqn-index.d.ts +47 -17
- package/dist/model/fqn-index.js +155 -68
- package/dist/model/index.d.ts +0 -1
- package/dist/model/index.js +0 -1
- package/dist/model/model-builder.d.ts +13 -9
- package/dist/model/model-builder.js +101 -547
- package/dist/model/model-locator.d.ts +1 -0
- package/dist/model/model-locator.js +7 -9
- package/dist/model/model-parser.d.ts +24 -18
- package/dist/model/model-parser.js +51 -31
- package/dist/model/parser/Base.d.ts +3 -3
- package/dist/model/parser/Base.js +15 -9
- package/dist/model/parser/DeploymentModelParser.d.ts +4 -3
- package/dist/model/parser/DeploymentModelParser.js +54 -3
- package/dist/model/parser/DeploymentViewParser.d.ts +3 -2
- package/dist/model/parser/FqnRefParser.d.ts +2 -2
- package/dist/model/parser/GlobalsParser.d.ts +3 -2
- package/dist/model/parser/ModelParser.d.ts +4 -4
- package/dist/model/parser/ModelParser.js +45 -4
- package/dist/model/parser/PredicatesParser.d.ts +2 -2
- package/dist/model/parser/SpecificationParser.d.ts +2 -2
- package/dist/model/parser/ViewsParser.d.ts +3 -2
- package/dist/module.d.ts +2 -3
- package/dist/module.js +2 -3
- package/dist/references/scope-computation.d.ts +1 -1
- package/dist/references/scope-computation.js +14 -11
- package/dist/references/scope-provider.d.ts +16 -4
- package/dist/references/scope-provider.js +64 -30
- package/dist/test/testServices.d.ts +2 -1
- package/dist/test/testServices.js +17 -14
- package/dist/utils/elementRef.d.ts +1 -1
- package/dist/utils/elementRef.js +3 -3
- package/dist/validation/deployment-checks.d.ts +1 -0
- package/dist/validation/deployment-checks.js +12 -0
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +8 -1
- package/dist/views/configurable-layouter.js +3 -3
- package/dist/views/likec4-views.d.ts +1 -0
- package/dist/views/likec4-views.js +11 -11
- package/package.json +6 -7
- package/dist/bundled.d.ts +0 -8
- package/dist/bundled.js +0 -25
- package/dist/model/fqn-computation.d.ts +0 -3
- package/dist/model/fqn-computation.js +0 -72
|
@@ -45,8 +45,8 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
|
|
|
45
45
|
parseTags<E extends {
|
|
46
46
|
tags?: ast.Tags;
|
|
47
47
|
}>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
|
|
48
|
-
convertLinks(source?: ast.LinkProperty["$container"]):
|
|
49
|
-
parseLinks(source?: ast.LinkProperty["$container"]):
|
|
48
|
+
convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
49
|
+
parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
|
|
50
50
|
parseDeploymentView(astNode: ast.DeploymentView): import("../../ast").ParsedAstDeploymentView;
|
|
51
51
|
parseDeploymentViewRule(astRule: ast.DeploymentViewRule): c4.DeploymentViewRule;
|
|
52
52
|
parseDeploymentViewRulePredicate(astRule: ast.DeploymentViewRulePredicate): c4.DeploymentViewRulePredicate;
|
|
@@ -61,6 +61,7 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
|
|
|
61
61
|
parseDeployment(): void;
|
|
62
62
|
parseDeploymentNode(astNode: ast.DeploymentNode): import("../../ast").ParsedAstDeployment.Node;
|
|
63
63
|
parseDeployedInstance(astNode: ast.DeployedInstance): import("../../ast").ParsedAstDeployment.Instance;
|
|
64
|
+
parseExtendDeployment(astNode: ast.ExtendDeployment): import("../../ast").ParsedAstExtend | null;
|
|
64
65
|
parseDeploymentRelation(astNode: ast.DeploymentRelation): import("../../ast").ParsedAstDeploymentRelation;
|
|
65
66
|
};
|
|
66
67
|
} & TBase;
|
package/dist/module.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GraphvizLayouter } from '@likec4/layouts';
|
|
2
|
-
import { type Module,
|
|
2
|
+
import { type Module, WorkspaceCache } from 'langium';
|
|
3
3
|
import { type DefaultSharedModuleContext, type LangiumServices, type LangiumSharedServices, type PartialLangiumServices } from 'langium/lsp';
|
|
4
4
|
import { LikeC4DocumentationProvider } from './documentation';
|
|
5
5
|
import { LikeC4CodeLensProvider, LikeC4CompletionProvider, LikeC4DocumentHighlightProvider, LikeC4DocumentLinkProvider, LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
|
|
@@ -26,8 +26,7 @@ export interface LikeC4AddedServices {
|
|
|
26
26
|
documentation: {
|
|
27
27
|
DocumentationProvider: LikeC4DocumentationProvider;
|
|
28
28
|
};
|
|
29
|
-
|
|
30
|
-
DocumentCache: DocumentCache<string, any>;
|
|
29
|
+
ValidatedWorkspaceCache: WorkspaceCache<string, any>;
|
|
31
30
|
Rpc: Rpc;
|
|
32
31
|
likec4: {
|
|
33
32
|
Views: LikeC4Views;
|
package/dist/module.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GraphvizLayouter, GraphvizWasmAdapter } from "@likec4/layouts";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
DocumentState,
|
|
4
4
|
EmptyFileSystem,
|
|
5
5
|
inject,
|
|
6
6
|
WorkspaceCache
|
|
@@ -62,8 +62,7 @@ export const LikeC4Module = {
|
|
|
62
62
|
documentation: {
|
|
63
63
|
DocumentationProvider: bind(LikeC4DocumentationProvider)
|
|
64
64
|
},
|
|
65
|
-
|
|
66
|
-
DocumentCache: (services) => new DocumentCache(services.shared),
|
|
65
|
+
ValidatedWorkspaceCache: (services) => new WorkspaceCache(services.shared, DocumentState.Validated),
|
|
67
66
|
Rpc: bind(Rpc),
|
|
68
67
|
likec4: {
|
|
69
68
|
Layouter: (_services) => {
|
|
@@ -3,7 +3,7 @@ import type { CancellationToken } from 'vscode-languageserver';
|
|
|
3
3
|
import { type LikeC4LangiumDocument, ast } from '../ast';
|
|
4
4
|
import type { LikeC4Services } from '../module';
|
|
5
5
|
type ElementsContainer = ast.Model | ast.ElementBody | ast.ExtendElementBody;
|
|
6
|
-
type DeploymentsContainer = ast.ModelDeployments | ast.DeploymentNodeBody;
|
|
6
|
+
type DeploymentsContainer = ast.ModelDeployments | ast.DeploymentNodeBody | ast.ExtendDeploymentBody;
|
|
7
7
|
export declare class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
8
8
|
constructor(services: LikeC4Services);
|
|
9
9
|
computeExports(document: LikeC4LangiumDocument, _cancelToken?: CancellationToken): Promise<AstNodeDescription[]>;
|
|
@@ -242,19 +242,20 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
242
242
|
return local;
|
|
243
243
|
}
|
|
244
244
|
processDeployments(container, scopes, document) {
|
|
245
|
-
const
|
|
245
|
+
const localScope = new MultiMap();
|
|
246
246
|
const descedants = [];
|
|
247
247
|
for (const el of container.elements) {
|
|
248
248
|
if (ast.isDeploymentRelation(el)) {
|
|
249
249
|
continue;
|
|
250
250
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
251
|
+
if (!ast.isExtendDeployment(el)) {
|
|
252
|
+
let name = this.nameProvider.getName(el);
|
|
253
|
+
if (isTruthy(name)) {
|
|
254
|
+
const desc = this.descriptions.createDescription(el, name, document);
|
|
255
|
+
localScope.add(name, desc);
|
|
256
|
+
}
|
|
256
257
|
}
|
|
257
|
-
if (ast.
|
|
258
|
+
if (!ast.isDeployedInstance(el) && el.body) {
|
|
258
259
|
try {
|
|
259
260
|
descedants.push(
|
|
260
261
|
...this.processDeployments(el.body, scopes, document)
|
|
@@ -264,18 +265,20 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
264
265
|
}
|
|
265
266
|
}
|
|
266
267
|
}
|
|
267
|
-
if (descedants.length
|
|
268
|
+
if (descedants.length) {
|
|
268
269
|
pipe(
|
|
269
270
|
descedants,
|
|
270
|
-
filter((desc) => !
|
|
271
|
+
filter((desc) => !localScope.has(desc.name)),
|
|
271
272
|
groupBy((desc) => desc.name),
|
|
272
273
|
forEachObj((descs, name) => {
|
|
273
274
|
if (descs.length === 1) {
|
|
274
|
-
|
|
275
|
+
localScope.add(name, descs[0]);
|
|
275
276
|
}
|
|
276
277
|
})
|
|
277
278
|
);
|
|
278
279
|
}
|
|
279
|
-
|
|
280
|
+
const local = [...localScope.values()];
|
|
281
|
+
scopes.addAll(container, local);
|
|
282
|
+
return local;
|
|
280
283
|
}
|
|
281
284
|
}
|
|
@@ -5,12 +5,24 @@ export declare class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
5
5
|
private deploymentsIndex;
|
|
6
6
|
private fqnIndex;
|
|
7
7
|
constructor(services: LikeC4Services);
|
|
8
|
-
private directChildrenOf;
|
|
9
8
|
private uniqueDescedants;
|
|
10
|
-
private scopeElementRef;
|
|
11
|
-
private scopeExtendElement;
|
|
12
|
-
private scopeElementView;
|
|
13
9
|
getScope(context: ReferenceInfo): Scope;
|
|
10
|
+
private getScopeElementRef;
|
|
11
|
+
private getScopeExtendElement;
|
|
12
|
+
private getScopeElementView;
|
|
13
|
+
private getScopeForStrictFqnRef;
|
|
14
|
+
private getScopeExtendDeployment;
|
|
14
15
|
protected getScopeForFqnRef(container: ast.FqnRef, context: ReferenceInfo): any;
|
|
16
|
+
/**
|
|
17
|
+
* Computes the scope for a given reference context.
|
|
18
|
+
*
|
|
19
|
+
* @param context - The reference information containing the context for which the scope is being computed.
|
|
20
|
+
* @param referenceType - The type of reference being resolved. Defaults to the reference type derived from the context.
|
|
21
|
+
* @returns A scope containing the relevant AST node descriptions for the given reference context.
|
|
22
|
+
*
|
|
23
|
+
* This method first checks if there are precomputed scopes available in the document. If not, it falls back to the global scope.
|
|
24
|
+
* It then iterates through the container hierarchy, collecting relevant scopes based on the reference type and container type.
|
|
25
|
+
* Finally, it combines the collected scopes with the global scope to produce the final scope.
|
|
26
|
+
*/
|
|
15
27
|
protected computeScope(context: ReferenceInfo, referenceType?: any): any;
|
|
16
28
|
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "langium";
|
|
13
13
|
import { ast } from "../ast.js";
|
|
14
14
|
import { logWarnError } from "../logger.js";
|
|
15
|
-
import { elementRef,
|
|
15
|
+
import { elementRef, readStrictFqn } from "../utils/elementRef.js";
|
|
16
16
|
const { getDocument } = AstUtils;
|
|
17
17
|
export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
18
18
|
deploymentsIndex;
|
|
@@ -22,18 +22,19 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
22
22
|
this.fqnIndex = services.likec4.FqnIndex;
|
|
23
23
|
this.deploymentsIndex = services.likec4.DeploymentsIndex;
|
|
24
24
|
}
|
|
25
|
-
directChildrenOf(parent) {
|
|
26
|
-
return this.fqnIndex.directChildrenOf(parent);
|
|
27
|
-
}
|
|
28
25
|
// we need lazy resolving here
|
|
29
26
|
uniqueDescedants(of) {
|
|
30
27
|
return new StreamImpl(
|
|
31
28
|
() => {
|
|
32
29
|
const element = of();
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
if (element && ast.isElement(element)) {
|
|
31
|
+
const fqn = this.fqnIndex.getFqn(element);
|
|
35
32
|
return this.fqnIndex.uniqueDescedants(fqn).iterator();
|
|
36
33
|
}
|
|
34
|
+
if (element && ast.isDeploymentNode(element)) {
|
|
35
|
+
const fqn = this.deploymentsIndex.getFqn(element);
|
|
36
|
+
return this.deploymentsIndex.uniqueDescedants(fqn).iterator();
|
|
37
|
+
}
|
|
37
38
|
return null;
|
|
38
39
|
},
|
|
39
40
|
(iterator) => {
|
|
@@ -44,24 +45,6 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
44
45
|
}
|
|
45
46
|
);
|
|
46
47
|
}
|
|
47
|
-
scopeElementRef(ref) {
|
|
48
|
-
return this.uniqueDescedants(() => ref.el.ref);
|
|
49
|
-
}
|
|
50
|
-
scopeExtendElement({ element }) {
|
|
51
|
-
return stream([element.el.$nodeDescription]).nonNullable().concat(this.uniqueDescedants(() => elementRef(element)));
|
|
52
|
-
}
|
|
53
|
-
scopeElementView({ viewOf, extends: ext }) {
|
|
54
|
-
if (viewOf) {
|
|
55
|
-
return stream([viewOf.el.$nodeDescription]).nonNullable().concat(this.uniqueDescedants(() => elementRef(viewOf)));
|
|
56
|
-
}
|
|
57
|
-
if (ext) {
|
|
58
|
-
return stream([ext]).flatMap((v) => {
|
|
59
|
-
const view = v.view.ref;
|
|
60
|
-
return view ? this.scopeElementView(view) : EMPTY_STREAM;
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
return EMPTY_STREAM;
|
|
64
|
-
}
|
|
65
48
|
getScope(context) {
|
|
66
49
|
try {
|
|
67
50
|
const referenceType = this.reflection.getReferenceType(context);
|
|
@@ -70,6 +53,9 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
70
53
|
if (ast.isFqnRef(container)) {
|
|
71
54
|
return this.getScopeForFqnRef(container, context);
|
|
72
55
|
}
|
|
56
|
+
if (ast.isStrictFqnRef(container)) {
|
|
57
|
+
return this.getScopeForStrictFqnRef(container, context);
|
|
58
|
+
}
|
|
73
59
|
if (referenceType !== ast.Element) {
|
|
74
60
|
return this.getGlobalScope(referenceType, context);
|
|
75
61
|
}
|
|
@@ -78,12 +64,12 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
78
64
|
if (!parent) {
|
|
79
65
|
return this.getGlobalScope(referenceType, context);
|
|
80
66
|
}
|
|
81
|
-
return new StreamScope(this.directChildrenOf(
|
|
67
|
+
return new StreamScope(this.fqnIndex.directChildrenOf(readStrictFqn(parent)));
|
|
82
68
|
}
|
|
83
69
|
if (ast.isElementRef(container) && context.property === "el") {
|
|
84
70
|
const parent = container.parent;
|
|
85
71
|
if (parent) {
|
|
86
|
-
return new StreamScope(this.
|
|
72
|
+
return new StreamScope(this.getScopeElementRef(parent));
|
|
87
73
|
}
|
|
88
74
|
if (context.reference.$refText === "this" || context.reference.$refText === "it") {
|
|
89
75
|
const closestElement = AstUtils.getContainerOfType(container, ast.isElement);
|
|
@@ -106,6 +92,39 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
106
92
|
return EMPTY_SCOPE;
|
|
107
93
|
}
|
|
108
94
|
}
|
|
95
|
+
getScopeElementRef(ref) {
|
|
96
|
+
return this.uniqueDescedants(() => ref.el.ref);
|
|
97
|
+
}
|
|
98
|
+
getScopeExtendElement({ element }) {
|
|
99
|
+
return stream([element.el.$nodeDescription]).nonNullable().concat(this.uniqueDescedants(() => elementRef(element)));
|
|
100
|
+
}
|
|
101
|
+
getScopeElementView({ viewOf, extends: ext }) {
|
|
102
|
+
if (viewOf) {
|
|
103
|
+
return stream([viewOf.el.$nodeDescription]).nonNullable().concat(this.uniqueDescedants(() => elementRef(viewOf)));
|
|
104
|
+
}
|
|
105
|
+
if (ext) {
|
|
106
|
+
return stream([ext]).flatMap((v) => {
|
|
107
|
+
const view = v.view.ref;
|
|
108
|
+
return view ? this.getScopeElementView(view) : EMPTY_STREAM;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return EMPTY_STREAM;
|
|
112
|
+
}
|
|
113
|
+
getScopeForStrictFqnRef(container, context) {
|
|
114
|
+
const parent = container.parent;
|
|
115
|
+
if (!parent) {
|
|
116
|
+
return this.getGlobalScope(ast.DeploymentNode, context);
|
|
117
|
+
}
|
|
118
|
+
return new StreamScope(
|
|
119
|
+
this.deploymentsIndex.directChildrenOf(readStrictFqn(parent)).filter((desc) => this.reflection.isSubtype(desc.type, ast.DeploymentNode))
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
getScopeExtendDeployment({ deploymentNode }) {
|
|
123
|
+
return stream([deploymentNode.value.$nodeDescription]).nonNullable().concat(this.uniqueDescedants(() => {
|
|
124
|
+
const target = deploymentNode.value.ref;
|
|
125
|
+
return target && ast.isDeploymentNode(target) ? target : void 0;
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
109
128
|
getScopeForFqnRef(container, context) {
|
|
110
129
|
const parent = container.parent;
|
|
111
130
|
if (!parent) {
|
|
@@ -125,18 +144,30 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
125
144
|
return EMPTY_SCOPE;
|
|
126
145
|
}
|
|
127
146
|
if (ast.isDeploymentNode(parentRef)) {
|
|
128
|
-
return new StreamScope(this.
|
|
147
|
+
return new StreamScope(this.uniqueDescedants(() => parentRef));
|
|
129
148
|
}
|
|
130
149
|
if (ast.isDeployedInstance(parentRef)) {
|
|
131
|
-
return new StreamScope(this.
|
|
150
|
+
return new StreamScope(this.getScopeElementRef(parentRef.element));
|
|
132
151
|
}
|
|
133
152
|
if (ast.isElement(parentRef)) {
|
|
134
153
|
return new StreamScope(this.uniqueDescedants(() => parentRef));
|
|
135
154
|
}
|
|
136
155
|
return nonexhaustive(parentRef);
|
|
137
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Computes the scope for a given reference context.
|
|
159
|
+
*
|
|
160
|
+
* @param context - The reference information containing the context for which the scope is being computed.
|
|
161
|
+
* @param referenceType - The type of reference being resolved. Defaults to the reference type derived from the context.
|
|
162
|
+
* @returns A scope containing the relevant AST node descriptions for the given reference context.
|
|
163
|
+
*
|
|
164
|
+
* This method first checks if there are precomputed scopes available in the document. If not, it falls back to the global scope.
|
|
165
|
+
* It then iterates through the container hierarchy, collecting relevant scopes based on the reference type and container type.
|
|
166
|
+
* Finally, it combines the collected scopes with the global scope to produce the final scope.
|
|
167
|
+
*/
|
|
138
168
|
computeScope(context, referenceType = this.reflection.getReferenceType(context)) {
|
|
139
169
|
const isElementReference = this.reflection.isSubtype(referenceType, ast.Element);
|
|
170
|
+
const isDeploymentReference = this.reflection.isSubtype(referenceType, ast.DeploymentElement);
|
|
140
171
|
const scopes = [];
|
|
141
172
|
const doc = getDocument(context.container);
|
|
142
173
|
const precomputed = doc.precomputedScopes;
|
|
@@ -150,11 +181,14 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
150
181
|
if (elements.length > 0) {
|
|
151
182
|
scopes.push(stream(elements));
|
|
152
183
|
}
|
|
184
|
+
if (isDeploymentReference && ast.isExtendDeploymentBody(container)) {
|
|
185
|
+
scopes.push(this.getScopeExtendDeployment(container.$container));
|
|
186
|
+
}
|
|
153
187
|
if (isElementReference && ast.isExtendElementBody(container)) {
|
|
154
|
-
scopes.push(this.
|
|
188
|
+
scopes.push(this.getScopeExtendElement(container.$container));
|
|
155
189
|
}
|
|
156
190
|
if (isElementReference && ast.isElementViewBody(container)) {
|
|
157
|
-
scopes.push(this.
|
|
191
|
+
scopes.push(this.getScopeElementView(container.$container));
|
|
158
192
|
}
|
|
159
193
|
container = container.$container;
|
|
160
194
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LikeC4LangiumDocument } from '../ast';
|
|
2
2
|
export declare function createTestServices(workspace?: string): {
|
|
3
3
|
services: any;
|
|
4
|
+
addDocument: (input: string, uri?: string) => Promise<LikeC4LangiumDocument>;
|
|
4
5
|
parse: (input: string, uri?: string) => Promise<LikeC4LangiumDocument>;
|
|
5
6
|
validate: (input: string | LikeC4LangiumDocument, uri?: string) => Promise<{
|
|
6
7
|
document: LikeC4LangiumDocument;
|
|
@@ -13,7 +14,7 @@ export declare function createTestServices(workspace?: string): {
|
|
|
13
14
|
errors: any;
|
|
14
15
|
warnings: any;
|
|
15
16
|
}>;
|
|
16
|
-
buildModel: () => Promise<
|
|
17
|
+
buildModel: () => Promise<ComputedLikeC4Model>;
|
|
17
18
|
buildLikeC4Model: () => Promise<any>;
|
|
18
19
|
resetState: () => Promise<void>;
|
|
19
20
|
format: (input: string | LikeC4LangiumDocument, uri?: string) => Promise<any>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { LikeC4Model } from "@likec4/core";
|
|
2
1
|
import { DocumentState, EmptyFileSystem, TextDocument } from "langium";
|
|
3
2
|
import * as assert from "node:assert";
|
|
4
3
|
import stripIndent from "strip-indent";
|
|
@@ -19,7 +18,7 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
19
18
|
};
|
|
20
19
|
let isInitialized = false;
|
|
21
20
|
let documentIndex = 1;
|
|
22
|
-
const
|
|
21
|
+
const addDocument = async (input, uri) => {
|
|
23
22
|
if (!isInitialized) {
|
|
24
23
|
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
25
24
|
if (isInitialized) {
|
|
@@ -45,13 +44,17 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
45
44
|
docUri
|
|
46
45
|
);
|
|
47
46
|
langiumDocuments.addDocument(document);
|
|
47
|
+
return document;
|
|
48
|
+
};
|
|
49
|
+
const parse = async (input, uri) => {
|
|
50
|
+
const document = await addDocument(input, uri);
|
|
48
51
|
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
49
52
|
await documentBuilder.build([document], { validation: false });
|
|
50
53
|
});
|
|
51
54
|
return document;
|
|
52
55
|
};
|
|
53
56
|
const validate = async (input, uri) => {
|
|
54
|
-
const document = typeof input === "string" ? await
|
|
57
|
+
const document = typeof input === "string" ? await addDocument(input, uri) : input;
|
|
55
58
|
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
56
59
|
await documentBuilder.build([document], { validation: true });
|
|
57
60
|
});
|
|
@@ -80,13 +83,12 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
80
83
|
return TextDocument.applyEdits(document.textDocument, edits ?? []);
|
|
81
84
|
};
|
|
82
85
|
const validateAll = async () => {
|
|
83
|
-
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
84
|
-
const docs2 = langiumDocuments.all.toArray();
|
|
85
|
-
await documentBuilder.build(docs2, { validation: true });
|
|
86
|
-
});
|
|
87
|
-
await documentBuilder.waitUntil(DocumentState.Validated);
|
|
88
86
|
const docs = langiumDocuments.all.toArray();
|
|
89
87
|
assert.ok(docs.length > 0, "no documents to validate");
|
|
88
|
+
await services.shared.workspace.WorkspaceLock.write(async (cancelToken) => {
|
|
89
|
+
await documentBuilder.build(docs, { validation: true }, cancelToken);
|
|
90
|
+
});
|
|
91
|
+
await documentBuilder.waitUntil(DocumentState.Validated);
|
|
90
92
|
const diagnostics = docs.flatMap((doc) => doc.diagnostics ?? []);
|
|
91
93
|
const warnings = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Warning ? d.message : []);
|
|
92
94
|
const errors = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Error ? d.message : []);
|
|
@@ -98,15 +100,15 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
98
100
|
};
|
|
99
101
|
const buildModel = async () => {
|
|
100
102
|
await validateAll();
|
|
101
|
-
const
|
|
102
|
-
if (!
|
|
103
|
-
return model;
|
|
103
|
+
const likec4model = await modelBuilder.buildLikeC4Model();
|
|
104
|
+
if (!likec4model) throw new Error("No model found");
|
|
105
|
+
return likec4model.$model;
|
|
104
106
|
};
|
|
105
107
|
const buildLikeC4Model = async () => {
|
|
106
108
|
await validateAll();
|
|
107
|
-
const
|
|
108
|
-
if (!
|
|
109
|
-
return
|
|
109
|
+
const likec4model = await modelBuilder.buildLikeC4Model();
|
|
110
|
+
if (!likec4model) throw new Error("No model found");
|
|
111
|
+
return likec4model;
|
|
110
112
|
};
|
|
111
113
|
const resetState = async () => {
|
|
112
114
|
await services.shared.workspace.WorkspaceLock.write(async (cancelToken) => {
|
|
@@ -116,6 +118,7 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
116
118
|
};
|
|
117
119
|
return {
|
|
118
120
|
services,
|
|
121
|
+
addDocument,
|
|
119
122
|
parse,
|
|
120
123
|
validate,
|
|
121
124
|
validateAll,
|
|
@@ -8,4 +8,4 @@ export declare function elementRef(node: ast.ElementRef | ast.StrictFqnElementRe
|
|
|
8
8
|
* Returns FQN of StrictFqnElementRef
|
|
9
9
|
* a.b.c.d - for c node returns a.b.c
|
|
10
10
|
*/
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function readStrictFqn(node: ast.StrictFqnElementRef | ast.StrictFqnRef): c4.Fqn;
|
package/dist/utils/elementRef.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export function elementRef(node) {
|
|
2
2
|
return node.el.ref;
|
|
3
3
|
}
|
|
4
|
-
export function
|
|
5
|
-
const name = [node.el.$refText];
|
|
4
|
+
export function readStrictFqn(node) {
|
|
5
|
+
const name = [node.$type === "StrictFqnRef" ? node.value.$refText : node.el.$refText];
|
|
6
6
|
let parent = node.parent;
|
|
7
7
|
while (parent) {
|
|
8
|
-
name.push(parent.el.$refText);
|
|
8
|
+
name.push(parent.$type === "StrictFqnRef" ? parent.value.$refText : parent.el.$refText);
|
|
9
9
|
parent = parent.parent;
|
|
10
10
|
}
|
|
11
11
|
if (name.length === 1) {
|
|
@@ -4,3 +4,4 @@ import type { LikeC4Services } from '../module';
|
|
|
4
4
|
export declare const deploymentNodeChecks: (services: LikeC4Services) => ValidationCheck<ast.DeploymentNode>;
|
|
5
5
|
export declare const deployedInstanceChecks: (services: LikeC4Services) => ValidationCheck<ast.DeployedInstance>;
|
|
6
6
|
export declare const deploymentRelationChecks: (services: LikeC4Services) => ValidationCheck<ast.DeploymentRelation>;
|
|
7
|
+
export declare const extendDeploymentChecks: (services: LikeC4Services) => ValidationCheck<ast.ExtendDeployment>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FqnRef, isSameHierarchy, nonNullable } from "@likec4/core";
|
|
2
2
|
import { AstUtils } from "langium";
|
|
3
|
+
import { ast } from "../ast.js";
|
|
3
4
|
import { RESERVED_WORDS, tryOrLog } from "./_shared.js";
|
|
4
5
|
const { getDocument } = AstUtils;
|
|
5
6
|
export const deploymentNodeChecks = (services) => {
|
|
@@ -112,3 +113,14 @@ export const deploymentRelationChecks = (services) => {
|
|
|
112
113
|
}
|
|
113
114
|
});
|
|
114
115
|
};
|
|
116
|
+
export const extendDeploymentChecks = (services) => {
|
|
117
|
+
return tryOrLog((el, accept) => {
|
|
118
|
+
const target = el.deploymentNode.value.ref;
|
|
119
|
+
if (!target || !ast.isDeploymentNode(target)) {
|
|
120
|
+
accept("error", "ExtendDeployment allows only DeploymentNode", {
|
|
121
|
+
node: el,
|
|
122
|
+
property: "deploymentNode"
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
};
|
|
@@ -3,7 +3,7 @@ import { type LikeC4LangiumDocument, ast } from '../ast';
|
|
|
3
3
|
import type { LikeC4Services } from '../module';
|
|
4
4
|
type Guard<N extends AstNode> = (n: AstNode) => n is N;
|
|
5
5
|
type Guarded<G> = G extends Guard<infer N> ? N : never;
|
|
6
|
-
declare const isValidatableAstNode: (n: AstNode) => n is ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentViewRulePredicate | ast.DeploymentViewRuleStyle | ast.ViewRuleAutoLayout | ast.DynamicViewGlobalPredicateRef | ast.DynamicViewIncludePredicate | ast.ViewRuleGlobalStyle | ast.ViewRuleStyle | ast.ElementDescedantsExpression | ast.ElementKindExpression | ast.ElementRef | ast.ElementTagExpression | ast.ExpandElementExpression | ast.WildcardExpression | ast.ElementPredicateWhere | ast.ElementPredicateWith | ast.ElementPredicateWhereV2 | ast.FqnRefExpr | ast.ElementStringProperty | ast.ElementStyleProperty | ast.IconProperty | ast.LinkProperty | ast.MetadataBody | ast.DirectedRelationExpr | ast.InOutRelationExpr | ast.IncomingRelationExpr | ast.OutgoingRelationExpr | ast.RelationPredicateWhereV2 | ast.Element | ast.ExtendElement | ast.DeploymentView | ast.DynamicView | ast.ElementView | ast.DirectedRelationExpression | ast.InOutRelationExpression | ast.IncomingRelationExpression | ast.OutgoingRelationExpression | ast.RelationPredicateWhere | ast.RelationPredicateWith | ast.RelationStringProperty | ast.ArrowProperty | ast.ColorProperty | ast.LineProperty | ast.PaddingSizeProperty | ast.ShapeSizeProperty | ast.TextSizeProperty | ast.MetadataAttribute | ast.NotationProperty | ast.NotesProperty | ast.SpecificationElementStringProperty | ast.SpecificationRelationshipStringProperty | ast.ViewStringProperty | ast.BorderProperty | ast.MultipleProperty | ast.OpacityProperty | ast.ShapeProperty | ast.ViewRuleGlobalPredicateRef | ast.ViewRuleGroup | ast.ExcludePredicate | ast.IncludePredicate | ast.SpecificationRelationshipKind | ast.GlobalStyle | ast.SpecificationColor | ast.NavigateToProperty | ast.DynamicViewStep | ast.Tags | ast.DeploymentRelation | ast.SpecificationDeploymentNodeKind | ast.DynamicViewParallelSteps | ast.GlobalDynamicPredicateGroup | ast.DynamicViewPredicateIterator | ast.Relation | ast.SpecificationElementKind | ast.GlobalPredicateGroup | ast.Globals | ast.GlobalStyleGroup | ast.SpecificationRule | ast.SpecificationTag;
|
|
6
|
+
declare const isValidatableAstNode: (n: AstNode) => n is ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentViewRulePredicate | ast.DeploymentViewRuleStyle | ast.ViewRuleAutoLayout | ast.DynamicViewGlobalPredicateRef | ast.DynamicViewIncludePredicate | ast.ViewRuleGlobalStyle | ast.ViewRuleStyle | ast.ElementDescedantsExpression | ast.ElementKindExpression | ast.ElementRef | ast.ElementTagExpression | ast.ExpandElementExpression | ast.WildcardExpression | ast.ElementPredicateWhere | ast.ElementPredicateWith | ast.ElementPredicateWhereV2 | ast.FqnRefExpr | ast.ElementStringProperty | ast.ElementStyleProperty | ast.IconProperty | ast.LinkProperty | ast.MetadataBody | ast.DirectedRelationExpr | ast.InOutRelationExpr | ast.IncomingRelationExpr | ast.OutgoingRelationExpr | ast.RelationPredicateWhereV2 | ast.Element | ast.ExtendDeployment | ast.ExtendElement | ast.DeploymentView | ast.DynamicView | ast.ElementView | ast.DirectedRelationExpression | ast.InOutRelationExpression | ast.IncomingRelationExpression | ast.OutgoingRelationExpression | ast.RelationPredicateWhere | ast.RelationPredicateWith | ast.RelationStringProperty | ast.ArrowProperty | ast.ColorProperty | ast.LineProperty | ast.PaddingSizeProperty | ast.ShapeSizeProperty | ast.TextSizeProperty | ast.MetadataAttribute | ast.NotationProperty | ast.NotesProperty | ast.SpecificationElementStringProperty | ast.SpecificationRelationshipStringProperty | ast.ViewStringProperty | ast.BorderProperty | ast.MultipleProperty | ast.OpacityProperty | ast.ShapeProperty | ast.ViewRuleGlobalPredicateRef | ast.ViewRuleGroup | ast.ExcludePredicate | ast.IncludePredicate | ast.SpecificationRelationshipKind | ast.GlobalStyle | ast.SpecificationColor | ast.NavigateToProperty | ast.DynamicViewStep | ast.Tags | ast.DeploymentRelation | ast.SpecificationDeploymentNodeKind | ast.DynamicViewParallelSteps | ast.GlobalDynamicPredicateGroup | ast.DynamicViewPredicateIterator | ast.Relation | ast.SpecificationElementKind | ast.GlobalPredicateGroup | ast.Globals | ast.GlobalStyleGroup | ast.SpecificationRule | ast.SpecificationTag;
|
|
7
7
|
type ValidatableAstNode = Guarded<typeof isValidatableAstNode>;
|
|
8
8
|
export declare function checksFromDiagnostics(doc: LikeC4LangiumDocument): {
|
|
9
9
|
isValid: (n: ValidatableAstNode) => boolean;
|
package/dist/validation/index.js
CHANGED
|
@@ -3,7 +3,12 @@ import { isNullish } from "remeda";
|
|
|
3
3
|
import { DiagnosticSeverity } from "vscode-languageserver-types";
|
|
4
4
|
import { ast } from "../ast.js";
|
|
5
5
|
import { logger } from "../logger.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
deployedInstanceChecks,
|
|
8
|
+
deploymentNodeChecks,
|
|
9
|
+
deploymentRelationChecks,
|
|
10
|
+
extendDeploymentChecks
|
|
11
|
+
} from "./deployment-checks.js";
|
|
7
12
|
import { dynamicViewRulePredicate } from "./dynamic-view-rule.js";
|
|
8
13
|
import { dynamicViewStep } from "./dynamic-view-step.js";
|
|
9
14
|
import { elementChecks } from "./element.js";
|
|
@@ -70,6 +75,7 @@ const isValidatableAstNode = validatableAstNodeGuards([
|
|
|
70
75
|
ast.isNavigateToProperty,
|
|
71
76
|
ast.isElement,
|
|
72
77
|
ast.isExtendElement,
|
|
78
|
+
ast.isExtendDeployment,
|
|
73
79
|
ast.isSpecificationElementKind,
|
|
74
80
|
ast.isSpecificationRelationshipKind,
|
|
75
81
|
ast.isSpecificationDeploymentNodeKind,
|
|
@@ -114,6 +120,7 @@ export function registerValidationChecks(services) {
|
|
|
114
120
|
DeploymentNodeKind: deploymentNodeKindChecks(services),
|
|
115
121
|
DeploymentNode: deploymentNodeChecks(services),
|
|
116
122
|
DeploymentRelation: deploymentRelationChecks(services),
|
|
123
|
+
ExtendDeployment: extendDeploymentChecks(services),
|
|
117
124
|
FqnRefExpr: fqnRefExprChecks(services),
|
|
118
125
|
RelationExpr: relationExprChecks(services),
|
|
119
126
|
NotesProperty: notesPropertyRuleChecks(services),
|
|
@@ -19,7 +19,7 @@ export const ConfigurableLayouter = {
|
|
|
19
19
|
const layouter = new GraphvizLayouter(wasmAdapter);
|
|
20
20
|
const langId = services.LanguageMetaData.languageId;
|
|
21
21
|
services.shared.workspace.ConfigurationProvider.onConfigurationSectionUpdate((update) => {
|
|
22
|
-
logger.debug("Configuration update", { update });
|
|
22
|
+
logger.debug("Configuration update: {update}", { update });
|
|
23
23
|
if (update.section === langId) {
|
|
24
24
|
try {
|
|
25
25
|
const { mode, path } = update.configuration.graphviz ?? {
|
|
@@ -41,13 +41,13 @@ export const ConfigurableLayouter = {
|
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
layouter.changePort(new GraphvizBinaryAdapter(binaryPath));
|
|
44
|
-
logger.info
|
|
44
|
+
logger.info`use graphviz binary: ${binaryPath}`;
|
|
45
45
|
} catch (error) {
|
|
46
46
|
logger.error("Failed to update configuration", { error });
|
|
47
47
|
}
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
|
-
logger.warn("Unexpected configuration update", { update });
|
|
50
|
+
logger.warn("Unexpected configuration update: {update}", { update });
|
|
51
51
|
});
|
|
52
52
|
return layouter;
|
|
53
53
|
}
|
|
@@ -15,6 +15,7 @@ export declare class LikeC4Views {
|
|
|
15
15
|
private services;
|
|
16
16
|
private cache;
|
|
17
17
|
private viewsWithReportedErrors;
|
|
18
|
+
private ModelBuilder;
|
|
18
19
|
constructor(services: LikeC4Services);
|
|
19
20
|
get layouter(): GraphvizLayouter;
|
|
20
21
|
computedViews(cancelToken?: Cancellation.CancellationToken): Promise<ComputedView[]>;
|
|
@@ -3,15 +3,17 @@ import { logError, logWarnError } from "../logger.js";
|
|
|
3
3
|
export class LikeC4Views {
|
|
4
4
|
constructor(services) {
|
|
5
5
|
this.services = services;
|
|
6
|
+
this.ModelBuilder = services.likec4.ModelBuilder;
|
|
6
7
|
}
|
|
7
8
|
cache = /* @__PURE__ */ new WeakMap();
|
|
8
9
|
viewsWithReportedErrors = /* @__PURE__ */ new Set();
|
|
10
|
+
ModelBuilder;
|
|
9
11
|
get layouter() {
|
|
10
12
|
return this.services.likec4.Layouter;
|
|
11
13
|
}
|
|
12
14
|
async computedViews(cancelToken) {
|
|
13
|
-
const
|
|
14
|
-
return
|
|
15
|
+
const likeC4Model = await this.ModelBuilder.buildLikeC4Model(cancelToken);
|
|
16
|
+
return values(likeC4Model.$model.views);
|
|
15
17
|
}
|
|
16
18
|
async layoutAllViews(cancelToken) {
|
|
17
19
|
const views = await this.computedViews(cancelToken);
|
|
@@ -24,6 +26,7 @@ export class LikeC4Views {
|
|
|
24
26
|
this.viewsWithReportedErrors.delete(view.id);
|
|
25
27
|
tasks.push(
|
|
26
28
|
this.layouter.layout(view).then((result) => {
|
|
29
|
+
this.viewsWithReportedErrors.delete(view.id);
|
|
27
30
|
this.cache.set(view, result);
|
|
28
31
|
return result;
|
|
29
32
|
}).catch((e) => {
|
|
@@ -41,17 +44,14 @@ export class LikeC4Views {
|
|
|
41
44
|
return results;
|
|
42
45
|
}
|
|
43
46
|
async layoutView(viewId, cancelToken) {
|
|
44
|
-
const model = await this.
|
|
45
|
-
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
const view = model.views[viewId];
|
|
47
|
+
const model = await this.ModelBuilder.buildLikeC4Model(cancelToken);
|
|
48
|
+
const view = model.findView(viewId)?.$view;
|
|
49
49
|
if (!view) {
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
52
52
|
let cached = this.cache.get(view);
|
|
53
53
|
if (cached) {
|
|
54
|
-
return cached;
|
|
54
|
+
return await Promise.resolve(cached);
|
|
55
55
|
}
|
|
56
56
|
try {
|
|
57
57
|
const result = await this.layouter.layout(view);
|
|
@@ -63,8 +63,8 @@ export class LikeC4Views {
|
|
|
63
63
|
const errMessage = e instanceof Error ? e.message : "" + e;
|
|
64
64
|
this.services.shared.lsp.Connection?.window.showErrorMessage(`LikeC4: ${errMessage}`);
|
|
65
65
|
this.viewsWithReportedErrors.add(viewId);
|
|
66
|
+
logError(e);
|
|
66
67
|
}
|
|
67
|
-
logError(e);
|
|
68
68
|
return Promise.reject(e);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -74,7 +74,7 @@ export class LikeC4Views {
|
|
|
74
74
|
}
|
|
75
75
|
async viewsAsGraphvizOut() {
|
|
76
76
|
const KEY = "All-LayoutedViews-DotWithSvg";
|
|
77
|
-
const cache = this.services.
|
|
77
|
+
const cache = this.services.ValidatedWorkspaceCache;
|
|
78
78
|
if (cache.has(KEY)) {
|
|
79
79
|
return await Promise.resolve(cache.get(KEY));
|
|
80
80
|
}
|
|
@@ -101,7 +101,7 @@ export class LikeC4Views {
|
|
|
101
101
|
}
|
|
102
102
|
async overviewGraph() {
|
|
103
103
|
const KEY = "OverviewGraph";
|
|
104
|
-
const cache = this.services.
|
|
104
|
+
const cache = this.services.ValidatedWorkspaceCache;
|
|
105
105
|
if (cache.has(KEY)) {
|
|
106
106
|
return await Promise.resolve(cache.get(KEY));
|
|
107
107
|
}
|