@likec4/language-server 0.46.1 → 0.47.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/contrib/likec4.monarch.ts +5 -8
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/ast.d.ts +8 -3
- package/dist/ast.js +12 -8
- package/dist/generated/ast.d.ts +152 -110
- package/dist/generated/ast.js +149 -94
- package/dist/generated/grammar.js +1 -1
- package/dist/lsp/DocumentSymbolProvider.d.ts +7 -2
- package/dist/lsp/DocumentSymbolProvider.js +63 -56
- package/dist/lsp/HoverProvider.js +2 -8
- package/dist/lsp/SemanticTokenProvider.js +15 -25
- package/dist/model/fqn-index.d.ts +2 -7
- package/dist/model/fqn-index.js +14 -26
- package/dist/model/model-builder.js +5 -3
- package/dist/model/model-parser.d.ts +3 -2
- package/dist/model/model-parser.js +111 -51
- package/dist/module.js +3 -2
- package/dist/references/scope-computation.js +35 -15
- package/dist/references/scope-provider.d.ts +1 -2
- package/dist/references/scope-provider.js +17 -25
- package/dist/shared/NodeKindProvider.d.ts +4 -2
- package/dist/shared/NodeKindProvider.js +40 -13
- package/dist/shared/WorkspaceSymbolProvider.d.ts +4 -0
- package/dist/shared/WorkspaceSymbolProvider.js +3 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/test/testServices.d.ts +5 -0
- package/dist/test/testServices.js +25 -13
- package/dist/validation/index.js +22 -14
- package/dist/validation/specification.js +2 -1
- package/dist/validation/view-predicates/custom-element-expr.d.ts +5 -0
- package/dist/validation/view-predicates/custom-element-expr.js +16 -0
- package/dist/validation/view-predicates/incoming.d.ts +1 -1
- package/dist/validation/view-predicates/incoming.js +1 -1
- package/dist/validation/view-predicates/index.d.ts +1 -0
- package/dist/validation/view-predicates/index.js +1 -0
- package/dist/validation/view-predicates/outgoing.d.ts +1 -1
- package/dist/validation/view-predicates/outgoing.js +1 -1
- package/dist/validation/view.js +1 -1
- package/dist/view-utils/assignNavigateTo.js +3 -0
- package/package.json +6 -5
|
@@ -11,13 +11,6 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
11
11
|
modifier: [SemanticTokenModifiers.definition]
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
|
-
if (ast.isElementRef(node) || ast.isFqnElementRef(node)) {
|
|
15
|
-
acceptor({
|
|
16
|
-
node,
|
|
17
|
-
property: "el",
|
|
18
|
-
type: node.parent ? SemanticTokenTypes.property : SemanticTokenTypes.variable
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
14
|
if (ast.isElementViewRef(node)) {
|
|
22
15
|
acceptor({
|
|
23
16
|
node,
|
|
@@ -25,21 +18,19 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
25
18
|
type: SemanticTokenTypes.variable
|
|
26
19
|
});
|
|
27
20
|
}
|
|
28
|
-
if (ast.
|
|
21
|
+
if (ast.isDescedantsExpr(node) && node.$cstNode) {
|
|
29
22
|
acceptor({
|
|
30
|
-
node,
|
|
31
|
-
keyword: ".*",
|
|
23
|
+
cst: node.$cstNode,
|
|
32
24
|
type: SemanticTokenTypes.variable
|
|
33
25
|
});
|
|
34
26
|
}
|
|
35
|
-
if (ast.
|
|
27
|
+
if (ast.isWildcardExpr(node) && node.$cstNode) {
|
|
36
28
|
acceptor({
|
|
37
|
-
node,
|
|
38
|
-
property: "isWildcard",
|
|
29
|
+
cst: node.$cstNode,
|
|
39
30
|
type: SemanticTokenTypes.variable
|
|
40
31
|
});
|
|
41
32
|
}
|
|
42
|
-
if (ast.
|
|
33
|
+
if (ast.isElementKindExpr(node)) {
|
|
43
34
|
acceptor({
|
|
44
35
|
node,
|
|
45
36
|
property: "kind",
|
|
@@ -47,7 +38,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
47
38
|
modifier: [SemanticTokenModifiers.definition]
|
|
48
39
|
});
|
|
49
40
|
}
|
|
50
|
-
if (ast.
|
|
41
|
+
if (ast.isElementTagExpr(node)) {
|
|
51
42
|
acceptor({
|
|
52
43
|
node,
|
|
53
44
|
property: "tag",
|
|
@@ -55,7 +46,14 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
55
46
|
modifier: [SemanticTokenModifiers.definition]
|
|
56
47
|
});
|
|
57
48
|
}
|
|
58
|
-
if (ast.
|
|
49
|
+
if (ast.isElementRef(node) || ast.isFqnElementRef(node)) {
|
|
50
|
+
acceptor({
|
|
51
|
+
node,
|
|
52
|
+
property: "el",
|
|
53
|
+
type: node.parent ? SemanticTokenTypes.property : SemanticTokenTypes.variable
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (ast.isSpecificationElementKind(node) || ast.isSpecificationRelationshipKind(node)) {
|
|
59
57
|
acceptor({
|
|
60
58
|
node,
|
|
61
59
|
property: "kind",
|
|
@@ -79,14 +77,6 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
79
77
|
modifier: [SemanticTokenModifiers.definition]
|
|
80
78
|
});
|
|
81
79
|
}
|
|
82
|
-
if (ast.isSpecificationRelationshipKind(node)) {
|
|
83
|
-
acceptor({
|
|
84
|
-
node,
|
|
85
|
-
property: "kind",
|
|
86
|
-
type: SemanticTokenTypes.type,
|
|
87
|
-
modifier: [SemanticTokenModifiers.definition]
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
80
|
if (ast.isColorProperty(node) || ast.isShapeProperty(node) || ast.isArrowProperty(node) || ast.isLineProperty(node)) {
|
|
91
81
|
acceptor({
|
|
92
82
|
node,
|
|
@@ -104,7 +94,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
104
94
|
if (ast.isElement(node)) {
|
|
105
95
|
this.highlightAstElement(node, acceptor);
|
|
106
96
|
}
|
|
107
|
-
if (ast.
|
|
97
|
+
if (ast.isElementView(node)) {
|
|
108
98
|
this.highlightView(node, acceptor);
|
|
109
99
|
}
|
|
110
100
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import type { Fqn } from '@likec4/core';
|
|
2
|
-
import type {
|
|
3
|
-
import type { ast } from '../ast';
|
|
4
|
-
import { type LikeC4LangiumDocument } from '../ast';
|
|
2
|
+
import type { LangiumDocuments, Stream } from 'langium';
|
|
3
|
+
import type { FqnIndexedDocument, ast } from '../ast';
|
|
5
4
|
import type { LikeC4Services } from '../module';
|
|
6
|
-
export type FqnIndexedDocument = Omit<LikeC4LangiumDocument, 'c4fqns'> & {
|
|
7
|
-
c4fqns: NonNullable<LikeC4LangiumDocument['c4fqns']>;
|
|
8
|
-
};
|
|
9
|
-
export declare function isFqnIndexedDocument(doc: LangiumDocument): doc is FqnIndexedDocument;
|
|
10
5
|
export interface FqnIndexEntry {
|
|
11
6
|
fqn: Fqn;
|
|
12
7
|
name: string;
|
package/dist/model/fqn-index.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { nameFromFqn, parentFqn } from "@likec4/core";
|
|
2
|
-
import { DONE_RESULT, DocumentState, MultiMap, StreamImpl,
|
|
3
|
-
import {
|
|
4
|
-
import { ElementOps, isLikeC4LangiumDocument } from "../ast.js";
|
|
2
|
+
import { DONE_RESULT, DocumentState, MultiMap, StreamImpl, stream } from "langium";
|
|
3
|
+
import { ElementOps, isFqnIndexedDocument, isLikeC4LangiumDocument } from "../ast.js";
|
|
5
4
|
import { logError, logger } from "../logger.js";
|
|
6
5
|
import { printDocs } from "../utils.js";
|
|
7
6
|
import { computeDocumentFqn } from "./fqn-computation.js";
|
|
8
|
-
|
|
9
|
-
return isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns);
|
|
10
|
-
}
|
|
7
|
+
const True = () => true;
|
|
11
8
|
export class FqnIndex {
|
|
12
9
|
constructor(services) {
|
|
13
10
|
this.services = services;
|
|
@@ -40,37 +37,28 @@ export class FqnIndex {
|
|
|
40
37
|
get documents() {
|
|
41
38
|
return this.langiumDocuments.all.filter(isFqnIndexedDocument);
|
|
42
39
|
}
|
|
43
|
-
entries(filterByFqn =
|
|
40
|
+
entries(filterByFqn = True) {
|
|
44
41
|
return this.documents.flatMap(
|
|
45
|
-
(doc) => doc.c4fqns.entries().
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
(doc) => doc.c4fqns.entries().flatMap(([fqn, entry]) => {
|
|
43
|
+
if (filterByFqn(fqn)) {
|
|
44
|
+
const el = entry.el.deref();
|
|
45
|
+
if (el) {
|
|
46
|
+
return { ...entry, fqn, el, doc };
|
|
47
|
+
}
|
|
49
48
|
}
|
|
50
|
-
return
|
|
51
|
-
})
|
|
49
|
+
return [];
|
|
50
|
+
})
|
|
52
51
|
);
|
|
53
52
|
}
|
|
54
53
|
getFqn(el) {
|
|
55
|
-
|
|
56
|
-
if (fqn) {
|
|
57
|
-
const doc = getDocument(el);
|
|
58
|
-
if (isFqnIndexedDocument(doc) && doc.c4fqns.has(fqn)) {
|
|
59
|
-
return fqn;
|
|
60
|
-
}
|
|
61
|
-
const path = this.services.workspace.AstNodeLocator.getAstNodePath(el);
|
|
62
|
-
logError(`Clean cached FQN ${fqn} at ${path}`);
|
|
63
|
-
ElementOps.writeId(el, null);
|
|
64
|
-
fqn = null;
|
|
65
|
-
}
|
|
66
|
-
return fqn;
|
|
54
|
+
return ElementOps.readId(el) ?? null;
|
|
67
55
|
}
|
|
68
56
|
byFqn(fqn) {
|
|
69
57
|
return this.documents.flatMap((doc) => {
|
|
70
58
|
return doc.c4fqns.get(fqn).flatMap((entry) => {
|
|
71
59
|
const el = entry.el.deref();
|
|
72
60
|
if (el) {
|
|
73
|
-
return
|
|
61
|
+
return { fqn, el, doc, path: entry.path, name: entry.name };
|
|
74
62
|
}
|
|
75
63
|
return [];
|
|
76
64
|
});
|
|
@@ -240,9 +240,11 @@ ${printDocs(docs)}`);
|
|
|
240
240
|
);
|
|
241
241
|
const computedView = result.view;
|
|
242
242
|
computedView.nodes.forEach((node) => {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
if (!node.navigateTo) {
|
|
244
|
+
const navigateTo = R.find(allElementViews, (v) => v.viewOf === node.id);
|
|
245
|
+
if (navigateTo) {
|
|
246
|
+
node.navigateTo = navigateTo.id;
|
|
247
|
+
}
|
|
246
248
|
}
|
|
247
249
|
});
|
|
248
250
|
return computedView;
|
|
@@ -15,8 +15,9 @@ export declare class LikeC4ModelParser {
|
|
|
15
15
|
private parseElement;
|
|
16
16
|
private parseRelation;
|
|
17
17
|
private parseViews;
|
|
18
|
-
private
|
|
19
|
-
private
|
|
18
|
+
private parseElementExpr;
|
|
19
|
+
private parseCustomElementExpr;
|
|
20
|
+
private parsePredicateExpr;
|
|
20
21
|
private parseViewRule;
|
|
21
22
|
private parseElementView;
|
|
22
23
|
protected resolveFqn(node: ast.Element | ast.ExtendElement): c4.Fqn;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
InvalidModelError,
|
|
3
|
+
invariant,
|
|
4
|
+
isNonEmptyArray,
|
|
5
|
+
nonexhaustive
|
|
6
|
+
} from "@likec4/core";
|
|
2
7
|
import { getDocument } from "langium";
|
|
3
8
|
import objectHash from "object-hash";
|
|
4
9
|
import stripIndent from "strip-indent";
|
|
@@ -6,6 +11,7 @@ import {
|
|
|
6
11
|
ElementViewOps,
|
|
7
12
|
ast,
|
|
8
13
|
cleanParsedModel,
|
|
14
|
+
isFqnIndexedDocument,
|
|
9
15
|
isLikeC4LangiumDocument,
|
|
10
16
|
resolveRelationPoints,
|
|
11
17
|
streamModel,
|
|
@@ -16,6 +22,13 @@ import {
|
|
|
16
22
|
} from "../ast.js";
|
|
17
23
|
import { elementRef, getFqnElementRef } from "../elementRef.js";
|
|
18
24
|
import { logError, logWarnError, logger } from "../logger.js";
|
|
25
|
+
import { isTruthy } from "remeda";
|
|
26
|
+
function toSingleLine(str) {
|
|
27
|
+
return str?.split("\n").join(" ").trim();
|
|
28
|
+
}
|
|
29
|
+
function removeIndent(str) {
|
|
30
|
+
return str ? stripIndent(str).trim() : void 0;
|
|
31
|
+
}
|
|
19
32
|
export class LikeC4ModelParser {
|
|
20
33
|
constructor(services) {
|
|
21
34
|
this.services = services;
|
|
@@ -39,6 +52,7 @@ export class LikeC4ModelParser {
|
|
|
39
52
|
return result;
|
|
40
53
|
}
|
|
41
54
|
parseLikeC4Document(_doc) {
|
|
55
|
+
invariant(isFqnIndexedDocument(_doc), "Not a FqnIndexedDocument");
|
|
42
56
|
const doc = cleanParsedModel(_doc);
|
|
43
57
|
this.parseSpecification(doc);
|
|
44
58
|
this.parseModel(doc);
|
|
@@ -100,29 +114,26 @@ export class LikeC4ModelParser {
|
|
|
100
114
|
}
|
|
101
115
|
parseElement(astNode) {
|
|
102
116
|
const id = this.resolveFqn(astNode);
|
|
103
|
-
|
|
104
|
-
const kind = astNode.kind.ref.name;
|
|
117
|
+
const kind = astNode.kind.$refText;
|
|
105
118
|
const tags = this.convertTags(astNode.body);
|
|
106
119
|
const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
|
|
107
120
|
const styleProps = toElementStyleExcludeDefaults(stylePropsAst);
|
|
108
121
|
const astPath = this.getAstNodePath(astNode);
|
|
109
122
|
let [title, description, technology] = astNode.props;
|
|
110
|
-
const bodyProps = astNode.body?.props.filter(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
description = description ?? bodyProps.find((p) => p.key === "description")?.value;
|
|
115
|
-
technology = technology ?? bodyProps.find((p) => p.key === "technology")?.value;
|
|
123
|
+
const bodyProps = astNode.body?.props.filter(ast.isElementStringProperty) ?? [];
|
|
124
|
+
title = toSingleLine(title ?? bodyProps.find((p) => p.key === "title")?.value);
|
|
125
|
+
description = removeIndent(description ?? bodyProps.find((p) => p.key === "description")?.value);
|
|
126
|
+
technology = toSingleLine(technology ?? bodyProps.find((p) => p.key === "technology")?.value);
|
|
116
127
|
const links = astNode.body?.props.filter(ast.isLinkProperty).map((p) => p.value);
|
|
117
128
|
return {
|
|
118
129
|
id,
|
|
119
130
|
kind,
|
|
120
131
|
astPath,
|
|
121
|
-
title: title
|
|
132
|
+
title: title ?? astNode.name,
|
|
122
133
|
...tags && { tags },
|
|
123
134
|
...links && isNonEmptyArray(links) && { links },
|
|
124
|
-
...technology && { technology },
|
|
125
|
-
...description && { description
|
|
135
|
+
...isTruthy(technology) && { technology },
|
|
136
|
+
...isTruthy(description) && { description },
|
|
126
137
|
...styleProps
|
|
127
138
|
};
|
|
128
139
|
}
|
|
@@ -133,7 +144,7 @@ export class LikeC4ModelParser {
|
|
|
133
144
|
const tags = this.convertTags(astNode);
|
|
134
145
|
const kind = astNode.kind?.ref?.name;
|
|
135
146
|
const astPath = this.getAstNodePath(astNode);
|
|
136
|
-
const title = astNode.title ?? astNode.props.find((p) => p.key === "title")?.value ?? "";
|
|
147
|
+
const title = toSingleLine(astNode.title ?? astNode.props.find((p) => p.key === "title")?.value) ?? "";
|
|
137
148
|
const id = objectHash({
|
|
138
149
|
astPath,
|
|
139
150
|
source,
|
|
@@ -160,81 +171,130 @@ export class LikeC4ModelParser {
|
|
|
160
171
|
}
|
|
161
172
|
}
|
|
162
173
|
}
|
|
163
|
-
|
|
164
|
-
if (ast.
|
|
174
|
+
parseElementExpr(astNode) {
|
|
175
|
+
if (ast.isWildcardExpr(astNode)) {
|
|
165
176
|
return {
|
|
166
177
|
wildcard: true
|
|
167
178
|
};
|
|
168
179
|
}
|
|
169
|
-
if (ast.
|
|
170
|
-
invariant(
|
|
171
|
-
astNode.kind.ref,
|
|
172
|
-
"ElementKindExpression kind is not resolved: " + astNode.$cstNode?.text
|
|
173
|
-
);
|
|
180
|
+
if (ast.isElementKindExpr(astNode)) {
|
|
174
181
|
return {
|
|
175
|
-
elementKind: astNode.kind
|
|
182
|
+
elementKind: astNode.kind.$refText,
|
|
176
183
|
isEqual: astNode.isEqual
|
|
177
184
|
};
|
|
178
185
|
}
|
|
179
|
-
if (ast.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
186
|
+
if (ast.isElementTagExpr(astNode)) {
|
|
187
|
+
let elementTag = astNode.tag.$refText;
|
|
188
|
+
if (elementTag.startsWith("#")) {
|
|
189
|
+
elementTag = elementTag.slice(1);
|
|
190
|
+
}
|
|
184
191
|
return {
|
|
185
|
-
elementTag
|
|
192
|
+
elementTag,
|
|
186
193
|
isEqual: astNode.isEqual
|
|
187
194
|
};
|
|
188
195
|
}
|
|
189
|
-
if (ast.
|
|
190
|
-
const elementNode = elementRef(astNode.
|
|
191
|
-
invariant(elementNode, "Element not found " + astNode.
|
|
196
|
+
if (ast.isDescedantsExpr(astNode)) {
|
|
197
|
+
const elementNode = elementRef(astNode.parent);
|
|
198
|
+
invariant(elementNode, "Element not found " + astNode.parent.$cstNode?.text);
|
|
192
199
|
const element = this.resolveFqn(elementNode);
|
|
193
|
-
return
|
|
200
|
+
return {
|
|
194
201
|
element,
|
|
195
|
-
isDescedants:
|
|
196
|
-
}
|
|
202
|
+
isDescedants: true
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (ast.isElementRef(astNode)) {
|
|
206
|
+
const elementNode = elementRef(astNode);
|
|
207
|
+
invariant(elementNode, "Element not found " + astNode.$cstNode?.text);
|
|
208
|
+
const element = this.resolveFqn(elementNode);
|
|
209
|
+
return {
|
|
197
210
|
element
|
|
198
211
|
};
|
|
199
212
|
}
|
|
200
213
|
nonexhaustive(astNode);
|
|
201
214
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
215
|
+
parseCustomElementExpr(astNode) {
|
|
216
|
+
invariant(ast.isElementRef(astNode.target), "ElementRef expected as target of custom element");
|
|
217
|
+
const elementNode = elementRef(astNode.target);
|
|
218
|
+
invariant(elementNode, "element not found: " + astNode.$cstNode?.text);
|
|
219
|
+
const element = this.resolveFqn(elementNode);
|
|
220
|
+
const props = astNode.body?.props ?? [];
|
|
221
|
+
return props.reduce(
|
|
222
|
+
(acc, prop) => {
|
|
223
|
+
if (ast.isNavigateToProperty(prop)) {
|
|
224
|
+
const viewId = prop.value.view.$refText;
|
|
225
|
+
if (isTruthy(viewId)) {
|
|
226
|
+
acc.custom.navigateTo = viewId;
|
|
227
|
+
}
|
|
228
|
+
return acc;
|
|
229
|
+
}
|
|
230
|
+
if (ast.isElementStringProperty(prop)) {
|
|
231
|
+
const value = prop.key === "description" ? removeIndent(prop.value) : toSingleLine(prop.value);
|
|
232
|
+
acc.custom[prop.key] = value.trim();
|
|
233
|
+
return acc;
|
|
234
|
+
}
|
|
235
|
+
if (ast.isColorProperty(prop)) {
|
|
236
|
+
acc.custom[prop.key] = prop.value;
|
|
237
|
+
return acc;
|
|
238
|
+
}
|
|
239
|
+
if (ast.isShapeProperty(prop)) {
|
|
240
|
+
acc.custom[prop.key] = prop.value;
|
|
241
|
+
return acc;
|
|
242
|
+
}
|
|
243
|
+
nonexhaustive(prop);
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
custom: {
|
|
247
|
+
element
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
parsePredicateExpr(astNode) {
|
|
253
|
+
if (ast.isRelationExpr(astNode)) {
|
|
207
254
|
return {
|
|
208
|
-
|
|
255
|
+
source: this.parseElementExpr(astNode.source),
|
|
256
|
+
target: this.parseElementExpr(astNode.target)
|
|
209
257
|
};
|
|
210
258
|
}
|
|
211
|
-
if (ast.
|
|
259
|
+
if (ast.isInOutExpr(astNode)) {
|
|
212
260
|
return {
|
|
213
|
-
|
|
261
|
+
inout: this.parseElementExpr(astNode.inout.to)
|
|
214
262
|
};
|
|
215
263
|
}
|
|
216
|
-
if (ast.
|
|
264
|
+
if (ast.isOutgoingExpr(astNode)) {
|
|
217
265
|
return {
|
|
218
|
-
|
|
266
|
+
outgoing: this.parseElementExpr(astNode.from)
|
|
219
267
|
};
|
|
220
268
|
}
|
|
221
|
-
if (ast.
|
|
269
|
+
if (ast.isIncomingExpr(astNode)) {
|
|
222
270
|
return {
|
|
223
|
-
|
|
224
|
-
target: this.parseElementExpression(astNode.target)
|
|
271
|
+
incoming: this.parseElementExpr(astNode.to)
|
|
225
272
|
};
|
|
226
273
|
}
|
|
274
|
+
if (ast.isCustomElementExpr(astNode)) {
|
|
275
|
+
return this.parseCustomElementExpr(astNode);
|
|
276
|
+
}
|
|
277
|
+
if (ast.isElementExpr(astNode)) {
|
|
278
|
+
return this.parseElementExpr(astNode);
|
|
279
|
+
}
|
|
227
280
|
nonexhaustive(astNode);
|
|
228
281
|
}
|
|
229
282
|
parseViewRule(astRule) {
|
|
230
|
-
if (ast.
|
|
231
|
-
const exprs = astRule.expressions.
|
|
232
|
-
|
|
283
|
+
if (ast.isIncludePredicate(astRule) || ast.isExcludePredicate(astRule)) {
|
|
284
|
+
const exprs = astRule.expressions.flatMap((n) => {
|
|
285
|
+
try {
|
|
286
|
+
return this.parsePredicateExpr(n);
|
|
287
|
+
} catch (e) {
|
|
288
|
+
logWarnError(e);
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
return ast.isIncludePredicate(astRule) ? { include: exprs } : { exclude: exprs };
|
|
233
293
|
}
|
|
234
294
|
if (ast.isViewRuleStyle(astRule)) {
|
|
235
|
-
const styleProps = toElementStyle(astRule.
|
|
295
|
+
const styleProps = toElementStyle(astRule.styleprops);
|
|
236
296
|
return {
|
|
237
|
-
targets: astRule.targets.map((n) => this.
|
|
297
|
+
targets: astRule.targets.map((n) => this.parseElementExpr(n)),
|
|
238
298
|
style: {
|
|
239
299
|
...styleProps
|
|
240
300
|
}
|
package/dist/module.js
CHANGED
|
@@ -16,7 +16,7 @@ import { Rpc } from "./Rpc.js";
|
|
|
16
16
|
import { registerValidationChecks } from "./validation/index.js";
|
|
17
17
|
import { logger } from "./logger.js";
|
|
18
18
|
import { serializeError } from "@likec4/core";
|
|
19
|
-
import { NodeKindProvider } from "./shared/
|
|
19
|
+
import { WorkspaceSymbolProvider, NodeKindProvider } from "./shared/index.js";
|
|
20
20
|
function bind(Type) {
|
|
21
21
|
return (services) => new Type(services);
|
|
22
22
|
}
|
|
@@ -43,7 +43,8 @@ export const LikeC4Module = {
|
|
|
43
43
|
};
|
|
44
44
|
const LikeC4SharedModule = {
|
|
45
45
|
lsp: {
|
|
46
|
-
NodeKindProvider: () => new NodeKindProvider()
|
|
46
|
+
NodeKindProvider: (services) => new NodeKindProvider(services),
|
|
47
|
+
WorkspaceSymbolProvider: (services) => new WorkspaceSymbolProvider(services)
|
|
47
48
|
}
|
|
48
49
|
// workspace: {
|
|
49
50
|
// WorkspaceManager: services => new LikeC4WorkspaceManager(services)
|
|
@@ -2,54 +2,72 @@ import {
|
|
|
2
2
|
DefaultScopeComputation,
|
|
3
3
|
MultiMap
|
|
4
4
|
} from "langium";
|
|
5
|
-
import {
|
|
5
|
+
import { isEmpty, isTruthy } from "remeda";
|
|
6
6
|
import { ast } from "../ast.js";
|
|
7
7
|
import { logError } from "../logger.js";
|
|
8
8
|
export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
9
9
|
computeExports(document, _cancelToken) {
|
|
10
|
+
const docExports = [];
|
|
11
|
+
const { specifications, models, views } = document.parseResult.value;
|
|
10
12
|
try {
|
|
11
|
-
const { specifications, models, views } = document.parseResult.value;
|
|
12
|
-
const docExports = [];
|
|
13
13
|
for (const spec of specifications.flatMap((s) => s.elements)) {
|
|
14
|
-
if (spec.kind &&
|
|
14
|
+
if (spec.kind && isTruthy(spec.kind.name)) {
|
|
15
15
|
docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
} catch (e) {
|
|
19
|
+
logError(e);
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
18
22
|
for (const spec of specifications.flatMap((s) => s.tags)) {
|
|
19
|
-
if (spec.tag &&
|
|
20
|
-
docExports.push(this.descriptions.createDescription(spec.tag, spec.tag.name, document));
|
|
23
|
+
if (spec.tag && isTruthy(spec.tag.name)) {
|
|
21
24
|
docExports.push(
|
|
22
25
|
this.descriptions.createDescription(spec.tag, "#" + spec.tag.name, document)
|
|
23
26
|
);
|
|
24
27
|
}
|
|
25
28
|
}
|
|
29
|
+
} catch (e) {
|
|
30
|
+
logError(e);
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
26
33
|
for (const spec of specifications.flatMap((s) => s.relationships)) {
|
|
27
|
-
if (spec.kind &&
|
|
34
|
+
if (spec.kind && isTruthy(spec.kind.name)) {
|
|
28
35
|
docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
|
|
29
36
|
}
|
|
30
37
|
}
|
|
38
|
+
} catch (e) {
|
|
39
|
+
logError(e);
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
31
42
|
for (const elAst of models.flatMap((m) => m.elements)) {
|
|
32
43
|
if (ast.isElement(elAst) && !isEmpty(elAst.name)) {
|
|
33
44
|
docExports.push(this.descriptions.createDescription(elAst, elAst.name, document));
|
|
34
45
|
}
|
|
35
46
|
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
logError(e);
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
36
51
|
for (const viewAst of views.flatMap((v) => v.views)) {
|
|
37
|
-
if (
|
|
52
|
+
if (isTruthy(viewAst.name)) {
|
|
38
53
|
docExports.push(this.descriptions.createDescription(viewAst, viewAst.name, document));
|
|
39
54
|
}
|
|
40
55
|
}
|
|
41
|
-
return Promise.resolve(docExports);
|
|
42
56
|
} catch (e) {
|
|
43
57
|
logError(e);
|
|
44
|
-
return Promise.reject(e);
|
|
45
58
|
}
|
|
59
|
+
return Promise.resolve(docExports);
|
|
46
60
|
}
|
|
47
61
|
async computeLocalScopes(document, _cancelToken) {
|
|
48
62
|
const root = document.parseResult.value;
|
|
49
63
|
const scopes = new MultiMap();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
for (const model of root.models) {
|
|
65
|
+
try {
|
|
66
|
+
const nested = this.processContainer(model, scopes, document);
|
|
67
|
+
scopes.addAll(root, nested.values());
|
|
68
|
+
} catch (e) {
|
|
69
|
+
logError(e);
|
|
70
|
+
}
|
|
53
71
|
}
|
|
54
72
|
return Promise.resolve(scopes);
|
|
55
73
|
}
|
|
@@ -61,8 +79,10 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
61
79
|
continue;
|
|
62
80
|
}
|
|
63
81
|
let subcontainer;
|
|
64
|
-
if (ast.isElement(el)
|
|
65
|
-
|
|
82
|
+
if (ast.isElement(el)) {
|
|
83
|
+
if (isTruthy(el.name)) {
|
|
84
|
+
localScope.add(el.name, this.descriptions.createDescription(el, el.name, document));
|
|
85
|
+
}
|
|
66
86
|
subcontainer = el.body;
|
|
67
87
|
} else if (ast.isExtendElement(el)) {
|
|
68
88
|
subcontainer = el.body;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { AstNode } from 'langium';
|
|
2
1
|
import { DefaultScopeProvider, type ReferenceInfo, type Scope } from 'langium';
|
|
3
2
|
import type { LikeC4Services } from '../module';
|
|
4
3
|
export declare class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
@@ -10,7 +9,7 @@ export declare class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
10
9
|
private scopeExtendElement;
|
|
11
10
|
private scopeElementView;
|
|
12
11
|
getScope(context: ReferenceInfo): Scope;
|
|
13
|
-
protected computeScope(
|
|
12
|
+
protected computeScope(context: ReferenceInfo): Scope;
|
|
14
13
|
/**
|
|
15
14
|
* Create a global scope filtered for the given reference type.
|
|
16
15
|
*/
|
|
@@ -58,15 +58,12 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
58
58
|
scopeElementRef(ref) {
|
|
59
59
|
return this.uniqueDescedants(() => ref.el.ref);
|
|
60
60
|
}
|
|
61
|
-
scopeExtendElement(
|
|
62
|
-
return this.uniqueDescedants(() => elementRef(
|
|
61
|
+
scopeExtendElement({ element }) {
|
|
62
|
+
return stream([element.el.$nodeDescription]).nonNullable().concat(this.uniqueDescedants(() => elementRef(element)));
|
|
63
63
|
}
|
|
64
64
|
scopeElementView({ viewOf, extends: ext }) {
|
|
65
65
|
if (viewOf) {
|
|
66
|
-
return stream([viewOf]).
|
|
67
|
-
const el = elementRef(v);
|
|
68
|
-
return el ? this.descriptions.createDescription(el, el.name) : [];
|
|
69
|
-
}).concat(this.uniqueDescedants(() => elementRef(viewOf)));
|
|
66
|
+
return stream([viewOf.el.$nodeDescription]).nonNullable().concat(this.uniqueDescedants(() => elementRef(viewOf)));
|
|
70
67
|
}
|
|
71
68
|
if (ext) {
|
|
72
69
|
return stream([ext]).flatMap((v) => {
|
|
@@ -93,38 +90,33 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
93
90
|
return new StreamScope(this.scopeElementRef(parent));
|
|
94
91
|
}
|
|
95
92
|
}
|
|
96
|
-
return this.computeScope(
|
|
93
|
+
return this.computeScope(context);
|
|
97
94
|
} catch (e) {
|
|
98
95
|
logError(e);
|
|
99
96
|
return this.getGlobalScope(referenceType);
|
|
100
97
|
}
|
|
101
98
|
}
|
|
102
|
-
computeScope(
|
|
99
|
+
computeScope(context) {
|
|
100
|
+
const referenceType = this.reflection.getReferenceType(context);
|
|
103
101
|
const scopes = [];
|
|
104
|
-
const doc = getDocument(
|
|
102
|
+
const doc = getDocument(context.container);
|
|
105
103
|
const precomputed = doc.precomputedScopes;
|
|
106
104
|
const byReferenceType = (desc) => this.reflection.isSubtype(desc.type, referenceType);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
let container = context.container;
|
|
106
|
+
while (container) {
|
|
107
|
+
const elements = precomputed?.get(container).filter(byReferenceType);
|
|
108
|
+
if (elements && elements.length > 0) {
|
|
110
109
|
scopes.push(stream(elements));
|
|
111
110
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (elements2.length > 0) {
|
|
116
|
-
scopes.push(stream(elements2));
|
|
111
|
+
if (referenceType === ast.Element) {
|
|
112
|
+
if (ast.isExtendElementBody(container)) {
|
|
113
|
+
scopes.push(this.scopeExtendElement(container.$container));
|
|
117
114
|
}
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
scopes.push(this.scopeExtendElement(container.$container));
|
|
121
|
-
}
|
|
122
|
-
if (ast.isElementViewBody(container)) {
|
|
123
|
-
scopes.push(this.scopeElementView(container.$container));
|
|
124
|
-
}
|
|
115
|
+
if (ast.isElementViewBody(container)) {
|
|
116
|
+
scopes.push(this.scopeElementView(container.$container));
|
|
125
117
|
}
|
|
126
|
-
container = container.$container;
|
|
127
118
|
}
|
|
119
|
+
container = container.$container;
|
|
128
120
|
}
|
|
129
121
|
return scopes.reduceRight((outerScope, elements) => {
|
|
130
122
|
return this.createScope(elements, outerScope);
|