@likec4/language-server 1.17.1 → 1.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.
Files changed (89) hide show
  1. package/contrib/likec4.tmLanguage.json +1 -1
  2. package/dist/browser.cjs +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.mts +2 -2
  5. package/dist/browser.d.ts +2 -2
  6. package/dist/browser.mjs +2 -2
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.d.cts +2 -2
  9. package/dist/index.d.mts +2 -2
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.mjs +2 -2
  12. package/dist/protocol.d.cts +8 -5
  13. package/dist/protocol.d.mts +8 -5
  14. package/dist/protocol.d.ts +8 -5
  15. package/dist/shared/{language-server.DKV_FdPN.cjs → language-server.CO_nmHiL.cjs} +5598 -4213
  16. package/dist/shared/{language-server.BQRvVmE0.d.cts → language-server.Da6ey08o.d.cts} +390 -74
  17. package/dist/shared/{language-server.BysPcTxr.d.ts → language-server.De7S3e5Z.d.ts} +390 -74
  18. package/dist/shared/{language-server._wkyPgso.d.mts → language-server.Dj4iDjtB.d.mts} +390 -74
  19. package/dist/shared/{language-server.BIbAD1T-.mjs → language-server.oO_9JoAG.mjs} +5604 -4230
  20. package/package.json +11 -25
  21. package/src/Rpc.ts +6 -3
  22. package/src/ast.ts +124 -71
  23. package/src/generated/ast.ts +655 -39
  24. package/src/generated/grammar.ts +1 -1
  25. package/src/index.ts +1 -0
  26. package/src/like-c4.langium +170 -22
  27. package/src/logger.ts +7 -2
  28. package/src/lsp/CodeLensProvider.ts +0 -1
  29. package/src/lsp/CompletionProvider.ts +17 -2
  30. package/src/lsp/DocumentSymbolProvider.ts +5 -2
  31. package/src/lsp/HoverProvider.ts +34 -2
  32. package/src/lsp/SemanticTokenProvider.ts +58 -32
  33. package/src/model/deployments-index.ts +218 -0
  34. package/src/model/fqn-computation.ts +1 -1
  35. package/src/model/fqn-index.ts +0 -1
  36. package/src/model/index.ts +1 -0
  37. package/src/model/model-builder.ts +172 -37
  38. package/src/model/model-locator.ts +36 -7
  39. package/src/model/model-parser.ts +554 -92
  40. package/src/model-change/changeViewLayout.ts +2 -2
  41. package/src/module.ts +10 -4
  42. package/src/protocol.ts +10 -6
  43. package/src/references/index.ts +1 -0
  44. package/src/references/name-provider.ts +37 -0
  45. package/src/references/scope-computation.ts +130 -21
  46. package/src/references/scope-provider.ts +63 -36
  47. package/src/shared/NodeKindProvider.ts +15 -3
  48. package/src/utils/deploymentRef.ts +31 -0
  49. package/src/{elementRef.ts → utils/elementRef.ts} +1 -1
  50. package/src/utils/stringHash.ts +2 -2
  51. package/src/validation/_shared.ts +7 -5
  52. package/src/validation/deployment-checks.ts +144 -0
  53. package/src/validation/dynamic-view-step.ts +1 -1
  54. package/src/validation/index.ts +7 -0
  55. package/src/validation/relation.ts +1 -1
  56. package/src/validation/view-predicates/deployments.ts +56 -0
  57. package/src/validation/view-predicates/index.ts +1 -0
  58. package/src/view-utils/assignNavigateTo.ts +6 -5
  59. package/src/view-utils/index.ts +0 -1
  60. package/dist/model-graph/index.cjs +0 -10
  61. package/dist/model-graph/index.d.cts +0 -81
  62. package/dist/model-graph/index.d.mts +0 -81
  63. package/dist/model-graph/index.d.ts +0 -81
  64. package/dist/model-graph/index.mjs +0 -1
  65. package/dist/shared/language-server.D2QdbOJO.cjs +0 -1995
  66. package/dist/shared/language-server.DGrBGmsd.mjs +0 -1981
  67. package/src/model-graph/LikeC4ModelGraph.ts +0 -338
  68. package/src/model-graph/compute-view/__test__/fixture.ts +0 -630
  69. package/src/model-graph/compute-view/compute.ts +0 -788
  70. package/src/model-graph/compute-view/index.ts +0 -33
  71. package/src/model-graph/compute-view/predicates.ts +0 -509
  72. package/src/model-graph/dynamic-view/__test__/fixture.ts +0 -61
  73. package/src/model-graph/dynamic-view/compute.ts +0 -313
  74. package/src/model-graph/dynamic-view/index.ts +0 -29
  75. package/src/model-graph/index.ts +0 -3
  76. package/src/model-graph/utils/applyCustomElementProperties.ts +0 -65
  77. package/src/model-graph/utils/applyCustomRelationProperties.ts +0 -41
  78. package/src/model-graph/utils/applyViewRuleStyles.ts +0 -49
  79. package/src/model-graph/utils/buildComputeNodes.ts +0 -113
  80. package/src/model-graph/utils/buildElementNotations.ts +0 -63
  81. package/src/model-graph/utils/elementExpressionToPredicate.ts +0 -39
  82. package/src/model-graph/utils/relationExpressionToPredicates.ts +0 -43
  83. package/src/model-graph/utils/sortNodes.ts +0 -105
  84. package/src/model-graph/utils/uniqueTags.test.ts +0 -42
  85. package/src/model-graph/utils/uniqueTags.ts +0 -19
  86. package/src/utils/graphlib.ts +0 -9
  87. package/src/view-utils/resolve-extended-views.ts +0 -66
  88. package/src/view-utils/resolve-global-rules.ts +0 -88
  89. package/src/view-utils/view-hash.ts +0 -27
@@ -0,0 +1,218 @@
1
+ import type { Fqn } from '@likec4/core'
2
+ import type { LangiumDocument, LangiumDocuments, Stream } from 'langium'
3
+ import { AstUtils, DocumentState, MultiMap } from 'langium'
4
+ import { forEachObj, groupBy, isTruthy, pipe, prop } from 'remeda'
5
+ import {
6
+ ast,
7
+ type DeploymentAstNodeDescription,
8
+ ElementOps,
9
+ isLikeC4LangiumDocument,
10
+ type LikeC4LangiumDocument
11
+ } from '../ast'
12
+ import { logWarnError } from '../logger'
13
+ import type { LikeC4Services } from '../module'
14
+ import type { LikeC4NameProvider } from '../references'
15
+
16
+ const DeploymentsIndexKey = Symbol.for('DeploymentsIndex')
17
+
18
+ type IndexedDocument = LangiumDocument & {
19
+ [DeploymentsIndexKey]?: DocumentDeploymentsIndex
20
+ }
21
+
22
+ export class DeploymentsIndex {
23
+ protected Names: LikeC4NameProvider
24
+ protected langiumDocuments: LangiumDocuments
25
+
26
+ constructor(private services: LikeC4Services) {
27
+ this.Names = services.references.NameProvider
28
+ this.langiumDocuments = services.shared.workspace.LangiumDocuments
29
+
30
+ services.shared.workspace.DocumentBuilder.onBuildPhase(
31
+ DocumentState.IndexedContent,
32
+ (docs, _cancelToken) => {
33
+ for (const doc of docs) {
34
+ delete (doc as IndexedDocument)[DeploymentsIndexKey]
35
+ }
36
+ }
37
+ )
38
+ }
39
+
40
+ private documents() {
41
+ return this.langiumDocuments.all.filter((d): d is LikeC4LangiumDocument =>
42
+ isLikeC4LangiumDocument(d) && d.state >= DocumentState.IndexedContent
43
+ )
44
+ }
45
+
46
+ public get(document: LikeC4LangiumDocument): DocumentDeploymentsIndex {
47
+ if (document.state < DocumentState.IndexedContent) {
48
+ logWarnError(`Document ${document.uri.path} is not indexed`)
49
+ }
50
+ return (document as IndexedDocument)[DeploymentsIndexKey] ??= this.createDocumentIndex(document)
51
+ }
52
+ /**
53
+ * Nested elements (nodes/artifacts) of the node
54
+ * @param nodeName Name of the deployment node
55
+ * @returns Stream of artifacts
56
+ */
57
+ public nested(node: ast.DeploymentNode): Stream<DeploymentAstNodeDescription> {
58
+ const fqnName = this.getFqnName(node)
59
+ return this.documents().flatMap(doc => this.get(doc).nested(fqnName))
60
+ }
61
+
62
+ public byFqn(fqnName: string): Stream<DeploymentAstNodeDescription> {
63
+ return this.documents().flatMap(doc => this.get(doc).byFqn(fqnName))
64
+ }
65
+
66
+ public getFqnName(node: ast.DeploymentElement): Fqn {
67
+ let id = ElementOps.readId(node)
68
+ if (isTruthy(id)) {
69
+ return id
70
+ }
71
+ const fqn = [
72
+ this.Names.getNameStrict(node)
73
+ ]
74
+ let parentNode: ast.DeploymentNode | undefined
75
+ while ((parentNode = AstUtils.getContainerOfType(node.$container, ast.isDeploymentNode))) {
76
+ fqn.push(this.Names.getNameStrict(parentNode))
77
+ node = parentNode
78
+ }
79
+ id = fqn.reduceRight((acc, cur) => `${acc}.${cur}`) as Fqn
80
+ ElementOps.writeId(node, id)
81
+ return id
82
+ }
83
+
84
+ public createDocumentIndex(document: LikeC4LangiumDocument): DocumentDeploymentsIndex {
85
+ const rootNodes = document.parseResult.value.deployments.flatMap(m => m.elements)
86
+ if (rootNodes.length === 0) {
87
+ return DocumentDeploymentsIndex.EMPTY
88
+ }
89
+ const _root = new Array<DeploymentAstNodeDescription>()
90
+ const _nested = new MultiMap<string, DeploymentAstNodeDescription>()
91
+ const _byfqn = new MultiMap<string, DeploymentAstNodeDescription>()
92
+ const Names = this.Names
93
+ const Descriptions = this.services.workspace.AstNodeDescriptionProvider
94
+
95
+ const createAndSaveDescription = (
96
+ props: { node: ast.DeploymentNode | ast.DeployedInstance; name: string; fqn: string }
97
+ ) => {
98
+ const desc = {
99
+ ...Descriptions.createDescription(props.node, props.name, document),
100
+ fqn: props.fqn
101
+ }
102
+ ElementOps.writeId(props.node, props.fqn as Fqn)
103
+ _byfqn.add(props.fqn, desc)
104
+ return desc
105
+ }
106
+
107
+ const traverseNode = (
108
+ container: ast.DeploymentNode,
109
+ parentFqn: string
110
+ ): readonly DeploymentAstNodeDescription[] => {
111
+ const _descedants = [] as DeploymentAstNodeDescription[]
112
+ const children = container.body?.elements
113
+ if (!children || children.length === 0) {
114
+ return []
115
+ }
116
+ const directChildren = new Set<string>()
117
+ for (const node of children) {
118
+ if (ast.isDeploymentRelation(node)) {
119
+ continue
120
+ }
121
+ try {
122
+ const name = Names.getName(node)
123
+ if (isTruthy(name)) {
124
+ const fqn = `${parentFqn}.${name}`
125
+ const desc = createAndSaveDescription({ node, name, fqn })
126
+ _nested.add(parentFqn, desc)
127
+ directChildren.add(desc.name)
128
+ if (ast.isDeploymentNode(node) && node.body) {
129
+ _descedants.push(...traverseNode(node, fqn))
130
+ }
131
+ }
132
+ } catch (e) {
133
+ logWarnError(e)
134
+ }
135
+ }
136
+ if (_descedants.length > 0) {
137
+ pipe(
138
+ _descedants,
139
+ groupBy(prop('name')),
140
+ forEachObj((descs, key) => {
141
+ if (descs.length > 1 || directChildren.has(key)) {
142
+ return
143
+ }
144
+ _nested.add(parentFqn, descs[0])
145
+ })
146
+ )
147
+ }
148
+ return _nested.get(parentFqn)
149
+ }
150
+
151
+ for (const node of rootNodes) {
152
+ try {
153
+ if (ast.isDeploymentRelation(node)) {
154
+ continue
155
+ }
156
+ const name = Names.getName(node)
157
+ if (isTruthy(name)) {
158
+ _root.push(createAndSaveDescription({ node, name, fqn: name }))
159
+ traverseNode(node, name)
160
+ }
161
+ } catch (e) {
162
+ logWarnError(e)
163
+ }
164
+ }
165
+ return new DocumentDeploymentsIndex(_root, _nested, _byfqn)
166
+ }
167
+ }
168
+
169
+ export class DocumentDeploymentsIndex {
170
+ static readonly EMPTY = new DocumentDeploymentsIndex([], new MultiMap(), new MultiMap())
171
+
172
+ constructor(
173
+ private _rootNodes: Array<DeploymentAstNodeDescription>,
174
+ /**
175
+ * Nested of a deployment node
176
+ */
177
+ private _nested: MultiMap<string, DeploymentAstNodeDescription>,
178
+ /**
179
+ * All elements by FQN
180
+ */
181
+ private _byfqn: MultiMap<string, DeploymentAstNodeDescription>
182
+ ) {}
183
+
184
+ public rootNodes(): readonly DeploymentAstNodeDescription[] {
185
+ return this._rootNodes
186
+ }
187
+
188
+ public byFqn(fqnName: string): readonly DeploymentAstNodeDescription[] {
189
+ return this._byfqn.get(fqnName)
190
+ }
191
+
192
+ /**
193
+ * Returns artifacts of a deployment node
194
+ * @param nodeName Name of the deployment node
195
+ * @returns Stream of artifacts
196
+ */
197
+ public nested(nodeName: string): readonly DeploymentAstNodeDescription[] {
198
+ return this._nested.get(nodeName)
199
+ }
200
+
201
+ /**
202
+ * Returns all deployment elements in the document,
203
+ * with unique combination "type and name"
204
+ */
205
+ public unique(): readonly DeploymentAstNodeDescription[] {
206
+ const result = [] as DeploymentAstNodeDescription[]
207
+ pipe(
208
+ [...this._byfqn.values()],
209
+ groupBy(prop('name')),
210
+ forEachObj(descs => {
211
+ if (descs.length === 1) {
212
+ result.push(descs[0])
213
+ }
214
+ })
215
+ )
216
+ return result
217
+ }
218
+ }
@@ -3,9 +3,9 @@ import type * as c4 from '@likec4/core'
3
3
  import { type AstNodeDescription, type AstNodeLocator, AstUtils, CstUtils, GrammarUtils, MultiMap } from 'langium'
4
4
  import { isDefined, isEmpty } from 'remeda'
5
5
  import { ast, ElementOps, type LikeC4LangiumDocument } from '../ast'
6
- import { getFqnElementRef } from '../elementRef'
7
6
  import { logError } from '../logger'
8
7
  import type { LikeC4Services } from '../module'
8
+ import { getFqnElementRef } from '../utils/elementRef'
9
9
 
10
10
  const { findNodeForProperty } = GrammarUtils
11
11
  const { toDocumentSegment } = CstUtils
@@ -6,7 +6,6 @@ import type { ast, DocFqnIndexAstNodeDescription, FqnIndexedDocument } from '../
6
6
  import { ElementOps, isFqnIndexedDocument, isLikeC4LangiumDocument } from '../ast'
7
7
  import { logger, logWarnError } from '../logger'
8
8
  import type { LikeC4Services } from '../module'
9
- import { printDocs } from '../utils/printDocs'
10
9
  import { computeDocumentFqn } from './fqn-computation'
11
10
 
12
11
  export interface FqnIndexEntry {
@@ -1,3 +1,4 @@
1
+ export * from './deployments-index'
1
2
  export * from './fqn-computation'
2
3
  export * from './fqn-index'
3
4
  export * from './model-builder'
@@ -1,14 +1,14 @@
1
- import type * as c4 from '@likec4/core'
1
+ import * as c4 from '@likec4/core'
2
2
  import {
3
3
  compareRelations,
4
4
  computeColorValues,
5
5
  type CustomColorDefinitions,
6
- isElementView,
7
6
  isScopedElementView,
8
7
  parentFqn,
9
8
  sortByFqnHierarchically,
10
- type ViewID
9
+ type ViewId
11
10
  } from '@likec4/core'
11
+ import { mkComputeView, resolveRulesExtendedViews } from '@likec4/core/compute-view'
12
12
  import { deepEqual as eq } from 'fast-equals'
13
13
  import type { Cancellation, LangiumDocument, LangiumDocuments, URI, WorkspaceCache } from 'langium'
14
14
  import { Disposable, DocumentState, interruptAndCheck } from 'langium'
@@ -17,6 +17,7 @@ import {
17
17
  flatMap,
18
18
  groupBy,
19
19
  indexBy,
20
+ isDefined,
20
21
  isEmpty,
21
22
  isNonNullish,
22
23
  isNullish,
@@ -25,6 +26,7 @@ import {
25
26
  map,
26
27
  mapToObj,
27
28
  mapValues,
29
+ pick,
28
30
  pipe,
29
31
  prop,
30
32
  reduce,
@@ -33,6 +35,7 @@ import {
33
35
  values
34
36
  } from 'remeda'
35
37
  import type {
38
+ ParsedAstDeploymentRelation,
36
39
  ParsedAstElement,
37
40
  ParsedAstRelation,
38
41
  ParsedAstSpecification,
@@ -42,14 +45,14 @@ import type {
42
45
  } from '../ast'
43
46
  import { isParsedLikeC4LangiumDocument } from '../ast'
44
47
  import { logError, logger, logWarnError } from '../logger'
45
- import { computeDynamicView, computeView, LikeC4ModelGraph } from '../model-graph'
46
48
  import type { LikeC4Services } from '../module'
47
- import { assignNavigateTo, resolveRelativePaths, resolveRulesExtendedViews } from '../view-utils'
49
+ import { assignNavigateTo, resolveRelativePaths } from '../view-utils'
48
50
 
49
51
  function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[]): c4.ParsedLikeC4Model {
50
52
  // Merge specifications and globals from all documents
51
53
  const c4Specification: ParsedAstSpecification = {
52
54
  tags: new Set(),
55
+ deployments: {},
53
56
  elements: {},
54
57
  relationships: {},
55
58
  colors: {}
@@ -69,7 +72,7 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
69
72
  Object.assign(c4Specification.elements, spec.elements)
70
73
  Object.assign(c4Specification.relationships, spec.relationships)
71
74
  Object.assign(c4Specification.colors, spec.colors)
72
-
75
+ Object.assign(c4Specification.deployments, spec.deployments)
73
76
  Object.assign(globals.predicates, c4Globals.predicates)
74
77
  Object.assign(globals.dynamicPredicates, c4Globals.dynamicPredicates)
75
78
  Object.assign(globals.styles, c4Globals.styles)
@@ -189,7 +192,9 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
189
192
  }: ParsedAstRelation): c4.Relation | null => {
190
193
  if (isNullish(elements[source]) || isNullish(elements[target])) {
191
194
  logger.warn(
192
- `Invalid relation ${id}, source: ${source}(${!!elements[source]}), target: ${target}(${!!elements[target]})`
195
+ `Invalid relation ${id} at ${doc.uri.path} ${astPath}, source: ${source}(${!!elements[
196
+ source
197
+ ]}), target: ${target}(${!!elements[target]})`
193
198
  )
194
199
  return null
195
200
  }
@@ -207,7 +212,7 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
207
212
  } satisfies c4.Relation
208
213
  }
209
214
  return {
210
- ...(links && { links }),
215
+ ...links && { links },
211
216
  ...model,
212
217
  source,
213
218
  target,
@@ -225,6 +230,128 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
225
230
  indexBy(prop('id'))
226
231
  )
227
232
 
233
+ function toDeploymentElement(doc: LangiumDocument) {
234
+ return (parsed: c4.DeploymentElement): c4.DeploymentElement | null => {
235
+ if (!c4.DeploymentElement.isDeploymentNode(parsed)) {
236
+ if (!parsed.links || parsed.links.length === 0) {
237
+ return parsed
238
+ }
239
+ const links = resolveLinks(doc, parsed.links)
240
+ return {
241
+ ...parsed,
242
+ links
243
+ }
244
+ }
245
+ try {
246
+ const __kind = c4Specification.deployments[parsed.kind]
247
+ if (!__kind) {
248
+ logger.warn(`No kind '${parsed.kind}' found for ${parsed.id}`)
249
+ return null
250
+ }
251
+ let {
252
+ technology = __kind.technology,
253
+ notation = __kind.notation,
254
+ links,
255
+ style
256
+ } = parsed
257
+ return {
258
+ ...parsed,
259
+ ...notation && { notation },
260
+ ...technology && { technology },
261
+ style: {
262
+ border: 'dashed',
263
+ opacity: 10,
264
+ ...__kind.style,
265
+ ...style
266
+ },
267
+ links: links ? resolveLinks(doc, links) : null
268
+ }
269
+ } catch (e) {
270
+ logWarnError(e)
271
+ }
272
+ return null
273
+ }
274
+ }
275
+
276
+ const deploymentElements = pipe(
277
+ docs,
278
+ flatMap(d => map(d.c4Deployments, toDeploymentElement(d))),
279
+ filter(isTruthy),
280
+ // sort from root elements to nested, so that parent is always present
281
+ // Import to preserve the order from the source
282
+ sortByFqnHierarchically,
283
+ reduce(
284
+ (acc, el) => {
285
+ const parent = parentFqn(el.id)
286
+ if (parent && isNullish(acc[parent])) {
287
+ logWarnError(`No parent found for deployment element ${el.id}`)
288
+ return acc
289
+ }
290
+ acc[el.id] = el
291
+ return acc
292
+ },
293
+ {} as c4.ParsedLikeC4Model['deployments']['elements']
294
+ )
295
+ )
296
+
297
+ function toDeploymentRelation(doc: LangiumDocument) {
298
+ return ({
299
+ astPath,
300
+ source,
301
+ target,
302
+ kind,
303
+ links: unresolvedLinks,
304
+ id,
305
+ ...model
306
+ }: ParsedAstDeploymentRelation): c4.DeploymentRelation | null => {
307
+ if (isNullish(deploymentElements[source.id]) || isNullish(deploymentElements[target.id])) {
308
+ logger.warn(
309
+ `Invalid deployment relation ${id} at ${doc.uri.path} ${astPath}, source: ${source.id}(${!!deploymentElements[
310
+ source.id
311
+ ]}), target: ${target.id}(${!!deploymentElements[target.id]})`
312
+ )
313
+ return null
314
+ }
315
+ const links = unresolvedLinks ? resolveLinks(doc, unresolvedLinks) : null
316
+
317
+ if (isNonNullish(kind) && kind in c4Specification.relationships) {
318
+ return {
319
+ ...c4Specification.relationships[kind],
320
+ ...model,
321
+ ...(links && { links }),
322
+ source,
323
+ target,
324
+ kind,
325
+ id
326
+ } satisfies c4.DeploymentRelation
327
+ }
328
+ return {
329
+ ...links && { links },
330
+ ...model,
331
+ source,
332
+ target,
333
+ id
334
+ } satisfies c4.DeploymentRelation
335
+ }
336
+ }
337
+
338
+ const deploymentRelations = pipe(
339
+ docs,
340
+ flatMap(d => map(d.c4DeploymentRelations, toDeploymentRelation(d))),
341
+ filter(isTruthy),
342
+ reduce(
343
+ (acc, el) => {
344
+ if (isDefined(acc[el.id])) {
345
+ logWarnError(`Duplicate deployment relation ${el.id}`)
346
+ return acc
347
+ }
348
+ acc[el.id] = el
349
+ return acc
350
+ },
351
+ {} as c4.ParsedLikeC4Model['deployments']['relations']
352
+ )
353
+ )
354
+
228
355
  function toC4View(doc: LangiumDocument) {
229
356
  const docUri = doc.uri.toString()
230
357
  return (parsedAstView: ParsedAstView): c4.LikeC4View => {
@@ -273,7 +400,7 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
273
400
  if (!parsedViews.some(v => v.id === 'index')) {
274
401
  parsedViews.unshift({
275
402
  __: 'element',
276
- id: 'index' as ViewID,
403
+ id: 'index' as ViewId,
277
404
  title: 'Landscape view',
278
405
  description: null,
279
406
  tags: null,
@@ -301,12 +428,17 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
301
428
  specification: {
302
429
  tags: Array.from(c4Specification.tags),
303
430
  elements: c4Specification.elements,
304
- relationships: c4Specification.relationships
431
+ relationships: c4Specification.relationships,
432
+ deployments: c4Specification.deployments
305
433
  },
306
434
  elements,
307
435
  relations,
308
436
  globals,
309
- views
437
+ views,
438
+ deployments: {
439
+ elements: deploymentElements,
440
+ relations: deploymentRelations
441
+ }
310
442
  }
311
443
  }
312
444
 
@@ -335,8 +467,8 @@ export class LikeC4ModelBuilder {
335
467
  let parsed = [] as URI[]
336
468
  try {
337
469
  logger.debug(`[ModelBuilder] onValidated (${docs.length} docs)`)
338
- for (const doc of parser.parse(docs)) {
339
- parsed.push(doc.uri)
470
+ for (const doc of docs) {
471
+ parsed.push(parser.parse(doc).uri)
340
472
  }
341
473
  } catch (e) {
342
474
  logWarnError(e)
@@ -356,13 +488,13 @@ export class LikeC4ModelBuilder {
356
488
  * Otherwise, the model may be incomplete.
357
489
  */
358
490
  public unsafeSyncBuildModel(): c4.ParsedLikeC4Model | null {
491
+ const docs = this.documents()
492
+ if (docs.length === 0) {
493
+ logger.debug('[ModelBuilder] No documents to build model from')
494
+ return null
495
+ }
359
496
  const cache = this.services.WorkspaceCache as WorkspaceCache<string, c4.ParsedLikeC4Model | null>
360
497
  return cache.get(CACHE_KEY_PARSED_MODEL, () => {
361
- const docs = this.documents()
362
- if (docs.length === 0) {
363
- logger.debug('[ModelBuilder] No documents to build model from')
364
- return null
365
- }
366
498
  logger.debug(`[ModelBuilder] buildModel (${docs.length} docs)`)
367
499
  return buildModel(this.services, docs)
368
500
  })
@@ -370,8 +502,9 @@ export class LikeC4ModelBuilder {
370
502
 
371
503
  public async buildModel(cancelToken?: Cancellation.CancellationToken): Promise<c4.ParsedLikeC4Model | null> {
372
504
  const cache = this.services.WorkspaceCache as WorkspaceCache<string, c4.ParsedLikeC4Model | null>
373
- if (cache.has(CACHE_KEY_PARSED_MODEL)) {
374
- return cache.get(CACHE_KEY_PARSED_MODEL)!
505
+ const cached = cache.get(CACHE_KEY_PARSED_MODEL)
506
+ if (cached) {
507
+ return cached
375
508
  }
376
509
  return await this.services.shared.workspace.WorkspaceLock.read(async () => {
377
510
  if (cancelToken) {
@@ -381,7 +514,7 @@ export class LikeC4ModelBuilder {
381
514
  })
382
515
  }
383
516
 
384
- private previousViews: Record<ViewID, c4.ComputedView> = {}
517
+ private previousViews: Record<ViewId, c4.ComputedView> = {}
385
518
 
386
519
  /**
387
520
  * WARNING:
@@ -392,13 +525,10 @@ export class LikeC4ModelBuilder {
392
525
  const cache = this.services.WorkspaceCache as WorkspaceCache<string, c4.ComputedLikeC4Model>
393
526
  const viewsCache = this.services.WorkspaceCache as WorkspaceCache<string, c4.ComputedView | null>
394
527
  return cache.get(CACHE_KEY_COMPUTED_MODEL, () => {
395
- const index = new LikeC4ModelGraph(model)
396
-
528
+ const computeView = mkComputeView(model)
397
529
  const allViews = [] as c4.ComputedView[]
398
530
  for (const view of values(model.views)) {
399
- const result = isElementView(view)
400
- ? computeView(view, index)
401
- : computeDynamicView(view, index)
531
+ const result = computeView(view)
402
532
  if (!result.isSuccess) {
403
533
  logWarnError(result.error)
404
534
  continue
@@ -414,10 +544,15 @@ export class LikeC4ModelBuilder {
414
544
  })
415
545
  this.previousViews = { ...views }
416
546
  return {
417
- specification: model.specification,
418
- elements: model.elements,
419
- relations: model.relations,
420
- globals: model.globals,
547
+ ...structuredClone(
548
+ pick(model, [
549
+ 'specification',
550
+ 'elements',
551
+ 'relations',
552
+ 'globals',
553
+ 'deployments'
554
+ ])
555
+ ),
421
556
  views
422
557
  }
423
558
  })
@@ -443,7 +578,7 @@ export class LikeC4ModelBuilder {
443
578
  }
444
579
 
445
580
  public async computeView(
446
- viewId: ViewID,
581
+ viewId: ViewId,
447
582
  cancelToken?: Cancellation.CancellationToken
448
583
  ): Promise<c4.ComputedView | null> {
449
584
  const cache = this.services.WorkspaceCache as WorkspaceCache<string, c4.ComputedView | null>
@@ -462,10 +597,7 @@ export class LikeC4ModelBuilder {
462
597
  logger.warn(`[ModelBuilder] Cannot find view ${viewId}`)
463
598
  return null
464
599
  }
465
- const index = new LikeC4ModelGraph(model)
466
- const result = isElementView(view)
467
- ? computeView(view, index)
468
- : computeDynamicView(view, index)
600
+ const result = mkComputeView(model)(view)
469
601
  if (!result.isSuccess) {
470
602
  logError(result.error)
471
603
  return null
@@ -490,8 +622,11 @@ export class LikeC4ModelBuilder {
490
622
  }
491
623
 
492
624
  const previous = this.previousViews[viewId]
493
- computedView = previous && eq(computedView, previous) ? previous : computedView
494
- this.previousViews[viewId] = computedView
625
+ if (previous && eq(computedView, previous)) {
626
+ computedView = previous
627
+ } else {
628
+ this.previousViews[viewId] = computedView
629
+ }
495
630
 
496
631
  return computedView
497
632
  })