@likec4/language-server 1.23.1 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LikeC4FileSystem.d.ts +1 -0
- package/dist/LikeC4FileSystem.js +7 -0
- package/dist/Rpc.js +10 -7
- package/dist/ast.d.ts +13 -29
- package/dist/ast.js +3 -70
- package/dist/bundled.mjs +2466 -2641
- package/dist/generated/ast.d.ts +36 -8
- package/dist/generated/ast.js +44 -2
- package/dist/generated/grammar.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/likec4lib.d.ts +2 -0
- package/dist/likec4lib.js +3 -0
- package/dist/lsp/CodeLensProvider.js +7 -4
- package/dist/lsp/CompletionProvider.js +20 -2
- package/dist/lsp/DocumentLinkProvider.d.ts +3 -3
- package/dist/lsp/DocumentLinkProvider.js +14 -5
- package/dist/lsp/DocumentSymbolProvider.d.ts +1 -1
- package/dist/lsp/DocumentSymbolProvider.js +5 -2
- package/dist/lsp/HoverProvider.js +20 -7
- package/dist/lsp/SemanticTokenProvider.js +18 -1
- package/dist/model/builder/MergedExtends.d.ts +12 -0
- package/dist/model/builder/MergedExtends.js +67 -0
- package/dist/model/builder/MergedSpecification.d.ts +29 -0
- package/dist/model/builder/MergedSpecification.js +203 -0
- package/dist/model/builder/buildModel.d.ts +3 -0
- package/dist/model/builder/buildModel.js +194 -0
- package/dist/model/deployments-index.d.ts +6 -56
- package/dist/model/deployments-index.js +59 -137
- package/dist/model/fqn-index.d.ts +47 -17
- package/dist/model/fqn-index.js +155 -68
- package/dist/model/index.d.ts +0 -1
- package/dist/model/index.js +0 -1
- package/dist/model/model-builder.d.ts +13 -9
- package/dist/model/model-builder.js +101 -547
- package/dist/model/model-locator.d.ts +1 -0
- package/dist/model/model-locator.js +7 -9
- package/dist/model/model-parser.d.ts +24 -18
- package/dist/model/model-parser.js +51 -31
- package/dist/model/parser/Base.d.ts +3 -3
- package/dist/model/parser/Base.js +15 -9
- package/dist/model/parser/DeploymentModelParser.d.ts +4 -3
- package/dist/model/parser/DeploymentModelParser.js +54 -3
- package/dist/model/parser/DeploymentViewParser.d.ts +3 -2
- package/dist/model/parser/FqnRefParser.d.ts +2 -2
- package/dist/model/parser/GlobalsParser.d.ts +3 -2
- package/dist/model/parser/ModelParser.d.ts +4 -4
- package/dist/model/parser/ModelParser.js +45 -4
- package/dist/model/parser/PredicatesParser.d.ts +2 -2
- package/dist/model/parser/SpecificationParser.d.ts +2 -2
- package/dist/model/parser/ViewsParser.d.ts +3 -2
- package/dist/module.d.ts +2 -3
- package/dist/module.js +2 -3
- package/dist/references/scope-computation.d.ts +1 -1
- package/dist/references/scope-computation.js +14 -11
- package/dist/references/scope-provider.d.ts +16 -4
- package/dist/references/scope-provider.js +64 -30
- package/dist/test/testServices.d.ts +2 -1
- package/dist/test/testServices.js +17 -14
- package/dist/utils/elementRef.d.ts +1 -1
- package/dist/utils/elementRef.js +3 -3
- package/dist/validation/deployment-checks.d.ts +1 -0
- package/dist/validation/deployment-checks.js +12 -0
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +8 -1
- package/dist/views/configurable-layouter.js +3 -3
- package/dist/views/likec4-views.d.ts +1 -0
- package/dist/views/likec4-views.js +11 -11
- package/package.json +6 -7
- package/dist/bundled.d.ts +0 -8
- package/dist/bundled.js +0 -25
- package/dist/model/fqn-computation.d.ts +0 -3
- package/dist/model/fqn-computation.js +0 -72
|
@@ -1,532 +1,82 @@
|
|
|
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("model-builder");
|
|
464
25
|
export class LikeC4ModelBuilder extends ADisposable {
|
|
26
|
+
parser;
|
|
27
|
+
listeners = [];
|
|
28
|
+
cache;
|
|
29
|
+
DocumentBuilder;
|
|
465
30
|
constructor(services) {
|
|
466
31
|
super();
|
|
467
|
-
this.
|
|
468
|
-
this.
|
|
469
|
-
|
|
32
|
+
this.parser = services.likec4.ModelParser;
|
|
33
|
+
this.cache = services.ValidatedWorkspaceCache;
|
|
34
|
+
this.DocumentBuilder = services.shared.workspace.DocumentBuilder;
|
|
470
35
|
this.onDispose(
|
|
471
|
-
|
|
36
|
+
this.DocumentBuilder.onUpdate((_changed, deleted) => {
|
|
472
37
|
if (deleted.length > 0) {
|
|
473
38
|
this.notifyListeners(deleted);
|
|
474
39
|
}
|
|
475
40
|
})
|
|
476
41
|
);
|
|
477
42
|
this.onDispose(
|
|
478
|
-
|
|
43
|
+
this.DocumentBuilder.onBuildPhase(
|
|
479
44
|
DocumentState.Validated,
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
}
|
|
45
|
+
(docs, _cancelToken) => {
|
|
46
|
+
logger.debug("onValidated ({docslength} docs)", { docslength: docs.length });
|
|
47
|
+
this.notifyListeners(docs.map((d) => d.uri));
|
|
494
48
|
}
|
|
495
49
|
)
|
|
496
50
|
);
|
|
497
|
-
logger.debug`
|
|
51
|
+
logger.debug`created`;
|
|
498
52
|
}
|
|
499
|
-
langiumDocuments;
|
|
500
|
-
listeners = [];
|
|
501
53
|
/**
|
|
502
54
|
* WARNING:
|
|
503
55
|
* This method is internal and should to be called only when all documents are known to be parsed.
|
|
504
56
|
* Otherwise, the model may be incomplete.
|
|
505
57
|
*/
|
|
506
|
-
|
|
58
|
+
unsafeSyncParseModel() {
|
|
507
59
|
const docs = this.documents();
|
|
508
60
|
if (docs.length === 0) {
|
|
509
|
-
logger.debug("
|
|
61
|
+
logger.debug("no documents to build model from");
|
|
510
62
|
return null;
|
|
511
63
|
}
|
|
512
|
-
const cache = this.
|
|
64
|
+
const cache = this.cache;
|
|
513
65
|
return cache.get(CACHE_KEY_PARSED_MODEL, () => {
|
|
514
|
-
logger.debug
|
|
515
|
-
|
|
66
|
+
logger.debug("unsafeSyncParseModel ({docslength} docs)", { docslength: docs.length });
|
|
67
|
+
const model = buildModel(docs);
|
|
68
|
+
const computeView = LikeC4Model.makeCompute(model);
|
|
69
|
+
return { model, computeView };
|
|
516
70
|
});
|
|
517
71
|
}
|
|
518
|
-
async
|
|
519
|
-
const cache = this.
|
|
72
|
+
async parseModel(cancelToken) {
|
|
73
|
+
const cache = this.cache;
|
|
520
74
|
const cached = cache.get(CACHE_KEY_PARSED_MODEL);
|
|
521
75
|
if (cached) {
|
|
522
|
-
return cached;
|
|
76
|
+
return await Promise.resolve(cached);
|
|
523
77
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
await interruptAndCheck(cancelToken);
|
|
527
|
-
}
|
|
528
|
-
return this.unsafeSyncBuildModel();
|
|
529
|
-
});
|
|
78
|
+
await this.DocumentBuilder.waitUntil(DocumentState.Validated, cancelToken);
|
|
79
|
+
return this.unsafeSyncParseModel();
|
|
530
80
|
}
|
|
531
81
|
previousViews = {};
|
|
532
82
|
/**
|
|
@@ -534,16 +84,26 @@ export class LikeC4ModelBuilder extends ADisposable {
|
|
|
534
84
|
* This method is internal and should to be called only when all documents are known to be parsed.
|
|
535
85
|
* Otherwise, the model may be incomplete.
|
|
536
86
|
*/
|
|
537
|
-
|
|
538
|
-
const
|
|
539
|
-
|
|
87
|
+
unsafeSyncBuildModel() {
|
|
88
|
+
const parsed = this.unsafeSyncParseModel();
|
|
89
|
+
if (!parsed) {
|
|
90
|
+
return LikeC4Model.EMPTY;
|
|
91
|
+
}
|
|
92
|
+
const cache = this.cache;
|
|
93
|
+
const viewsCache = this.cache;
|
|
540
94
|
return cache.get(CACHE_KEY_COMPUTED_MODEL, () => {
|
|
541
|
-
const
|
|
95
|
+
const {
|
|
96
|
+
model: {
|
|
97
|
+
views: parsedViews,
|
|
98
|
+
...model
|
|
99
|
+
},
|
|
100
|
+
computeView
|
|
101
|
+
} = 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,68 @@ 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.model.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
|
-
groupBy((v) => v.viewOf)
|
|
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 result = parsed.computeView(view);
|
|
154
|
+
if (!result.isSuccess) {
|
|
155
|
+
logWarnError(result.error);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
let computedView = result.view;
|
|
159
|
+
const allElementViews = pipe(
|
|
160
|
+
parsed.model.views,
|
|
161
|
+
values(),
|
|
162
|
+
filter(isScopedElementView),
|
|
163
|
+
filter((v) => v.id !== viewId),
|
|
164
|
+
groupBy((v) => v.viewOf)
|
|
165
|
+
);
|
|
166
|
+
for (const node of computedView.nodes) {
|
|
167
|
+
if (!node.navigateTo) {
|
|
168
|
+
const viewsOfNode = allElementViews[node.id];
|
|
169
|
+
if (viewsOfNode) {
|
|
170
|
+
node.navigateTo = viewsOfNode[0].id;
|
|
617
171
|
}
|
|
618
172
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
173
|
+
}
|
|
174
|
+
const previous = this.previousViews[viewId];
|
|
175
|
+
if (previous && eq(computedView, previous)) {
|
|
176
|
+
computedView = previous;
|
|
177
|
+
} else {
|
|
178
|
+
this.previousViews[viewId] = computedView;
|
|
179
|
+
}
|
|
180
|
+
return computedView;
|
|
627
181
|
});
|
|
628
182
|
}
|
|
629
183
|
onModelParsed(callback) {
|
|
@@ -636,7 +190,7 @@ export class LikeC4ModelBuilder extends ADisposable {
|
|
|
636
190
|
});
|
|
637
191
|
}
|
|
638
192
|
documents() {
|
|
639
|
-
return this.
|
|
193
|
+
return this.parser.documents().toArray();
|
|
640
194
|
}
|
|
641
195
|
notifyListeners(docs) {
|
|
642
196
|
for (const listener of this.listeners) {
|