@likec4/language-server 0.45.0 → 0.46.1
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 +9 -8
- package/dist/Rpc.js +14 -60
- package/dist/ast.d.ts +9 -10
- package/dist/ast.js +14 -22
- package/dist/elementRef.d.ts +5 -11
- package/dist/elementRef.js +5 -32
- package/dist/generated/ast.d.ts +62 -60
- package/dist/generated/ast.js +59 -33
- package/dist/generated/grammar.d.ts +1 -1
- package/dist/generated/grammar.js +1 -1
- package/dist/generated/module.d.ts +1 -1
- package/dist/generated/module.js +1 -0
- package/dist/lsp/CodeLensProvider.js +2 -1
- package/dist/lsp/DocumentSymbolProvider.d.ts +1 -1
- package/dist/lsp/DocumentSymbolProvider.js +8 -5
- package/dist/lsp/SemanticTokenProvider.js +29 -63
- package/dist/model/fqn-computation.js +5 -5
- package/dist/model/fqn-index.d.ts +2 -1
- package/dist/model/fqn-index.js +26 -31
- package/dist/model/model-builder.js +20 -11
- package/dist/model/model-locator.js +4 -11
- package/dist/model/model-parser.d.ts +6 -3
- package/dist/model/model-parser.js +41 -36
- package/dist/module.js +4 -0
- package/dist/protocol.d.ts +0 -3
- package/dist/protocol.js +1 -4
- package/dist/references/scope-computation.js +19 -17
- package/dist/references/scope-provider.js +19 -19
- package/dist/shared/NodeKindProvider.d.ts +13 -0
- package/dist/shared/NodeKindProvider.js +29 -0
- package/dist/test/testServices.js +8 -8
- package/dist/utils.js +1 -1
- package/dist/validation/index.js +13 -3
- package/dist/validation/relation.js +12 -18
- package/dist/validation/specification.d.ts +3 -0
- package/dist/validation/specification.js +30 -0
- package/package.json +9 -9
|
@@ -44,12 +44,15 @@ export class LikeC4DocumentSymbolProvider {
|
|
|
44
44
|
constructor(services) {
|
|
45
45
|
this.services = services;
|
|
46
46
|
}
|
|
47
|
-
getSymbols(
|
|
48
|
-
|
|
47
|
+
getSymbols({
|
|
48
|
+
parseResult: {
|
|
49
|
+
value: { specifications, models, views }
|
|
50
|
+
}
|
|
51
|
+
}) {
|
|
49
52
|
return [
|
|
50
|
-
() =>
|
|
51
|
-
() =>
|
|
52
|
-
() =>
|
|
53
|
+
...specifications.map((s) => () => this.getSpecSymbol(s)),
|
|
54
|
+
...models.map((s) => () => this.getModelSymbol(s)),
|
|
55
|
+
...views.map((s) => () => this.getModelViewsSymbol(s))
|
|
53
56
|
].flatMap((fn) => {
|
|
54
57
|
try {
|
|
55
58
|
return fn() ?? [];
|
|
@@ -1,38 +1,21 @@
|
|
|
1
1
|
import { AbstractSemanticTokenProvider } from "langium";
|
|
2
2
|
import { SemanticTokenModifiers, SemanticTokenTypes } from "vscode-languageserver-protocol";
|
|
3
3
|
import { ast } from "../ast.js";
|
|
4
|
-
import { isElementRefHead } from "../elementRef.js";
|
|
5
4
|
export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
6
5
|
highlightElement(node, acceptor) {
|
|
7
|
-
const keyword = (keyword2, _index) => acceptor({
|
|
8
|
-
node,
|
|
9
|
-
keyword: keyword2,
|
|
10
|
-
type: SemanticTokenTypes.keyword,
|
|
11
|
-
modifier: [SemanticTokenModifiers.defaultLibrary]
|
|
12
|
-
});
|
|
13
|
-
if ("arr" in node) {
|
|
14
|
-
acceptor({
|
|
15
|
-
node,
|
|
16
|
-
property: "arr",
|
|
17
|
-
type: SemanticTokenTypes.keyword,
|
|
18
|
-
modifier: [SemanticTokenModifiers.defaultLibrary]
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
6
|
if (ast.isRelation(node) && "kind" in node) {
|
|
22
|
-
keyword("-[");
|
|
23
7
|
acceptor({
|
|
24
8
|
node,
|
|
25
9
|
property: "kind",
|
|
26
10
|
type: SemanticTokenTypes.type,
|
|
27
11
|
modifier: [SemanticTokenModifiers.definition]
|
|
28
12
|
});
|
|
29
|
-
keyword("]->");
|
|
30
13
|
}
|
|
31
|
-
if (ast.isElementRef(node) || ast.
|
|
14
|
+
if (ast.isElementRef(node) || ast.isFqnElementRef(node)) {
|
|
32
15
|
acceptor({
|
|
33
16
|
node,
|
|
34
17
|
property: "el",
|
|
35
|
-
type:
|
|
18
|
+
type: node.parent ? SemanticTokenTypes.property : SemanticTokenTypes.variable
|
|
36
19
|
});
|
|
37
20
|
}
|
|
38
21
|
if (ast.isElementViewRef(node)) {
|
|
@@ -42,6 +25,13 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
42
25
|
type: SemanticTokenTypes.variable
|
|
43
26
|
});
|
|
44
27
|
}
|
|
28
|
+
if (ast.isElementRefExpression(node) && node.isDescedants) {
|
|
29
|
+
acceptor({
|
|
30
|
+
node,
|
|
31
|
+
keyword: ".*",
|
|
32
|
+
type: SemanticTokenTypes.variable
|
|
33
|
+
});
|
|
34
|
+
}
|
|
45
35
|
if (ast.isWildcardExpression(node)) {
|
|
46
36
|
acceptor({
|
|
47
37
|
node,
|
|
@@ -49,40 +39,26 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
49
39
|
type: SemanticTokenTypes.variable
|
|
50
40
|
});
|
|
51
41
|
}
|
|
52
|
-
if (ast.isElementKindExpression(node)
|
|
53
|
-
keyword("element");
|
|
54
|
-
if (ast.isElementKindExpression(node)) {
|
|
55
|
-
keyword("kind");
|
|
56
|
-
acceptor({
|
|
57
|
-
node,
|
|
58
|
-
property: "kind",
|
|
59
|
-
type: SemanticTokenTypes.type,
|
|
60
|
-
modifier: [SemanticTokenModifiers.definition]
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
if (ast.isElementTagExpression(node)) {
|
|
64
|
-
keyword("tag");
|
|
65
|
-
acceptor({
|
|
66
|
-
node,
|
|
67
|
-
property: "tag",
|
|
68
|
-
type: SemanticTokenTypes.type,
|
|
69
|
-
modifier: [SemanticTokenModifiers.definition]
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (ast.isRelation(node)) {
|
|
74
|
-
if ("title" in node) {
|
|
75
|
-
acceptor({
|
|
76
|
-
node,
|
|
77
|
-
property: "title",
|
|
78
|
-
type: SemanticTokenTypes.string
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (ast.isElementKind(node)) {
|
|
42
|
+
if (ast.isElementKindExpression(node)) {
|
|
83
43
|
acceptor({
|
|
84
44
|
node,
|
|
85
|
-
property: "
|
|
45
|
+
property: "kind",
|
|
46
|
+
type: SemanticTokenTypes.type,
|
|
47
|
+
modifier: [SemanticTokenModifiers.definition]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (ast.isElementTagExpression(node)) {
|
|
51
|
+
acceptor({
|
|
52
|
+
node,
|
|
53
|
+
property: "tag",
|
|
54
|
+
type: SemanticTokenTypes.type,
|
|
55
|
+
modifier: [SemanticTokenModifiers.definition]
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (ast.isSpecificationElementKind(node)) {
|
|
59
|
+
acceptor({
|
|
60
|
+
node,
|
|
61
|
+
property: "kind",
|
|
86
62
|
type: SemanticTokenTypes.type,
|
|
87
63
|
modifier: [SemanticTokenModifiers.definition]
|
|
88
64
|
});
|
|
@@ -103,20 +79,15 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
103
79
|
modifier: [SemanticTokenModifiers.definition]
|
|
104
80
|
});
|
|
105
81
|
}
|
|
106
|
-
if (ast.
|
|
82
|
+
if (ast.isSpecificationRelationshipKind(node)) {
|
|
107
83
|
acceptor({
|
|
108
84
|
node,
|
|
109
|
-
property: "
|
|
85
|
+
property: "kind",
|
|
110
86
|
type: SemanticTokenTypes.type,
|
|
111
87
|
modifier: [SemanticTokenModifiers.definition]
|
|
112
88
|
});
|
|
113
89
|
}
|
|
114
90
|
if (ast.isColorProperty(node) || ast.isShapeProperty(node) || ast.isArrowProperty(node) || ast.isLineProperty(node)) {
|
|
115
|
-
acceptor({
|
|
116
|
-
node,
|
|
117
|
-
property: "key",
|
|
118
|
-
type: SemanticTokenTypes.keyword
|
|
119
|
-
});
|
|
120
91
|
acceptor({
|
|
121
92
|
node,
|
|
122
93
|
property: "value",
|
|
@@ -124,11 +95,6 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
124
95
|
});
|
|
125
96
|
}
|
|
126
97
|
if (ast.isLinkProperty(node) || ast.isIconProperty(node)) {
|
|
127
|
-
acceptor({
|
|
128
|
-
node,
|
|
129
|
-
property: "key",
|
|
130
|
-
type: SemanticTokenTypes.keyword
|
|
131
|
-
});
|
|
132
98
|
acceptor({
|
|
133
99
|
node,
|
|
134
100
|
property: "value",
|
|
@@ -2,15 +2,15 @@ import { AsFqn, nonexhaustive } from "@likec4/core";
|
|
|
2
2
|
import { MultiMap } from "langium";
|
|
3
3
|
import { isEmpty, isNil } from "remeda";
|
|
4
4
|
import { ElementOps, ast } from "../ast.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getFqnElementRef } from "../elementRef.js";
|
|
6
6
|
export function computeDocumentFqn(document, services) {
|
|
7
7
|
const c4fqns = document.c4fqns = new MultiMap();
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
8
|
+
const elements = document.parseResult.value.models.flatMap((m) => m.elements);
|
|
9
|
+
if (elements.length === 0) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
const locator = services.workspace.AstNodeLocator;
|
|
13
|
-
const traverseStack =
|
|
13
|
+
const traverseStack = elements.map((el) => [el, null]);
|
|
14
14
|
let pair;
|
|
15
15
|
while (pair = traverseStack.shift()) {
|
|
16
16
|
const [el, parent] = pair;
|
|
@@ -19,7 +19,7 @@ export function computeDocumentFqn(document, services) {
|
|
|
19
19
|
}
|
|
20
20
|
if (ast.isExtendElement(el)) {
|
|
21
21
|
if (!isNil(el.body) && !isEmpty(el.body.elements)) {
|
|
22
|
-
const fqn =
|
|
22
|
+
const fqn = getFqnElementRef(el.element);
|
|
23
23
|
el.body.elements.forEach((child) => traverseStack.push([child, fqn]));
|
|
24
24
|
}
|
|
25
25
|
continue;
|
|
@@ -15,9 +15,10 @@ export interface FqnIndexEntry {
|
|
|
15
15
|
path: string;
|
|
16
16
|
}
|
|
17
17
|
export declare class FqnIndex {
|
|
18
|
+
private services;
|
|
18
19
|
protected langiumDocuments: LangiumDocuments;
|
|
19
20
|
constructor(services: LikeC4Services);
|
|
20
|
-
|
|
21
|
+
get documents(): Stream<FqnIndexedDocument>;
|
|
21
22
|
private entries;
|
|
22
23
|
getFqn(el: ast.Element): Fqn | null;
|
|
23
24
|
byFqn(fqn: Fqn): Stream<FqnIndexEntry>;
|
package/dist/model/fqn-index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { nameFromFqn, parentFqn } from "@likec4/core";
|
|
2
|
-
import { DONE_RESULT, DocumentState, MultiMap, StreamImpl, stream } from "langium";
|
|
2
|
+
import { DONE_RESULT, DocumentState, MultiMap, StreamImpl, getDocument, stream } from "langium";
|
|
3
3
|
import { isNil } from "remeda";
|
|
4
4
|
import { ElementOps, isLikeC4LangiumDocument } from "../ast.js";
|
|
5
5
|
import { logError, logger } from "../logger.js";
|
|
@@ -9,40 +9,21 @@ export function isFqnIndexedDocument(doc) {
|
|
|
9
9
|
return isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns);
|
|
10
10
|
}
|
|
11
11
|
export class FqnIndex {
|
|
12
|
-
langiumDocuments;
|
|
13
12
|
constructor(services) {
|
|
13
|
+
this.services = services;
|
|
14
14
|
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
15
|
-
services.shared.workspace.DocumentBuilder.onUpdate((changed, deleted) => {
|
|
16
|
-
const message = [`[FqnIndex] onUpdate`];
|
|
17
|
-
if (changed.length > 0) {
|
|
18
|
-
message.push(` changed:`);
|
|
19
|
-
changed.forEach((u) => message.push(` - ${u}`));
|
|
20
|
-
}
|
|
21
|
-
if (deleted.length > 0) {
|
|
22
|
-
message.push(` deleted:`);
|
|
23
|
-
deleted.forEach((u) => message.push(` - ${u}`));
|
|
24
|
-
}
|
|
25
|
-
logger.debug(message.join("\n"));
|
|
26
|
-
for (const uri of changed) {
|
|
27
|
-
if (this.langiumDocuments.hasDocument(uri)) {
|
|
28
|
-
const doc = this.langiumDocuments.getOrCreateDocument(uri);
|
|
29
|
-
if (isLikeC4LangiumDocument(doc)) {
|
|
30
|
-
delete doc.c4fqns;
|
|
31
|
-
delete doc.c4Elements;
|
|
32
|
-
delete doc.c4Specification;
|
|
33
|
-
delete doc.c4Relations;
|
|
34
|
-
delete doc.c4Views;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
15
|
services.shared.workspace.DocumentBuilder.onBuildPhase(
|
|
40
16
|
DocumentState.IndexedContent,
|
|
41
|
-
(docs, _cancelToken) => {
|
|
17
|
+
async (docs, _cancelToken) => {
|
|
42
18
|
logger.debug(`[FqnIndex] onIndexedContent ${docs.length}:
|
|
43
19
|
` + printDocs(docs));
|
|
44
20
|
for (const doc of docs) {
|
|
45
21
|
if (isLikeC4LangiumDocument(doc)) {
|
|
22
|
+
delete doc.c4fqns;
|
|
23
|
+
delete doc.c4Elements;
|
|
24
|
+
delete doc.c4Specification;
|
|
25
|
+
delete doc.c4Relations;
|
|
26
|
+
delete doc.c4Views;
|
|
46
27
|
try {
|
|
47
28
|
computeDocumentFqn(doc, services);
|
|
48
29
|
} catch (e) {
|
|
@@ -50,14 +31,17 @@ export class FqnIndex {
|
|
|
50
31
|
}
|
|
51
32
|
}
|
|
52
33
|
}
|
|
34
|
+
return Promise.resolve();
|
|
53
35
|
}
|
|
54
36
|
);
|
|
37
|
+
logger.debug(`[FqnIndex] Created`);
|
|
55
38
|
}
|
|
56
|
-
|
|
39
|
+
langiumDocuments;
|
|
40
|
+
get documents() {
|
|
57
41
|
return this.langiumDocuments.all.filter(isFqnIndexedDocument);
|
|
58
42
|
}
|
|
59
43
|
entries(filterByFqn = () => true) {
|
|
60
|
-
return this.documents
|
|
44
|
+
return this.documents.flatMap(
|
|
61
45
|
(doc) => doc.c4fqns.entries().filter(([fqn]) => filterByFqn(fqn)).map(([fqn, entry]) => {
|
|
62
46
|
const el = entry.el.deref();
|
|
63
47
|
if (el) {
|
|
@@ -68,10 +52,21 @@ export class FqnIndex {
|
|
|
68
52
|
);
|
|
69
53
|
}
|
|
70
54
|
getFqn(el) {
|
|
71
|
-
|
|
55
|
+
let fqn = ElementOps.readId(el) ?? null;
|
|
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;
|
|
72
67
|
}
|
|
73
68
|
byFqn(fqn) {
|
|
74
|
-
return this.documents
|
|
69
|
+
return this.documents.flatMap((doc) => {
|
|
75
70
|
return doc.c4fqns.get(fqn).flatMap((entry) => {
|
|
76
71
|
const el = entry.el.deref();
|
|
77
72
|
if (el) {
|
|
@@ -4,10 +4,7 @@ import {
|
|
|
4
4
|
parentFqn
|
|
5
5
|
} from "@likec4/core";
|
|
6
6
|
import { LikeC4ModelGraph, computeView } from "@likec4/graph";
|
|
7
|
-
import {
|
|
8
|
-
DocumentState,
|
|
9
|
-
interruptAndCheck
|
|
10
|
-
} from "langium";
|
|
7
|
+
import { DocumentState } from "langium";
|
|
11
8
|
import * as R from "remeda";
|
|
12
9
|
import { Disposable } from "vscode-languageserver";
|
|
13
10
|
import { isValidLikeC4LangiumDocument } from "../ast.js";
|
|
@@ -161,16 +158,24 @@ export class LikeC4ModelBuilder {
|
|
|
161
158
|
this.services = services;
|
|
162
159
|
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
163
160
|
const parser = services.likec4.ModelParser;
|
|
161
|
+
services.shared.workspace.DocumentBuilder.onUpdate((changed, deleted) => {
|
|
162
|
+
if (deleted.length > 0) {
|
|
163
|
+
this.notifyListeners(deleted);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
164
166
|
services.shared.workspace.DocumentBuilder.onBuildPhase(
|
|
165
167
|
DocumentState.Validated,
|
|
166
|
-
async (docs,
|
|
168
|
+
async (docs, _cancelToken) => {
|
|
169
|
+
logger.debug(`[ModelBuilder] onValidated (${docs.length} docs)
|
|
170
|
+
${printDocs(docs)}`);
|
|
167
171
|
const parsed = parser.parse(docs).map((d) => d.uri);
|
|
168
|
-
await interruptAndCheck(cancelToken);
|
|
169
172
|
if (parsed.length > 0) {
|
|
170
173
|
this.notifyListeners(parsed);
|
|
171
174
|
}
|
|
175
|
+
return Promise.resolve();
|
|
172
176
|
}
|
|
173
177
|
);
|
|
178
|
+
logger.debug(`[ModelBuilder] Created`);
|
|
174
179
|
}
|
|
175
180
|
langiumDocuments;
|
|
176
181
|
listeners = [];
|
|
@@ -200,11 +205,15 @@ ${printDocs(docs)}`);
|
|
|
200
205
|
return null;
|
|
201
206
|
}
|
|
202
207
|
const index = new LikeC4ModelGraph(model);
|
|
203
|
-
const views =
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
const views = [];
|
|
209
|
+
for (const view of R.values(model.views)) {
|
|
210
|
+
const result = computeView(view, index);
|
|
211
|
+
if (!result.isSuccess) {
|
|
212
|
+
logWarnError(result.error);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
views.push(result.view);
|
|
216
|
+
}
|
|
208
217
|
assignNavigateTo(views);
|
|
209
218
|
return {
|
|
210
219
|
elements: model.elements,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { findNodeForProperty, getDocument } from "langium";
|
|
1
|
+
import { findNodeForKeyword, findNodeForProperty, getDocument } from "langium";
|
|
3
2
|
import { ast, isParsedLikeC4LangiumDocument } from "../ast.js";
|
|
4
3
|
export class LikeC4ModelLocator {
|
|
5
4
|
constructor(services) {
|
|
@@ -61,20 +60,14 @@ export class LikeC4ModelLocator {
|
|
|
61
60
|
};
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
const targetNode = findNodeForProperty(node.$cstNode, "arr");
|
|
68
|
-
if (!targetNode) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
63
|
+
const targetNode = (node.kind ? findNodeForProperty(node.$cstNode, "kind") : findNodeForKeyword(node.$cstNode, "->")) ?? findNodeForProperty(node.$cstNode, "target");
|
|
64
|
+
return targetNode ? {
|
|
72
65
|
uri: doc.uri.toString(),
|
|
73
66
|
range: {
|
|
74
67
|
start: targetNode.range.start,
|
|
75
68
|
end: targetNode.range.end
|
|
76
69
|
}
|
|
77
|
-
};
|
|
70
|
+
} : null;
|
|
78
71
|
}
|
|
79
72
|
return null;
|
|
80
73
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type c4 } from '@likec4/core';
|
|
2
2
|
import type { LangiumDocument } from 'langium';
|
|
3
|
-
import type { LikeC4LangiumDocument } from '../ast';
|
|
3
|
+
import type { LikeC4LangiumDocument, ParsedLikeC4LangiumDocument } from '../ast';
|
|
4
4
|
import { ast } from '../ast';
|
|
5
5
|
import type { LikeC4Services } from '../module';
|
|
6
6
|
export type ModelParsedListener = () => void;
|
|
@@ -8,10 +8,13 @@ export declare class LikeC4ModelParser {
|
|
|
8
8
|
private services;
|
|
9
9
|
private fqnIndex;
|
|
10
10
|
constructor(services: LikeC4Services);
|
|
11
|
-
parse(doc: LangiumDocument | LangiumDocument[]):
|
|
12
|
-
protected parseLikeC4Document(
|
|
11
|
+
parse(doc: LangiumDocument | LangiumDocument[]): ParsedLikeC4LangiumDocument[];
|
|
12
|
+
protected parseLikeC4Document(_doc: LikeC4LangiumDocument): ParsedLikeC4LangiumDocument;
|
|
13
|
+
private parseSpecification;
|
|
14
|
+
private parseModel;
|
|
13
15
|
private parseElement;
|
|
14
16
|
private parseRelation;
|
|
17
|
+
private parseViews;
|
|
15
18
|
private parseElementExpression;
|
|
16
19
|
private parseExpression;
|
|
17
20
|
private parseViewRule;
|
|
@@ -14,44 +14,47 @@ import {
|
|
|
14
14
|
toElementStyleExcludeDefaults,
|
|
15
15
|
toRelationshipStyleExcludeDefaults
|
|
16
16
|
} from "../ast.js";
|
|
17
|
-
import { elementRef,
|
|
17
|
+
import { elementRef, getFqnElementRef } from "../elementRef.js";
|
|
18
18
|
import { logError, logWarnError, logger } from "../logger.js";
|
|
19
|
-
import { printDocs } from "../utils.js";
|
|
20
19
|
export class LikeC4ModelParser {
|
|
21
20
|
constructor(services) {
|
|
22
21
|
this.services = services;
|
|
23
22
|
this.fqnIndex = services.likec4.FqnIndex;
|
|
23
|
+
logger.debug(`[ModelParser] Created`);
|
|
24
24
|
}
|
|
25
25
|
fqnIndex;
|
|
26
26
|
parse(doc) {
|
|
27
27
|
const docs = Array.isArray(doc) ? doc : [doc];
|
|
28
|
-
logger.debug(`[ModelParser] onValidated (${docs.length} docs)
|
|
29
|
-
${printDocs(docs)}`);
|
|
30
28
|
const result = [];
|
|
31
29
|
for (const doc2 of docs) {
|
|
32
30
|
if (!isLikeC4LangiumDocument(doc2)) {
|
|
33
31
|
continue;
|
|
34
32
|
}
|
|
35
33
|
try {
|
|
36
|
-
this.parseLikeC4Document(doc2);
|
|
37
|
-
result.push(doc2);
|
|
34
|
+
result.push(this.parseLikeC4Document(doc2));
|
|
38
35
|
} catch (cause) {
|
|
39
36
|
logError(new InvalidModelError(`Error parsing document ${doc2.uri.toString()}`, { cause }));
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
return result;
|
|
43
40
|
}
|
|
44
|
-
parseLikeC4Document(
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
parseLikeC4Document(_doc) {
|
|
42
|
+
const doc = cleanParsedModel(_doc);
|
|
43
|
+
this.parseSpecification(doc);
|
|
44
|
+
this.parseModel(doc);
|
|
45
|
+
this.parseViews(doc);
|
|
46
|
+
return doc;
|
|
47
|
+
}
|
|
48
|
+
parseSpecification({ parseResult, c4Specification }) {
|
|
49
|
+
const element_specs = parseResult.value.specifications.flatMap((s) => s.elements);
|
|
50
|
+
if (element_specs.length > 0) {
|
|
51
|
+
for (const { kind, style } of element_specs) {
|
|
52
|
+
if (kind.name in c4Specification.kinds) {
|
|
53
|
+
logger.warn(`Duplicate specification for element kind ${kind.name}`);
|
|
51
54
|
continue;
|
|
52
55
|
}
|
|
53
56
|
try {
|
|
54
|
-
|
|
57
|
+
c4Specification.kinds[kind.name] = toElementStyleExcludeDefaults(
|
|
55
58
|
style?.props
|
|
56
59
|
);
|
|
57
60
|
} catch (e) {
|
|
@@ -59,24 +62,26 @@ ${printDocs(docs)}`);
|
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
|
-
const relations_specs =
|
|
63
|
-
if (relations_specs) {
|
|
65
|
+
const relations_specs = parseResult.value.specifications.flatMap((s) => s.relationships);
|
|
66
|
+
if (relations_specs.length > 0) {
|
|
64
67
|
for (const { kind, props } of relations_specs) {
|
|
65
|
-
if (kind.name in
|
|
66
|
-
logger.warn(`Duplicate specification for kind ${kind.name}`);
|
|
68
|
+
if (kind.name in c4Specification.relationships) {
|
|
69
|
+
logger.warn(`Duplicate specification for relationship kind ${kind.name}`);
|
|
67
70
|
continue;
|
|
68
71
|
}
|
|
69
72
|
try {
|
|
70
|
-
|
|
73
|
+
c4Specification.relationships[kind.name] = toRelationshipStyleExcludeDefaults(props);
|
|
71
74
|
} catch (e) {
|
|
72
75
|
logWarnError(e);
|
|
73
76
|
}
|
|
74
77
|
}
|
|
75
78
|
}
|
|
79
|
+
}
|
|
80
|
+
parseModel(doc) {
|
|
76
81
|
for (const el of streamModel(doc)) {
|
|
77
82
|
if (ast.isElement(el)) {
|
|
78
83
|
try {
|
|
79
|
-
|
|
84
|
+
doc.c4Elements.push(this.parseElement(el));
|
|
80
85
|
} catch (e) {
|
|
81
86
|
logWarnError(e);
|
|
82
87
|
}
|
|
@@ -84,7 +89,7 @@ ${printDocs(docs)}`);
|
|
|
84
89
|
}
|
|
85
90
|
if (ast.isRelation(el)) {
|
|
86
91
|
try {
|
|
87
|
-
|
|
92
|
+
doc.c4Relations.push(this.parseRelation(el));
|
|
88
93
|
} catch (e) {
|
|
89
94
|
logWarnError(e);
|
|
90
95
|
}
|
|
@@ -92,18 +97,6 @@ ${printDocs(docs)}`);
|
|
|
92
97
|
}
|
|
93
98
|
nonexhaustive(el);
|
|
94
99
|
}
|
|
95
|
-
const docviews = doc.parseResult.value.views?.views;
|
|
96
|
-
if (docviews) {
|
|
97
|
-
for (const view of docviews) {
|
|
98
|
-
try {
|
|
99
|
-
const v = this.parseElementView(view);
|
|
100
|
-
ElementViewOps.writeId(view, v.id);
|
|
101
|
-
views.push(v);
|
|
102
|
-
} catch (e) {
|
|
103
|
-
logWarnError(e);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
100
|
}
|
|
108
101
|
parseElement(astNode) {
|
|
109
102
|
const id = this.resolveFqn(astNode);
|
|
@@ -137,10 +130,10 @@ ${printDocs(docs)}`);
|
|
|
137
130
|
const coupling = resolveRelationPoints(astNode);
|
|
138
131
|
const target = this.resolveFqn(coupling.target);
|
|
139
132
|
const source = this.resolveFqn(coupling.source);
|
|
140
|
-
const tags = this.convertTags(astNode
|
|
133
|
+
const tags = this.convertTags(astNode);
|
|
141
134
|
const kind = astNode.kind?.ref?.name;
|
|
142
135
|
const astPath = this.getAstNodePath(astNode);
|
|
143
|
-
const title = astNode.title ?? astNode.
|
|
136
|
+
const title = astNode.title ?? astNode.props.find((p) => p.key === "title")?.value ?? "";
|
|
144
137
|
const id = objectHash({
|
|
145
138
|
astPath,
|
|
146
139
|
source,
|
|
@@ -156,6 +149,17 @@ ${printDocs(docs)}`);
|
|
|
156
149
|
...tags && { tags }
|
|
157
150
|
};
|
|
158
151
|
}
|
|
152
|
+
parseViews(doc) {
|
|
153
|
+
const views = doc.parseResult.value.views.flatMap((v) => v.views);
|
|
154
|
+
for (const view of views) {
|
|
155
|
+
try {
|
|
156
|
+
const v = this.parseElementView(view);
|
|
157
|
+
doc.c4Views.push(v);
|
|
158
|
+
} catch (e) {
|
|
159
|
+
logWarnError(e);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
159
163
|
parseElementExpression(astNode) {
|
|
160
164
|
if (ast.isWildcardExpression(astNode)) {
|
|
161
165
|
return {
|
|
@@ -275,6 +279,7 @@ ${printDocs(docs)}`);
|
|
|
275
279
|
}
|
|
276
280
|
})
|
|
277
281
|
};
|
|
282
|
+
ElementViewOps.writeId(astNode, basic.id);
|
|
278
283
|
if ("viewOf" in astNode) {
|
|
279
284
|
const viewOfEl = elementRef(astNode.viewOf);
|
|
280
285
|
const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
|
|
@@ -296,7 +301,7 @@ ${printDocs(docs)}`);
|
|
|
296
301
|
}
|
|
297
302
|
resolveFqn(node) {
|
|
298
303
|
if (ast.isExtendElement(node)) {
|
|
299
|
-
return
|
|
304
|
+
return getFqnElementRef(node.element);
|
|
300
305
|
}
|
|
301
306
|
const fqn = this.fqnIndex.getFqn(node);
|
|
302
307
|
invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
|
package/dist/module.js
CHANGED
|
@@ -16,6 +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/NodeKindProvider.js";
|
|
19
20
|
function bind(Type) {
|
|
20
21
|
return (services) => new Type(services);
|
|
21
22
|
}
|
|
@@ -41,6 +42,9 @@ export const LikeC4Module = {
|
|
|
41
42
|
}
|
|
42
43
|
};
|
|
43
44
|
const LikeC4SharedModule = {
|
|
45
|
+
lsp: {
|
|
46
|
+
NodeKindProvider: () => new NodeKindProvider()
|
|
47
|
+
}
|
|
44
48
|
// workspace: {
|
|
45
49
|
// WorkspaceManager: services => new LikeC4WorkspaceManager(services)
|
|
46
50
|
// }
|
package/dist/protocol.d.ts
CHANGED
package/dist/protocol.js
CHANGED
|
@@ -7,8 +7,5 @@ export const fetchModel = new RequestType0("likec4/fetchModel");
|
|
|
7
7
|
export const computeView = new RequestType(
|
|
8
8
|
"likec4/computeView"
|
|
9
9
|
);
|
|
10
|
-
export const
|
|
11
|
-
export const buildDocuments = new RequestType(
|
|
12
|
-
"likec4/buildDocuments"
|
|
13
|
-
);
|
|
10
|
+
export const buildDocuments = new RequestType("likec4/build");
|
|
14
11
|
export const locate = new RequestType("likec4/locate");
|