@likec4/language-server 0.6.3 → 0.18.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 +1 -1
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/ast.d.ts +4 -2
- package/dist/ast.js +13 -3
- package/dist/builtin.d.ts +1 -0
- package/dist/builtin.js +1 -0
- package/dist/elementRef.d.ts +2 -1
- package/dist/elementRef.js +1 -0
- package/dist/generated/ast.d.ts +12 -6
- package/dist/generated/ast.js +14 -2
- package/dist/generated/grammar.d.ts +3 -2
- package/dist/generated/grammar.js +79 -37
- package/dist/generated/module.d.ts +3 -2
- package/dist/generated/module.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +1 -0
- package/dist/lsp/DocumentSymbolProvider.d.ts +2 -1
- package/dist/lsp/DocumentSymbolProvider.js +29 -11
- package/dist/lsp/HoverProvider.d.ts +2 -1
- package/dist/lsp/HoverProvider.js +1 -0
- package/dist/lsp/SemanticTokenProvider.d.ts +1 -0
- package/dist/lsp/SemanticTokenProvider.js +9 -44
- package/dist/lsp/index.d.ts +1 -0
- package/dist/lsp/index.js +1 -0
- package/dist/model/fqn-computation.d.ts +4 -0
- package/dist/model/fqn-computation.js +41 -0
- package/dist/model/fqn-index.d.ts +36 -11
- package/dist/model/fqn-index.js +50 -89
- package/dist/model/index.d.ts +2 -0
- package/dist/model/index.js +2 -0
- package/dist/model/model-builder.d.ts +1 -0
- package/dist/model/model-builder.js +44 -30
- package/dist/model/model-locator.d.ts +1 -0
- package/dist/model/model-locator.js +24 -16
- package/dist/module.d.ts +1 -0
- package/dist/module.js +1 -4
- package/dist/protocol.d.ts +36 -0
- package/dist/protocol.js +22 -0
- package/dist/references/index.d.ts +1 -0
- package/dist/references/index.js +1 -0
- package/dist/references/scope-computation.d.ts +5 -1
- package/dist/references/scope-computation.js +11 -3
- package/dist/references/scope-provider.d.ts +4 -0
- package/dist/references/scope-provider.js +40 -30
- package/dist/registerProtocolHandlers.d.ts +1 -0
- package/dist/registerProtocolHandlers.js +48 -17
- package/dist/shared/CodeLensProvider.d.ts +2 -1
- package/dist/shared/CodeLensProvider.js +1 -0
- package/dist/shared/WorkspaceManager.d.ts +3 -2
- package/dist/shared/WorkspaceManager.js +3 -4
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/test/index.d.ts +2 -0
- package/dist/test/index.js +2 -0
- package/dist/test/testServices.d.ts +16 -0
- package/dist/test/testServices.js +58 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +1 -0
- package/dist/validation/element.d.ts +1 -0
- package/dist/validation/element.js +9 -2
- package/dist/validation/index.d.ts +1 -0
- package/dist/validation/index.js +1 -0
- package/dist/validation/relation.d.ts +1 -0
- package/dist/validation/relation.js +1 -0
- package/dist/validation/specification.d.ts +1 -0
- package/dist/validation/specification.js +1 -0
- package/dist/validation/view.d.ts +1 -0
- package/dist/validation/view.js +1 -0
- package/package.json +24 -42
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AbstractSemanticTokenProvider } from 'langium';
|
|
2
|
-
import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver
|
|
2
|
+
import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver';
|
|
3
3
|
import { ast } from '../ast';
|
|
4
4
|
import { isElementRefHead } from '../elementRef';
|
|
5
5
|
export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
@@ -135,46 +135,9 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
135
135
|
if (ast.isElement(node)) {
|
|
136
136
|
return this.highlightAstElement(node, acceptor);
|
|
137
137
|
}
|
|
138
|
-
// if (ast.isExtendElement(node)) {
|
|
139
|
-
// keyword('extend')
|
|
140
|
-
// return
|
|
141
|
-
// }
|
|
142
|
-
// if (ast.isElementProperty(node) || ast.isRelationProperty(node) || ast.isViewProperty(node)) {
|
|
143
|
-
// acceptor({
|
|
144
|
-
// node,
|
|
145
|
-
// property: 'key',
|
|
146
|
-
// type: SemanticTokenTypes.property,
|
|
147
|
-
// modifier: [SemanticTokenModifiers.definition]
|
|
148
|
-
// })
|
|
149
|
-
// if ('value' in node) {
|
|
150
|
-
// acceptor({
|
|
151
|
-
// node,
|
|
152
|
-
// property: 'value',
|
|
153
|
-
// type: SemanticTokenTypes.string
|
|
154
|
-
// })
|
|
155
|
-
// }
|
|
156
|
-
// return
|
|
157
|
-
// }
|
|
158
138
|
if (ast.isView(node)) {
|
|
159
139
|
return this.highlightView(node, acceptor);
|
|
160
140
|
}
|
|
161
|
-
// if (ast.isDynamicViewSteps(node)) {
|
|
162
|
-
// keyword('steps')
|
|
163
|
-
// return
|
|
164
|
-
// }
|
|
165
|
-
// if (ast.isViewRuleAutoLayout(node)) {
|
|
166
|
-
// keyword('autoLayout')
|
|
167
|
-
// return
|
|
168
|
-
// }
|
|
169
|
-
// if (ast.isViewRuleStyle(node)) {
|
|
170
|
-
// keyword('style')
|
|
171
|
-
// return
|
|
172
|
-
// }
|
|
173
|
-
// if (ast.isViewRuleExpression(node)) {
|
|
174
|
-
// keyword(node.isInclude ? 'include' : 'exclude')
|
|
175
|
-
// return
|
|
176
|
-
// }
|
|
177
|
-
// //
|
|
178
141
|
}
|
|
179
142
|
highlightAstElement(node, acceptor) {
|
|
180
143
|
acceptor({
|
|
@@ -198,11 +161,17 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
198
161
|
}
|
|
199
162
|
}
|
|
200
163
|
highlightView(node, acceptor) {
|
|
164
|
+
acceptor({
|
|
165
|
+
node,
|
|
166
|
+
keyword: 'view',
|
|
167
|
+
type: SemanticTokenTypes.keyword
|
|
168
|
+
});
|
|
201
169
|
if (node.name) {
|
|
202
170
|
acceptor({
|
|
203
171
|
node,
|
|
204
172
|
property: 'name',
|
|
205
|
-
type: SemanticTokenTypes.variable
|
|
173
|
+
type: SemanticTokenTypes.variable,
|
|
174
|
+
modifier: [SemanticTokenModifiers.declaration]
|
|
206
175
|
});
|
|
207
176
|
}
|
|
208
177
|
if (node.viewOf) {
|
|
@@ -212,10 +181,6 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
212
181
|
type: SemanticTokenTypes.keyword
|
|
213
182
|
});
|
|
214
183
|
}
|
|
215
|
-
acceptor({
|
|
216
|
-
node,
|
|
217
|
-
keyword: 'view',
|
|
218
|
-
type: SemanticTokenTypes.keyword
|
|
219
|
-
});
|
|
220
184
|
}
|
|
221
185
|
}
|
|
186
|
+
//# sourceMappingURL=SemanticTokenProvider.js.map
|
package/dist/lsp/index.d.ts
CHANGED
package/dist/lsp/index.js
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { failExpectedNever } from '@likec4/core';
|
|
2
|
+
import { Fqn } from '@likec4/core/types';
|
|
3
|
+
import { MultiMap } from 'langium';
|
|
4
|
+
import { isEmpty, isNil } from 'rambdax';
|
|
5
|
+
import { ElementOps, ast } from '../ast';
|
|
6
|
+
import { strictElementRefFqn } from '../elementRef';
|
|
7
|
+
export function computeDocumentFqn(document, services) {
|
|
8
|
+
const c4fqns = document.c4fqns = new MultiMap();
|
|
9
|
+
const { model } = document.parseResult.value;
|
|
10
|
+
if (!model?.elements) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const locator = services.workspace.AstNodeLocator;
|
|
14
|
+
const traverseStack = model.elements.map(el => [el, null]);
|
|
15
|
+
let pair;
|
|
16
|
+
while ((pair = traverseStack.shift())) {
|
|
17
|
+
const [el, parent] = pair;
|
|
18
|
+
if (ast.isRelation(el)) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (ast.isExtendElement(el)) {
|
|
22
|
+
if (!isNil(el.body) && !isEmpty(el.body.elements)) {
|
|
23
|
+
const fqn = strictElementRefFqn(el.element);
|
|
24
|
+
el.body.elements.forEach(child => traverseStack.push([child, fqn]));
|
|
25
|
+
}
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (ast.isElement(el)) {
|
|
29
|
+
const fqn = Fqn(el.name, parent);
|
|
30
|
+
const path = locator.getAstNodePath(el);
|
|
31
|
+
c4fqns.add(fqn, path);
|
|
32
|
+
ElementOps.writeId(el, fqn);
|
|
33
|
+
if (!isNil(el.body) && !isEmpty(el.body.elements)) {
|
|
34
|
+
el.body.elements.forEach(child => traverseStack.push([child, fqn]));
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
failExpectedNever(el);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=fqn-computation.js.map
|
|
@@ -1,17 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
1
|
+
import type { Fqn } from '@likec4/core/types';
|
|
2
|
+
import type { LangiumDocuments } from 'langium';
|
|
3
|
+
import { DocumentState, MultiMap, StreamImpl } from 'langium';
|
|
4
|
+
import type { SetNonNullable } from 'type-fest';
|
|
5
|
+
import type { ast } from '../ast';
|
|
6
|
+
import { type LikeC4LangiumDocument } from '../ast';
|
|
4
7
|
import type { LikeC4Services } from '../module';
|
|
5
|
-
|
|
8
|
+
export interface FqnIndexEntry {
|
|
9
|
+
fqn: Fqn;
|
|
10
|
+
name: string;
|
|
11
|
+
doc: LikeC4LangiumDocument;
|
|
12
|
+
path: string;
|
|
13
|
+
}
|
|
6
14
|
export declare class FqnIndex {
|
|
7
|
-
#private;
|
|
8
15
|
private services;
|
|
9
|
-
protected
|
|
16
|
+
protected langiumDocuments: LangiumDocuments;
|
|
10
17
|
constructor(services: LikeC4Services);
|
|
18
|
+
private documents;
|
|
19
|
+
private entries;
|
|
11
20
|
get(el: ast.Element): Fqn | null;
|
|
12
|
-
byFqn(fqn: Fqn):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
byFqn(fqn: Fqn): import("langium").Stream<{
|
|
22
|
+
path: string;
|
|
23
|
+
doc: SetNonNullable<{
|
|
24
|
+
c4hash?: string;
|
|
25
|
+
c4Specification: import("../ast").ParsedAstSpecification;
|
|
26
|
+
c4Elements: import("../ast").ParsedAstElement[];
|
|
27
|
+
c4Relations: import("../ast").ParsedAstRelation[];
|
|
28
|
+
c4Views: import("../ast").ParsedAstElementView[];
|
|
29
|
+
readonly uri: import("vscode-uri").URI;
|
|
30
|
+
readonly textDocument: import("vscode-languageserver-textdocument").TextDocument;
|
|
31
|
+
state: DocumentState;
|
|
32
|
+
parseResult: import("langium").ParseResult<ast.LikeC4Document>;
|
|
33
|
+
precomputedScopes?: import("langium").PrecomputedScopes;
|
|
34
|
+
references: import("langium").Reference<import("langium").AstNode>[];
|
|
35
|
+
diagnostics?: import("vscode-languageserver-types").Diagnostic[];
|
|
36
|
+
c4fqns: MultiMap<Fqn, string> | undefined;
|
|
37
|
+
}, "c4fqns">;
|
|
38
|
+
}>;
|
|
39
|
+
directChildrenOf(parent: Fqn): import("langium").Stream<FqnIndexEntry>;
|
|
40
|
+
uniqueDescedants(parent: Fqn): StreamImpl<IterableIterator<FqnIndexEntry> | null, FqnIndexEntry>;
|
|
17
41
|
}
|
|
42
|
+
//# sourceMappingURL=fqn-index.d.ts.map
|
package/dist/model/fqn-index.js
CHANGED
|
@@ -1,48 +1,60 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { nameFromFqn, parentFqn } from '@likec4/core/utils';
|
|
2
|
+
import { DocumentState, DONE_RESULT, getDocument, MultiMap, StreamImpl } from 'langium';
|
|
3
|
+
import { isNil } from 'rambdax';
|
|
4
|
+
import { ElementOps, isLikeC4LangiumDocument } from '../ast';
|
|
3
5
|
import { logger } from '../logger';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import { strictElementRefFqn } from '../elementRef';
|
|
6
|
+
import { computeDocumentFqn } from './fqn-computation';
|
|
7
|
+
const isFqnIndexedDocument = (doc) => isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns);
|
|
7
8
|
export class FqnIndex {
|
|
8
9
|
services;
|
|
9
|
-
|
|
10
|
-
#index = new MultiMap();
|
|
11
|
-
descriptions;
|
|
10
|
+
langiumDocuments;
|
|
12
11
|
constructor(services) {
|
|
13
12
|
this.services = services;
|
|
14
|
-
this.
|
|
15
|
-
services.shared.workspace.DocumentBuilder.
|
|
16
|
-
for (const uri of [...removed]) {
|
|
17
|
-
this.cleanIndexedElements(uri);
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.ComputedScopes, (docs, _cancelToken) => {
|
|
13
|
+
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
14
|
+
services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, (docs, _cancelToken) => {
|
|
21
15
|
for (const doc of docs) {
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
if (isLikeC4LangiumDocument(doc)) {
|
|
17
|
+
try {
|
|
18
|
+
computeDocumentFqn(doc, services);
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
logger.error(e);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
+
documents() {
|
|
28
|
+
return this.langiumDocuments.all.filter(isFqnIndexedDocument);
|
|
29
|
+
}
|
|
30
|
+
entries() {
|
|
31
|
+
return this.documents().flatMap(doc => doc.c4fqns.entries().map(([fqn, path]) => ({ fqn, path, doc })));
|
|
32
|
+
}
|
|
27
33
|
get(el) {
|
|
28
34
|
let fqn = ElementOps.readId(el) ?? null;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
if (fqn) {
|
|
36
|
+
const doc = getDocument(el);
|
|
37
|
+
if (isFqnIndexedDocument(doc) && doc.c4fqns.has(fqn)) {
|
|
38
|
+
return fqn;
|
|
39
|
+
}
|
|
32
40
|
const path = this.services.workspace.AstNodeLocator.getAstNodePath(el);
|
|
33
41
|
logger.error(`Clean cached FQN ${fqn} at ${path}`);
|
|
34
|
-
|
|
42
|
+
ElementOps.writeId(el, null);
|
|
35
43
|
fqn = null;
|
|
36
44
|
}
|
|
37
45
|
return fqn;
|
|
38
46
|
}
|
|
39
47
|
byFqn(fqn) {
|
|
40
|
-
return this
|
|
48
|
+
return this.documents()
|
|
49
|
+
.flatMap(doc => {
|
|
50
|
+
return doc.c4fqns.get(fqn).map(path => ({ path, doc }));
|
|
51
|
+
});
|
|
41
52
|
}
|
|
42
53
|
directChildrenOf(parent) {
|
|
43
|
-
return this
|
|
44
|
-
.
|
|
45
|
-
.
|
|
54
|
+
return this
|
|
55
|
+
.entries()
|
|
56
|
+
.filter(e => parentFqn(e.fqn) === parent)
|
|
57
|
+
.map((e) => ({ ...e, name: nameFromFqn(e.fqn) }));
|
|
46
58
|
}
|
|
47
59
|
uniqueDescedants(parent) {
|
|
48
60
|
return new StreamImpl(() => {
|
|
@@ -50,24 +62,26 @@ export class FqnIndex {
|
|
|
50
62
|
const children = [];
|
|
51
63
|
const childrenNames = new Set();
|
|
52
64
|
const descedants = [];
|
|
53
|
-
this
|
|
54
|
-
if (fqn.startsWith(prefix)) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
this.entries().forEach(e => {
|
|
66
|
+
if (e.fqn.startsWith(prefix)) {
|
|
67
|
+
const name = nameFromFqn(e.fqn);
|
|
68
|
+
const entry = { ...e, name };
|
|
69
|
+
if (parentFqn(e.fqn) === parent) {
|
|
70
|
+
childrenNames.add(name);
|
|
71
|
+
children.push(entry);
|
|
58
72
|
}
|
|
59
73
|
else {
|
|
60
|
-
descedants.push(
|
|
74
|
+
descedants.push(entry);
|
|
61
75
|
}
|
|
62
76
|
}
|
|
63
77
|
});
|
|
64
78
|
if (children.length + descedants.length === 0) {
|
|
65
79
|
return null;
|
|
66
80
|
}
|
|
67
|
-
const nested = new MultiMap(children.map(
|
|
68
|
-
for (const
|
|
69
|
-
if (!childrenNames.has(
|
|
70
|
-
nested.add(
|
|
81
|
+
const nested = new MultiMap(children.map(entry => [entry.name, entry]));
|
|
82
|
+
for (const descedant of descedants) {
|
|
83
|
+
if (!childrenNames.has(descedant.name)) {
|
|
84
|
+
nested.add(descedant.name, descedant);
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
return nested
|
|
@@ -81,58 +95,5 @@ export class FqnIndex {
|
|
|
81
95
|
return DONE_RESULT;
|
|
82
96
|
});
|
|
83
97
|
}
|
|
84
|
-
doIndexElements(doc) {
|
|
85
|
-
const visitElement = (element, parent = null) => {
|
|
86
|
-
try {
|
|
87
|
-
const name = element.name;
|
|
88
|
-
const fqn = Fqn(name, parent);
|
|
89
|
-
this.#index.add(fqn, this.descriptions.createDescription(element, name, doc));
|
|
90
|
-
ElementOps.writeId(element, fqn);
|
|
91
|
-
// this.#fqnMap.set(element, fqn)
|
|
92
|
-
if (element.body) {
|
|
93
|
-
for (const nested of element.body.elements) {
|
|
94
|
-
if (ast.isElement(nested)) {
|
|
95
|
-
visitElement(nested, fqn);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
catch (e) {
|
|
101
|
-
logger.warn(e);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
const visitExtendElement = (extendElement) => {
|
|
105
|
-
try {
|
|
106
|
-
const fqn = strictElementRefFqn(extendElement.element);
|
|
107
|
-
for (const nested of extendElement.body.elements) {
|
|
108
|
-
if (ast.isElement(nested)) {
|
|
109
|
-
visitElement(nested, fqn);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
catch (e) {
|
|
114
|
-
logger.warn(e);
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
const elements = doc.parseResult.value.model?.elements ?? [];
|
|
118
|
-
for (const modelElement of elements) {
|
|
119
|
-
if (ast.isExtendElement(modelElement)) {
|
|
120
|
-
visitExtendElement(modelElement);
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (ast.isElement(modelElement)) {
|
|
124
|
-
visitElement(modelElement);
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
cleanIndexedElements(docUri) {
|
|
130
|
-
const docUriAsString = docUri.toString();
|
|
131
|
-
const toDelete = this.#index
|
|
132
|
-
.entries()
|
|
133
|
-
.filter(([, indexed]) => indexed.documentUri.toString() === docUriAsString);
|
|
134
|
-
for (const [fqn, indexed] of toDelete.toArray()) {
|
|
135
|
-
this.#index.delete(fqn, indexed);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
98
|
}
|
|
99
|
+
//# sourceMappingURL=fqn-index.js.map
|
package/dist/model/index.d.ts
CHANGED
package/dist/model/index.js
CHANGED
|
@@ -2,15 +2,14 @@ import { computeViews } from '@likec4/core/compute-view';
|
|
|
2
2
|
import { DefaultElementShape, DefaultThemeColor } from '@likec4/core/types';
|
|
3
3
|
import { compareByFqnHierarchically, parentFqn } from '@likec4/core/utils';
|
|
4
4
|
import { A, O, flow, pipe } from '@mobily/ts-belt';
|
|
5
|
-
import { DocumentState, getDocument
|
|
5
|
+
import { DocumentState, getDocument } from 'langium';
|
|
6
6
|
import objectHash from 'object-hash';
|
|
7
|
-
import { clone, isNil, mergeDeepRight,
|
|
7
|
+
import { clone, isNil, mergeDeepRight, reduce } from 'rambdax';
|
|
8
8
|
import invariant from 'tiny-invariant';
|
|
9
|
-
import { toAutoLayout } from '../ast';
|
|
10
|
-
import { ElementViewOps, ast, c4hash, cleanParsedModel, isLikeC4LangiumDocument, isParsedLikeC4LangiumDocument, resolveRelationPoints, streamModel, toElementStyle } from '../ast';
|
|
9
|
+
import { ElementViewOps, ast, c4hash, cleanParsedModel, isLikeC4LangiumDocument, isParsedLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle } from '../ast';
|
|
11
10
|
import { elementRef, strictElementRefFqn } from '../elementRef';
|
|
12
11
|
import { logger } from '../logger';
|
|
13
|
-
import { Rpc } from '
|
|
12
|
+
import { Rpc } from '../protocol';
|
|
14
13
|
import { failExpectedNever } from '../utils';
|
|
15
14
|
export class LikeC4ModelBuilder {
|
|
16
15
|
services;
|
|
@@ -20,26 +19,24 @@ export class LikeC4ModelBuilder {
|
|
|
20
19
|
this.services = services;
|
|
21
20
|
this.fqnIndex = services.likec4.FqnIndex;
|
|
22
21
|
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
23
|
-
services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated,
|
|
22
|
+
services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, (docs, cancelToken) => {
|
|
24
23
|
let countOfChangedDocs = 0;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
catch (e) {
|
|
34
|
-
logger.warn(`Error parsing document ${doc.uri.toString()}`);
|
|
24
|
+
for (const doc of docs) {
|
|
25
|
+
if (cancelToken.isCancellationRequested) {
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
if (isLikeC4LangiumDocument(doc) && this.parseDocument(doc)) {
|
|
30
|
+
countOfChangedDocs++;
|
|
35
31
|
}
|
|
36
32
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (countOfChangedDocs > 0 && !cancelToken.isCancellationRequested) {
|
|
40
|
-
await this.notifyClient();
|
|
33
|
+
catch (e) {
|
|
34
|
+
logger.warn(`Error parsing document ${doc.uri.toString()}`);
|
|
41
35
|
}
|
|
42
36
|
}
|
|
37
|
+
if (countOfChangedDocs > 0 && !cancelToken.isCancellationRequested) {
|
|
38
|
+
void this.notifyClient();
|
|
39
|
+
}
|
|
43
40
|
});
|
|
44
41
|
}
|
|
45
42
|
get connection() {
|
|
@@ -51,8 +48,10 @@ export class LikeC4ModelBuilder {
|
|
|
51
48
|
buildModel() {
|
|
52
49
|
const docs = this.documents();
|
|
53
50
|
if (docs.length === 0) {
|
|
51
|
+
logger.debug('No documents to build model from');
|
|
54
52
|
return;
|
|
55
53
|
}
|
|
54
|
+
// TODO:
|
|
56
55
|
try {
|
|
57
56
|
const c4Specification = reduce((acc, doc) => mergeDeepRight(acc, doc.c4Specification), {
|
|
58
57
|
kinds: {}
|
|
@@ -65,43 +64,58 @@ export class LikeC4ModelBuilder {
|
|
|
65
64
|
const toModelElement = (el) => {
|
|
66
65
|
const kind = c4Specification.kinds[el.kind];
|
|
67
66
|
if (kind) {
|
|
67
|
+
const { astPath, ...model } = el;
|
|
68
68
|
return {
|
|
69
69
|
...(kind.shape !== DefaultElementShape ? { shape: kind.shape } : {}),
|
|
70
70
|
...(kind.color !== DefaultThemeColor ? { color: kind.color } : {}),
|
|
71
|
-
...
|
|
71
|
+
...model
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
return null;
|
|
75
75
|
};
|
|
76
76
|
const toModelRelation = (rel) => {
|
|
77
|
-
|
|
77
|
+
const { astPath, ...model } = rel;
|
|
78
|
+
return model;
|
|
78
79
|
};
|
|
79
80
|
const elements = pipe(docs.flatMap(d => d.c4Elements), A.filterMap(flow(toModelElement, O.fromNullable)), A.sort(compareByFqnHierarchically), A.reduce({}, (acc, el) => {
|
|
80
81
|
const parent = parentFqn(el.id);
|
|
81
82
|
if (!parent || parent in acc) {
|
|
82
|
-
|
|
83
|
+
if (el.id in acc) {
|
|
84
|
+
logger.warn(`Duplicate element id: ${el.id}`);
|
|
85
|
+
return acc;
|
|
86
|
+
}
|
|
83
87
|
acc[el.id] = el;
|
|
84
88
|
}
|
|
85
89
|
return acc;
|
|
86
90
|
}));
|
|
87
91
|
const relations = pipe(docs.flatMap(d => d.c4Relations), A.filterMap(flow(toModelRelation, O.fromPredicate(({ source, target }) => source in elements && target in elements))), A.reduce({}, (acc, el) => {
|
|
88
|
-
|
|
92
|
+
if (el.id in acc) {
|
|
93
|
+
logger.warn(`Duplicate relation id: ${el.id}`);
|
|
94
|
+
return acc;
|
|
95
|
+
}
|
|
89
96
|
acc[el.id] = el;
|
|
90
97
|
return acc;
|
|
91
98
|
}));
|
|
92
99
|
const toModelView = (view) => {
|
|
93
|
-
|
|
100
|
+
// eslint-disable-next-line prefer-const
|
|
101
|
+
let { astPath, rules, title, ...model } = view;
|
|
94
102
|
if (!title && view.viewOf) {
|
|
95
103
|
title = elements[view.viewOf]?.title;
|
|
96
104
|
}
|
|
105
|
+
if (!title && view.id === 'index') {
|
|
106
|
+
title = 'Landscape view';
|
|
107
|
+
}
|
|
97
108
|
return {
|
|
98
|
-
...
|
|
99
|
-
...(
|
|
100
|
-
rules: clone(
|
|
109
|
+
...model,
|
|
110
|
+
...(title && { title }),
|
|
111
|
+
rules: clone(rules)
|
|
101
112
|
};
|
|
102
113
|
};
|
|
103
114
|
const views = pipe(docs.flatMap(d => d.c4Views), A.filterMap(flow(toModelView, O.fromPredicate(v => isNil(v.viewOf) || v.viewOf in elements))), A.reduce({}, (acc, v) => {
|
|
104
|
-
|
|
115
|
+
if (v.id in acc) {
|
|
116
|
+
logger.warn(`Duplicate view id: ${v.id}`);
|
|
117
|
+
return acc;
|
|
118
|
+
}
|
|
105
119
|
acc[v.id] = v;
|
|
106
120
|
return acc;
|
|
107
121
|
}));
|
|
@@ -326,7 +340,7 @@ export class LikeC4ModelBuilder {
|
|
|
326
340
|
if (!connection) {
|
|
327
341
|
return;
|
|
328
342
|
}
|
|
329
|
-
logger.debug('Send onDidChangeModel');
|
|
330
343
|
await connection.sendNotification(Rpc.onDidChangeModel);
|
|
331
344
|
}
|
|
332
345
|
}
|
|
346
|
+
//# sourceMappingURL=model-builder.js.map
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { getDocument } from 'langium';
|
|
2
|
-
import { findNodeForKeyword, findNodeForProperty } from 'langium';
|
|
3
|
-
import { head } from 'rambdax';
|
|
1
|
+
import { findNodeForKeyword, findNodeForProperty, getDocument } from 'langium';
|
|
4
2
|
import { ast, isParsedLikeC4LangiumDocument } from '../ast';
|
|
5
3
|
export class LikeC4ModelLocator {
|
|
6
4
|
services;
|
|
@@ -25,19 +23,28 @@ export class LikeC4ModelLocator {
|
|
|
25
23
|
return doc.c4Elements.find(e => e.id === fqn) ?? null;
|
|
26
24
|
}
|
|
27
25
|
locateElement(fqn, property = 'name') {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
for (const doc of this.documents()) {
|
|
27
|
+
if (doc.c4fqns && !doc.c4fqns.has(fqn)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const element = doc.c4Elements.find(e => e.id === fqn);
|
|
31
|
+
if (!element) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const node = this.services.workspace.AstNodeLocator.getAstNode(doc.parseResult.value, element.astPath);
|
|
35
|
+
if (!ast.isElement(node)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const propertyNode = findNodeForProperty(node.$cstNode, property) ?? node.$cstNode;
|
|
39
|
+
if (!propertyNode) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
uri: doc.uri.toString(),
|
|
44
|
+
range: propertyNode.range
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
41
48
|
}
|
|
42
49
|
locateRelation(relationId) {
|
|
43
50
|
for (const doc of this.documents()) {
|
|
@@ -106,3 +113,4 @@ export class LikeC4ModelLocator {
|
|
|
106
113
|
return null;
|
|
107
114
|
}
|
|
108
115
|
}
|
|
116
|
+
//# sourceMappingURL=model-locator.js.map
|
package/dist/module.d.ts
CHANGED
package/dist/module.js
CHANGED
|
@@ -14,10 +14,6 @@ export const LikeC4Module = {
|
|
|
14
14
|
FqnIndex: bind(FqnIndex),
|
|
15
15
|
ModelBuilder: bind(LikeC4ModelBuilder),
|
|
16
16
|
ModelLocator: bind(LikeC4ModelLocator)
|
|
17
|
-
// Model: bind(LikeC4Model),
|
|
18
|
-
// Model: bind(LikeC4Model),
|
|
19
|
-
// SpecIndex: bind(LikeC4SpecIndex),
|
|
20
|
-
// Validator: bind(LikeC4Validator)
|
|
21
17
|
},
|
|
22
18
|
lsp: {
|
|
23
19
|
DocumentSymbolProvider: bind(LikeC4DocumentSymbolProvider),
|
|
@@ -63,3 +59,4 @@ export function createLanguageServices(context) {
|
|
|
63
59
|
registerProtocolHandlers(likec4);
|
|
64
60
|
return { shared, likec4 };
|
|
65
61
|
}
|
|
62
|
+
//# sourceMappingURL=module.js.map
|