@likec4/language-server 1.23.1 → 1.24.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/dist/LikeC4FileSystem.d.ts +1 -0
- package/dist/LikeC4FileSystem.js +7 -0
- package/dist/Rpc.js +13 -11
- package/dist/ast.d.ts +13 -29
- package/dist/ast.js +3 -70
- package/dist/bundled.mjs +2441 -2610
- 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 +10 -6
- package/dist/lsp/CompletionProvider.js +20 -2
- package/dist/lsp/DocumentLinkProvider.d.ts +3 -3
- package/dist/lsp/DocumentLinkProvider.js +5 -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 +5 -56
- package/dist/model/deployments-index.js +61 -137
- package/dist/model/fqn-index.d.ts +50 -19
- package/dist/model/fqn-index.js +176 -69
- package/dist/model/index.d.ts +0 -1
- package/dist/model/index.js +0 -1
- package/dist/model/model-builder.d.ts +10 -9
- package/dist/model/model-builder.js +102 -547
- package/dist/model/model-locator.d.ts +2 -1
- package/dist/model/model-locator.js +7 -9
- package/dist/model/model-parser.d.ts +156 -150
- package/dist/model/model-parser.js +68 -38
- 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 +23 -20
- 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 +12 -13
- 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
|
@@ -1,532 +1,84 @@
|
|
|
1
1
|
import {
|
|
2
|
-
compareRelations,
|
|
3
|
-
computeColorValues,
|
|
4
|
-
DeploymentElement,
|
|
5
2
|
isScopedElementView,
|
|
6
|
-
LikeC4Model
|
|
7
|
-
parentFqn,
|
|
8
|
-
sortByFqnHierarchically
|
|
3
|
+
LikeC4Model
|
|
9
4
|
} from "@likec4/core";
|
|
10
|
-
import {
|
|
5
|
+
import { loggable } from "@likec4/log";
|
|
11
6
|
import { deepEqual as eq } from "fast-equals";
|
|
12
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Disposable,
|
|
9
|
+
DocumentState
|
|
10
|
+
} from "langium";
|
|
13
11
|
import {
|
|
14
12
|
filter,
|
|
15
|
-
flatMap,
|
|
16
13
|
groupBy,
|
|
17
|
-
hasAtLeast,
|
|
18
|
-
indexBy,
|
|
19
|
-
isBoolean,
|
|
20
|
-
isDefined,
|
|
21
|
-
isEmpty,
|
|
22
|
-
isNonNullish,
|
|
23
|
-
isNullish,
|
|
24
|
-
isNumber,
|
|
25
|
-
isTruthy,
|
|
26
|
-
map,
|
|
27
14
|
mapToObj,
|
|
28
|
-
mapValues,
|
|
29
|
-
omit,
|
|
30
15
|
pipe,
|
|
31
|
-
prop,
|
|
32
|
-
reduce,
|
|
33
|
-
reverse,
|
|
34
|
-
sort,
|
|
35
|
-
unique,
|
|
36
16
|
values
|
|
37
17
|
} from "remeda";
|
|
38
|
-
import {
|
|
39
|
-
import { logger, logWarnError } from "../logger.js";
|
|
18
|
+
import { logger as mainLogger, logWarnError } from "../logger.js";
|
|
40
19
|
import { ADisposable } from "../utils/index.js";
|
|
41
|
-
import { assignNavigateTo
|
|
42
|
-
|
|
43
|
-
const c4Specification = {
|
|
44
|
-
tags: /* @__PURE__ */ new Set(),
|
|
45
|
-
deployments: {},
|
|
46
|
-
elements: {},
|
|
47
|
-
relationships: {},
|
|
48
|
-
colors: {}
|
|
49
|
-
};
|
|
50
|
-
const globals = {
|
|
51
|
-
predicates: {},
|
|
52
|
-
dynamicPredicates: {},
|
|
53
|
-
styles: {}
|
|
54
|
-
};
|
|
55
|
-
for (const doc of docs) {
|
|
56
|
-
const {
|
|
57
|
-
c4Specification: spec,
|
|
58
|
-
c4Globals
|
|
59
|
-
} = doc;
|
|
60
|
-
spec.tags.forEach((t) => c4Specification.tags.add(t));
|
|
61
|
-
Object.assign(c4Specification.elements, spec.elements);
|
|
62
|
-
Object.assign(c4Specification.relationships, spec.relationships);
|
|
63
|
-
Object.assign(c4Specification.colors, spec.colors);
|
|
64
|
-
Object.assign(c4Specification.deployments, spec.deployments);
|
|
65
|
-
Object.assign(globals.predicates, c4Globals.predicates);
|
|
66
|
-
Object.assign(globals.dynamicPredicates, c4Globals.dynamicPredicates);
|
|
67
|
-
Object.assign(globals.styles, c4Globals.styles);
|
|
68
|
-
}
|
|
69
|
-
function resolveLinks(doc, links) {
|
|
70
|
-
return map(
|
|
71
|
-
links,
|
|
72
|
-
(link) => {
|
|
73
|
-
try {
|
|
74
|
-
const relative = services.lsp.DocumentLinkProvider.relativeLink(doc, link.url);
|
|
75
|
-
if (relative && relative !== link.url) {
|
|
76
|
-
return {
|
|
77
|
-
...link,
|
|
78
|
-
relative
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
} catch (e) {
|
|
82
|
-
logWarnError(e);
|
|
83
|
-
}
|
|
84
|
-
return link;
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
const customColorDefinitions = mapValues(
|
|
89
|
-
c4Specification.colors,
|
|
90
|
-
(c) => computeColorValues(c.color)
|
|
91
|
-
);
|
|
92
|
-
function toModelElement(doc) {
|
|
93
|
-
return ({
|
|
94
|
-
tags,
|
|
95
|
-
links: unresolvedLinks,
|
|
96
|
-
style: {
|
|
97
|
-
color,
|
|
98
|
-
shape,
|
|
99
|
-
icon,
|
|
100
|
-
opacity,
|
|
101
|
-
border,
|
|
102
|
-
size,
|
|
103
|
-
multiple,
|
|
104
|
-
padding,
|
|
105
|
-
textSize
|
|
106
|
-
},
|
|
107
|
-
id,
|
|
108
|
-
kind,
|
|
109
|
-
title,
|
|
110
|
-
description,
|
|
111
|
-
technology,
|
|
112
|
-
metadata
|
|
113
|
-
}) => {
|
|
114
|
-
try {
|
|
115
|
-
const __kind = c4Specification.elements[kind];
|
|
116
|
-
if (!__kind) {
|
|
117
|
-
logger.warn`No kind '${kind}' found for ${id}`;
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
const links = unresolvedLinks ? resolveLinks(doc, unresolvedLinks) : null;
|
|
121
|
-
color ??= __kind.style.color;
|
|
122
|
-
shape ??= __kind.style.shape;
|
|
123
|
-
icon ??= __kind.style.icon;
|
|
124
|
-
opacity ??= __kind.style.opacity;
|
|
125
|
-
border ??= __kind.style.border;
|
|
126
|
-
technology ??= __kind.technology;
|
|
127
|
-
multiple ??= __kind.style.multiple;
|
|
128
|
-
size ??= __kind.style.size;
|
|
129
|
-
padding ??= __kind.style.padding;
|
|
130
|
-
textSize ??= __kind.style.textSize;
|
|
131
|
-
return {
|
|
132
|
-
...color && { color },
|
|
133
|
-
...shape && { shape },
|
|
134
|
-
...icon && { icon },
|
|
135
|
-
...metadata && !isEmpty(metadata) && { metadata },
|
|
136
|
-
...__kind.notation && { notation: __kind.notation },
|
|
137
|
-
style: {
|
|
138
|
-
...border && { border },
|
|
139
|
-
...size && { size },
|
|
140
|
-
...padding && { padding },
|
|
141
|
-
...textSize && { textSize },
|
|
142
|
-
...isBoolean(multiple) && { multiple },
|
|
143
|
-
...isNumber(opacity) && { opacity }
|
|
144
|
-
},
|
|
145
|
-
links,
|
|
146
|
-
tags: tags ?? null,
|
|
147
|
-
technology: technology ?? null,
|
|
148
|
-
description: description ?? null,
|
|
149
|
-
title,
|
|
150
|
-
kind,
|
|
151
|
-
id
|
|
152
|
-
};
|
|
153
|
-
} catch (e) {
|
|
154
|
-
logWarnError(e);
|
|
155
|
-
}
|
|
156
|
-
return null;
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
const elementsExtendData = /* @__PURE__ */ new Map();
|
|
160
|
-
function mergeAllC4ExtendElements(doc) {
|
|
161
|
-
for (const el of doc.c4ExtendElements) {
|
|
162
|
-
let links = el.links ? resolveLinks(doc, el.links) : null;
|
|
163
|
-
const existing = elementsExtendData.get(el.id);
|
|
164
|
-
if (existing) {
|
|
165
|
-
links = existing.links ? [...existing.links, ...links ?? []] : links;
|
|
166
|
-
let tags = [...existing.tags ?? [], ...el.tags ?? []];
|
|
167
|
-
if (!hasAtLeast(tags, 1)) {
|
|
168
|
-
tags = null;
|
|
169
|
-
}
|
|
170
|
-
elementsExtendData.set(el.id, {
|
|
171
|
-
tags,
|
|
172
|
-
links,
|
|
173
|
-
metadata: { ...existing.metadata, ...el.metadata }
|
|
174
|
-
});
|
|
175
|
-
} else {
|
|
176
|
-
elementsExtendData.set(el.id, {
|
|
177
|
-
tags: el.tags ?? null,
|
|
178
|
-
links,
|
|
179
|
-
metadata: { ...el.metadata }
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
function withExtendElementData(el) {
|
|
185
|
-
const extendData = elementsExtendData.get(el.id);
|
|
186
|
-
if (extendData) {
|
|
187
|
-
const links = [...el.links ?? [], ...extendData.links ?? []];
|
|
188
|
-
const tags = unique([...el.tags ?? [], ...extendData.tags ?? []]);
|
|
189
|
-
const metadata = { ...el.metadata, ...extendData.metadata };
|
|
190
|
-
return {
|
|
191
|
-
...el,
|
|
192
|
-
tags: hasAtLeast(tags, 1) ? tags : null,
|
|
193
|
-
links: hasAtLeast(links, 1) ? links : null,
|
|
194
|
-
...!isEmpty(metadata) && { metadata }
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
return el;
|
|
198
|
-
}
|
|
199
|
-
const elements = pipe(
|
|
200
|
-
docs,
|
|
201
|
-
flatMap((d) => {
|
|
202
|
-
mergeAllC4ExtendElements(d);
|
|
203
|
-
return map(d.c4Elements, toModelElement(d));
|
|
204
|
-
}),
|
|
205
|
-
filter(isTruthy),
|
|
206
|
-
// sort from root elements to nested, so that parent is always present
|
|
207
|
-
// Import to preserve the order from the source
|
|
208
|
-
sortByFqnHierarchically,
|
|
209
|
-
reduce(
|
|
210
|
-
(acc, el) => {
|
|
211
|
-
const parent = parentFqn(el.id);
|
|
212
|
-
if (parent && isNullish(acc[parent])) {
|
|
213
|
-
logger.debug`No parent found for ${el.id}`;
|
|
214
|
-
return acc;
|
|
215
|
-
}
|
|
216
|
-
acc[el.id] = withExtendElementData(el);
|
|
217
|
-
return acc;
|
|
218
|
-
},
|
|
219
|
-
{}
|
|
220
|
-
)
|
|
221
|
-
);
|
|
222
|
-
function toModelRelation(doc) {
|
|
223
|
-
return ({
|
|
224
|
-
astPath,
|
|
225
|
-
source,
|
|
226
|
-
target,
|
|
227
|
-
kind,
|
|
228
|
-
links: unresolvedLinks,
|
|
229
|
-
id,
|
|
230
|
-
...model
|
|
231
|
-
}) => {
|
|
232
|
-
if (isNullish(elements[source]) || isNullish(elements[target])) {
|
|
233
|
-
logger.debug`Invalid relation ${id}
|
|
234
|
-
source: ${source} resolved: ${!!elements[source]}
|
|
235
|
-
target: ${target} resolved: ${!!elements[target]}
|
|
236
|
-
at ${doc.uri.path}\n`;
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
239
|
-
const links = unresolvedLinks ? resolveLinks(doc, unresolvedLinks) : null;
|
|
240
|
-
if (isNonNullish(kind) && kind in c4Specification.relationships) {
|
|
241
|
-
return {
|
|
242
|
-
...c4Specification.relationships[kind],
|
|
243
|
-
...model,
|
|
244
|
-
...links && { links },
|
|
245
|
-
source,
|
|
246
|
-
target,
|
|
247
|
-
kind,
|
|
248
|
-
id
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
...links && { links },
|
|
253
|
-
...model,
|
|
254
|
-
source,
|
|
255
|
-
target,
|
|
256
|
-
id
|
|
257
|
-
};
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
const relations = pipe(
|
|
261
|
-
docs,
|
|
262
|
-
flatMap((d) => map(d.c4Relations, toModelRelation(d))),
|
|
263
|
-
filter(isTruthy),
|
|
264
|
-
sort(compareRelations),
|
|
265
|
-
reverse(),
|
|
266
|
-
indexBy(prop("id"))
|
|
267
|
-
);
|
|
268
|
-
function toDeploymentElement(doc) {
|
|
269
|
-
return (parsed) => {
|
|
270
|
-
if (!DeploymentElement.isDeploymentNode(parsed)) {
|
|
271
|
-
if (!parsed.links || parsed.links.length === 0) {
|
|
272
|
-
return parsed;
|
|
273
|
-
}
|
|
274
|
-
const links = resolveLinks(doc, parsed.links);
|
|
275
|
-
return {
|
|
276
|
-
...parsed,
|
|
277
|
-
links
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
try {
|
|
281
|
-
const __kind = c4Specification.deployments[parsed.kind];
|
|
282
|
-
if (!__kind) {
|
|
283
|
-
logger.warn`No kind ${parsed.kind} found for ${parsed.id}`;
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
let {
|
|
287
|
-
technology = __kind.technology,
|
|
288
|
-
notation = __kind.notation,
|
|
289
|
-
links,
|
|
290
|
-
style
|
|
291
|
-
} = parsed;
|
|
292
|
-
return {
|
|
293
|
-
...parsed,
|
|
294
|
-
...notation && { notation },
|
|
295
|
-
...technology && { technology },
|
|
296
|
-
style: {
|
|
297
|
-
border: "dashed",
|
|
298
|
-
opacity: 10,
|
|
299
|
-
...__kind.style,
|
|
300
|
-
...style
|
|
301
|
-
},
|
|
302
|
-
links: links ? resolveLinks(doc, links) : null
|
|
303
|
-
};
|
|
304
|
-
} catch (e) {
|
|
305
|
-
logWarnError(e);
|
|
306
|
-
}
|
|
307
|
-
return null;
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
const deploymentElements = pipe(
|
|
311
|
-
docs,
|
|
312
|
-
flatMap((d) => map(d.c4Deployments, toDeploymentElement(d))),
|
|
313
|
-
filter(isTruthy),
|
|
314
|
-
// sort from root elements to nested, so that parent is always present
|
|
315
|
-
// Import to preserve the order from the source
|
|
316
|
-
sortByFqnHierarchically,
|
|
317
|
-
reduce(
|
|
318
|
-
(acc, el) => {
|
|
319
|
-
const parent = parentFqn(el.id);
|
|
320
|
-
if (parent && isNullish(acc[parent])) {
|
|
321
|
-
logger.debug`No parent found for deployment element ${el.id}`;
|
|
322
|
-
return acc;
|
|
323
|
-
}
|
|
324
|
-
acc[el.id] = el;
|
|
325
|
-
return acc;
|
|
326
|
-
},
|
|
327
|
-
{}
|
|
328
|
-
)
|
|
329
|
-
);
|
|
330
|
-
function toDeploymentRelation(doc) {
|
|
331
|
-
return ({
|
|
332
|
-
astPath,
|
|
333
|
-
source,
|
|
334
|
-
target,
|
|
335
|
-
kind,
|
|
336
|
-
links: unresolvedLinks,
|
|
337
|
-
id,
|
|
338
|
-
...model
|
|
339
|
-
}) => {
|
|
340
|
-
if (isNullish(deploymentElements[source.id]) || isNullish(deploymentElements[target.id])) {
|
|
341
|
-
logger.warn`Invalid deployment relation ${id} at ${doc.uri.path} ${astPath}, source: ${source.id}(${!!deploymentElements[source.id]}), target: ${target.id}(${!!deploymentElements[target.id]})`;
|
|
342
|
-
return null;
|
|
343
|
-
}
|
|
344
|
-
const links = unresolvedLinks ? resolveLinks(doc, unresolvedLinks) : null;
|
|
345
|
-
if (isNonNullish(kind) && kind in c4Specification.relationships) {
|
|
346
|
-
return {
|
|
347
|
-
...c4Specification.relationships[kind],
|
|
348
|
-
...model,
|
|
349
|
-
...links && { links },
|
|
350
|
-
source,
|
|
351
|
-
target,
|
|
352
|
-
kind,
|
|
353
|
-
id
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
return {
|
|
357
|
-
...links && { links },
|
|
358
|
-
...model,
|
|
359
|
-
source,
|
|
360
|
-
target,
|
|
361
|
-
id
|
|
362
|
-
};
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
const deploymentRelations = pipe(
|
|
366
|
-
docs,
|
|
367
|
-
flatMap((d) => map(d.c4DeploymentRelations, toDeploymentRelation(d))),
|
|
368
|
-
filter(isTruthy),
|
|
369
|
-
reduce(
|
|
370
|
-
(acc, el) => {
|
|
371
|
-
if (isDefined(acc[el.id])) {
|
|
372
|
-
logger.debug`Duplicate deployment relation ${el.id}`;
|
|
373
|
-
return acc;
|
|
374
|
-
}
|
|
375
|
-
acc[el.id] = el;
|
|
376
|
-
return acc;
|
|
377
|
-
},
|
|
378
|
-
{}
|
|
379
|
-
)
|
|
380
|
-
);
|
|
381
|
-
function toC4View(doc) {
|
|
382
|
-
const docUri = doc.uri.toString();
|
|
383
|
-
return (parsedAstView) => {
|
|
384
|
-
let {
|
|
385
|
-
id,
|
|
386
|
-
title,
|
|
387
|
-
description,
|
|
388
|
-
tags,
|
|
389
|
-
links: unresolvedLinks,
|
|
390
|
-
// ignore this property
|
|
391
|
-
astPath: _ignore,
|
|
392
|
-
// model should include discriminant __
|
|
393
|
-
...model
|
|
394
|
-
} = parsedAstView;
|
|
395
|
-
if (parsedAstView.__ === "element" && isNullish(title) && "viewOf" in parsedAstView) {
|
|
396
|
-
title = elements[parsedAstView.viewOf]?.title ?? null;
|
|
397
|
-
}
|
|
398
|
-
if (isNullish(title) && id === "index") {
|
|
399
|
-
title = "Landscape view";
|
|
400
|
-
}
|
|
401
|
-
const links = unresolvedLinks ? resolveLinks(doc, unresolvedLinks) : null;
|
|
402
|
-
return {
|
|
403
|
-
...model,
|
|
404
|
-
customColorDefinitions,
|
|
405
|
-
tags,
|
|
406
|
-
links,
|
|
407
|
-
docUri,
|
|
408
|
-
description,
|
|
409
|
-
title,
|
|
410
|
-
id
|
|
411
|
-
};
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
const parsedViews = pipe(
|
|
415
|
-
docs,
|
|
416
|
-
flatMap((d) => map(d.c4Views, toC4View(d))),
|
|
417
|
-
// Resolve relative paths and sort by
|
|
418
|
-
resolveRelativePaths
|
|
419
|
-
);
|
|
420
|
-
if (!parsedViews.some((v) => v.id === "index")) {
|
|
421
|
-
parsedViews.unshift({
|
|
422
|
-
__: "element",
|
|
423
|
-
id: "index",
|
|
424
|
-
title: "Landscape view",
|
|
425
|
-
description: null,
|
|
426
|
-
tags: null,
|
|
427
|
-
links: null,
|
|
428
|
-
customColorDefinitions,
|
|
429
|
-
rules: [
|
|
430
|
-
{
|
|
431
|
-
include: [
|
|
432
|
-
{
|
|
433
|
-
wildcard: true
|
|
434
|
-
}
|
|
435
|
-
]
|
|
436
|
-
}
|
|
437
|
-
]
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
const views = pipe(
|
|
441
|
-
parsedViews,
|
|
442
|
-
indexBy(prop("id")),
|
|
443
|
-
resolveRulesExtendedViews
|
|
444
|
-
);
|
|
445
|
-
return {
|
|
446
|
-
specification: {
|
|
447
|
-
tags: Array.from(c4Specification.tags),
|
|
448
|
-
elements: c4Specification.elements,
|
|
449
|
-
relationships: c4Specification.relationships,
|
|
450
|
-
deployments: c4Specification.deployments
|
|
451
|
-
},
|
|
452
|
-
elements,
|
|
453
|
-
relations,
|
|
454
|
-
globals,
|
|
455
|
-
views,
|
|
456
|
-
deployments: {
|
|
457
|
-
elements: deploymentElements,
|
|
458
|
-
relations: deploymentRelations
|
|
459
|
-
}
|
|
460
|
-
};
|
|
461
|
-
}
|
|
20
|
+
import { assignNavigateTo } from "../view-utils/index.js";
|
|
21
|
+
import { buildModel } from "./builder/buildModel.js";
|
|
462
22
|
const CACHE_KEY_PARSED_MODEL = "ParsedLikeC4Model";
|
|
463
23
|
const CACHE_KEY_COMPUTED_MODEL = "ComputedLikeC4Model";
|
|
24
|
+
const logger = mainLogger.getChild("ModelBuilder");
|
|
464
25
|
export class LikeC4ModelBuilder extends ADisposable {
|
|
26
|
+
parser;
|
|
27
|
+
listeners = [];
|
|
28
|
+
cache;
|
|
29
|
+
DocumentBuilder;
|
|
30
|
+
LangiumDocuments;
|
|
465
31
|
constructor(services) {
|
|
466
32
|
super();
|
|
467
|
-
this.
|
|
468
|
-
this.
|
|
469
|
-
|
|
33
|
+
this.parser = services.likec4.ModelParser;
|
|
34
|
+
this.cache = services.ValidatedWorkspaceCache;
|
|
35
|
+
this.DocumentBuilder = services.shared.workspace.DocumentBuilder;
|
|
36
|
+
this.LangiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
470
37
|
this.onDispose(
|
|
471
|
-
|
|
38
|
+
this.DocumentBuilder.onUpdate((_changed, deleted) => {
|
|
472
39
|
if (deleted.length > 0) {
|
|
473
40
|
this.notifyListeners(deleted);
|
|
474
41
|
}
|
|
475
42
|
})
|
|
476
43
|
);
|
|
477
44
|
this.onDispose(
|
|
478
|
-
|
|
45
|
+
this.DocumentBuilder.onBuildPhase(
|
|
479
46
|
DocumentState.Validated,
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
logger.debug`[ModelBuilder] onValidated (${docs.length} docs)`;
|
|
483
|
-
for (const doc of docs) {
|
|
484
|
-
try {
|
|
485
|
-
parsed.push(parser.parse(doc).uri);
|
|
486
|
-
} catch (e) {
|
|
487
|
-
logWarnError(e);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
await interruptAndCheck(_cancelToken);
|
|
491
|
-
if (parsed.length > 0) {
|
|
492
|
-
this.notifyListeners(parsed);
|
|
493
|
-
}
|
|
47
|
+
(docs, _cancelToken) => {
|
|
48
|
+
this.notifyListeners(docs.map((d) => d.uri));
|
|
494
49
|
}
|
|
495
50
|
)
|
|
496
51
|
);
|
|
497
|
-
logger.debug`
|
|
52
|
+
logger.debug`created`;
|
|
498
53
|
}
|
|
499
|
-
langiumDocuments;
|
|
500
|
-
listeners = [];
|
|
501
54
|
/**
|
|
502
55
|
* WARNING:
|
|
503
56
|
* This method is internal and should to be called only when all documents are known to be parsed.
|
|
504
57
|
* Otherwise, the model may be incomplete.
|
|
505
58
|
*/
|
|
506
|
-
|
|
59
|
+
unsafeSyncParseModel() {
|
|
507
60
|
const docs = this.documents();
|
|
508
61
|
if (docs.length === 0) {
|
|
509
|
-
logger.debug("
|
|
62
|
+
logger.debug("no documents to build model from");
|
|
510
63
|
return null;
|
|
511
64
|
}
|
|
512
|
-
const cache = this.
|
|
65
|
+
const cache = this.cache;
|
|
513
66
|
return cache.get(CACHE_KEY_PARSED_MODEL, () => {
|
|
514
|
-
logger.debug
|
|
515
|
-
return buildModel(
|
|
67
|
+
logger.debug("unsafeSyncParseModel ({docslength} docs)", { docslength: docs.length });
|
|
68
|
+
return buildModel(docs);
|
|
516
69
|
});
|
|
517
70
|
}
|
|
518
|
-
async
|
|
519
|
-
const cache = this.
|
|
71
|
+
async parseModel(cancelToken) {
|
|
72
|
+
const cache = this.cache;
|
|
520
73
|
const cached = cache.get(CACHE_KEY_PARSED_MODEL);
|
|
521
74
|
if (cached) {
|
|
522
|
-
return cached;
|
|
75
|
+
return await Promise.resolve(cached);
|
|
523
76
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
});
|
|
77
|
+
if (this.LangiumDocuments.all.some((doc) => doc.state < DocumentState.Validated)) {
|
|
78
|
+
logger.debug("parseModel: waiting for documents to be validated");
|
|
79
|
+
await this.DocumentBuilder.waitUntil(DocumentState.Validated, cancelToken);
|
|
80
|
+
}
|
|
81
|
+
return this.unsafeSyncParseModel();
|
|
530
82
|
}
|
|
531
83
|
previousViews = {};
|
|
532
84
|
/**
|
|
@@ -534,16 +86,24 @@ export class LikeC4ModelBuilder extends ADisposable {
|
|
|
534
86
|
* This method is internal and should to be called only when all documents are known to be parsed.
|
|
535
87
|
* Otherwise, the model may be incomplete.
|
|
536
88
|
*/
|
|
537
|
-
|
|
538
|
-
const cache = this.
|
|
539
|
-
const viewsCache = this.
|
|
89
|
+
unsafeSyncBuildModel() {
|
|
90
|
+
const cache = this.cache;
|
|
91
|
+
const viewsCache = this.cache;
|
|
540
92
|
return cache.get(CACHE_KEY_COMPUTED_MODEL, () => {
|
|
541
|
-
const
|
|
93
|
+
const parsed = this.unsafeSyncParseModel();
|
|
94
|
+
if (!parsed) {
|
|
95
|
+
return LikeC4Model.EMPTY;
|
|
96
|
+
}
|
|
97
|
+
const {
|
|
98
|
+
views: parsedViews,
|
|
99
|
+
...model
|
|
100
|
+
} = parsed;
|
|
101
|
+
const computeView = LikeC4Model.makeCompute(parsed);
|
|
542
102
|
const allViews = [];
|
|
543
|
-
for (const view of values(
|
|
103
|
+
for (const view of values(parsedViews)) {
|
|
544
104
|
const result = computeView(view);
|
|
545
105
|
if (!result.isSuccess) {
|
|
546
|
-
|
|
106
|
+
logger.warn(loggable(result.error));
|
|
547
107
|
continue;
|
|
548
108
|
}
|
|
549
109
|
allViews.push(result.view);
|
|
@@ -556,74 +116,69 @@ export class LikeC4ModelBuilder extends ADisposable {
|
|
|
556
116
|
return [v.id, view];
|
|
557
117
|
});
|
|
558
118
|
this.previousViews = { ...views };
|
|
559
|
-
return {
|
|
560
|
-
...
|
|
119
|
+
return LikeC4Model.create({
|
|
120
|
+
...model,
|
|
561
121
|
views
|
|
562
|
-
};
|
|
122
|
+
});
|
|
563
123
|
});
|
|
564
124
|
}
|
|
565
|
-
async
|
|
566
|
-
const cache = this.
|
|
567
|
-
|
|
568
|
-
|
|
125
|
+
async buildLikeC4Model(cancelToken) {
|
|
126
|
+
const cache = this.cache;
|
|
127
|
+
const cached = cache.get(CACHE_KEY_COMPUTED_MODEL);
|
|
128
|
+
if (cached) {
|
|
129
|
+
return await Promise.resolve(cached);
|
|
569
130
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
if (!model) {
|
|
576
|
-
return null;
|
|
577
|
-
}
|
|
578
|
-
return this.unsafeSyncBuildComputedModel(model);
|
|
579
|
-
});
|
|
131
|
+
const model = await this.parseModel(cancelToken);
|
|
132
|
+
if (!model) {
|
|
133
|
+
return LikeC4Model.EMPTY;
|
|
134
|
+
}
|
|
135
|
+
return this.unsafeSyncBuildModel();
|
|
580
136
|
}
|
|
581
137
|
async computeView(viewId, cancelToken) {
|
|
582
|
-
const cache = this.
|
|
138
|
+
const cache = this.cache;
|
|
583
139
|
const cacheKey = computedViewKey(viewId);
|
|
584
140
|
if (cache.has(cacheKey)) {
|
|
585
141
|
return cache.get(cacheKey);
|
|
586
142
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
143
|
+
const parsed = await this.parseModel(cancelToken);
|
|
144
|
+
if (!parsed) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return cache.get(cacheKey, () => {
|
|
148
|
+
const view = parsed.views[viewId];
|
|
149
|
+
if (!view) {
|
|
150
|
+
logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
|
|
151
|
+
return null;
|
|
590
152
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
);
|
|
611
|
-
for (const node of computedView.nodes) {
|
|
612
|
-
if (!node.navigateTo) {
|
|
613
|
-
const viewsOfNode = allElementViews[node.id];
|
|
614
|
-
if (viewsOfNode) {
|
|
615
|
-
node.navigateTo = viewsOfNode[0].id;
|
|
616
|
-
}
|
|
153
|
+
const computeView = LikeC4Model.makeCompute(parsed);
|
|
154
|
+
const result = computeView(view);
|
|
155
|
+
if (!result.isSuccess) {
|
|
156
|
+
logWarnError(result.error);
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
let computedView = result.view;
|
|
160
|
+
const allElementViews = pipe(
|
|
161
|
+
parsed.views,
|
|
162
|
+
values(),
|
|
163
|
+
filter(isScopedElementView),
|
|
164
|
+
filter((v) => v.id !== viewId),
|
|
165
|
+
groupBy((v) => v.viewOf)
|
|
166
|
+
);
|
|
167
|
+
for (const node of computedView.nodes) {
|
|
168
|
+
if (!node.navigateTo) {
|
|
169
|
+
const viewsOfNode = allElementViews[node.id];
|
|
170
|
+
if (viewsOfNode) {
|
|
171
|
+
node.navigateTo = viewsOfNode[0].id;
|
|
617
172
|
}
|
|
618
173
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
174
|
+
}
|
|
175
|
+
const previous = this.previousViews[viewId];
|
|
176
|
+
if (previous && eq(computedView, previous)) {
|
|
177
|
+
computedView = previous;
|
|
178
|
+
} else {
|
|
179
|
+
this.previousViews[viewId] = computedView;
|
|
180
|
+
}
|
|
181
|
+
return computedView;
|
|
627
182
|
});
|
|
628
183
|
}
|
|
629
184
|
onModelParsed(callback) {
|
|
@@ -636,7 +191,7 @@ export class LikeC4ModelBuilder extends ADisposable {
|
|
|
636
191
|
});
|
|
637
192
|
}
|
|
638
193
|
documents() {
|
|
639
|
-
return this.
|
|
194
|
+
return this.parser.documents().toArray();
|
|
640
195
|
}
|
|
641
196
|
notifyListeners(docs) {
|
|
642
197
|
for (const listener of this.listeners) {
|