@likec4/language-server 1.18.0 → 1.19.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 (220) hide show
  1. package/dist/LikeC4FileSystem.d.ts +13 -0
  2. package/dist/LikeC4FileSystem.js +27 -0
  3. package/dist/Rpc.d.ts +9 -0
  4. package/dist/Rpc.js +126 -0
  5. package/dist/ast.d.ts +200 -0
  6. package/dist/ast.js +276 -0
  7. package/dist/browser.d.ts +6 -20
  8. package/dist/browser.js +13 -0
  9. package/dist/formatting/LikeC4Formatter.d.ts +27 -0
  10. package/dist/formatting/LikeC4Formatter.js +261 -0
  11. package/dist/formatting/utils.d.ts +6 -0
  12. package/dist/formatting/utils.js +15 -0
  13. package/dist/generated/ast.d.ts +1242 -0
  14. package/dist/generated/ast.js +1945 -0
  15. package/dist/generated/grammar.d.ts +6 -0
  16. package/dist/generated/grammar.js +3 -0
  17. package/dist/generated/module.d.ts +9 -0
  18. package/dist/generated/module.js +23 -0
  19. package/dist/generated-lib/icons.d.ts +1 -0
  20. package/dist/{likec4lib.mjs → generated-lib/icons.js} +1 -6
  21. package/dist/index.d.ts +8 -31
  22. package/dist/index.js +13 -0
  23. package/dist/like-c4.langium +845 -0
  24. package/dist/likec4lib.d.ts +4 -6
  25. package/dist/likec4lib.js +4 -0
  26. package/dist/logger.d.ts +7 -0
  27. package/dist/logger.js +73 -0
  28. package/dist/lsp/CodeLensProvider.d.ts +9 -0
  29. package/dist/lsp/CodeLensProvider.js +40 -0
  30. package/dist/lsp/CompletionProvider.d.ts +6 -0
  31. package/dist/lsp/CompletionProvider.js +135 -0
  32. package/dist/lsp/DocumentHighlightProvider.d.ts +9 -0
  33. package/dist/lsp/DocumentHighlightProvider.js +10 -0
  34. package/dist/lsp/DocumentLinkProvider.d.ts +11 -0
  35. package/dist/lsp/DocumentLinkProvider.js +49 -0
  36. package/dist/lsp/DocumentSymbolProvider.d.ts +23 -0
  37. package/dist/lsp/DocumentSymbolProvider.js +202 -0
  38. package/dist/lsp/HoverProvider.d.ts +10 -0
  39. package/dist/lsp/HoverProvider.js +69 -0
  40. package/dist/lsp/RenameProvider.d.ts +5 -0
  41. package/dist/lsp/RenameProvider.js +6 -0
  42. package/dist/lsp/SemanticTokenProvider.d.ts +7 -0
  43. package/dist/lsp/SemanticTokenProvider.js +297 -0
  44. package/dist/lsp/index.d.ts +7 -0
  45. package/dist/lsp/index.js +7 -0
  46. package/dist/model/deployments-index.d.ts +60 -0
  47. package/dist/model/deployments-index.js +181 -0
  48. package/dist/model/fqn-computation.d.ts +3 -0
  49. package/dist/model/fqn-computation.js +72 -0
  50. package/dist/model/fqn-index.d.ts +25 -0
  51. package/dist/model/fqn-index.js +96 -0
  52. package/dist/model/index.d.ts +6 -0
  53. package/dist/model/index.js +6 -0
  54. package/dist/model/model-builder.d.ts +32 -0
  55. package/dist/model/model-builder.js +598 -0
  56. package/dist/model/model-locator.d.ts +23 -0
  57. package/dist/model/model-locator.js +126 -0
  58. package/dist/model/model-parser-where.d.ts +3 -0
  59. package/dist/model/model-parser-where.js +70 -0
  60. package/dist/model/model-parser.d.ts +292 -0
  61. package/dist/model/model-parser.js +72 -0
  62. package/dist/model/parser/Base.d.ts +28 -0
  63. package/dist/model/parser/Base.js +87 -0
  64. package/dist/model/parser/DeploymentModelParser.d.ts +33 -0
  65. package/dist/model/parser/DeploymentModelParser.js +162 -0
  66. package/dist/model/parser/DeploymentViewParser.d.ts +38 -0
  67. package/dist/model/parser/DeploymentViewParser.js +98 -0
  68. package/dist/model/parser/FqnRefParser.d.ts +29 -0
  69. package/dist/model/parser/FqnRefParser.js +108 -0
  70. package/dist/model/parser/GlobalsParser.d.ts +66 -0
  71. package/dist/model/parser/GlobalsParser.js +80 -0
  72. package/dist/model/parser/ModelParser.d.ts +27 -0
  73. package/dist/model/parser/ModelParser.js +122 -0
  74. package/dist/model/parser/PredicatesParser.d.ts +34 -0
  75. package/dist/model/parser/PredicatesParser.js +272 -0
  76. package/dist/model/parser/SpecificationParser.d.ts +27 -0
  77. package/dist/model/parser/SpecificationParser.js +120 -0
  78. package/dist/model/parser/ViewsParser.d.ts +64 -0
  79. package/dist/model/parser/ViewsParser.js +377 -0
  80. package/dist/model-change/ModelChanges.d.ts +15 -0
  81. package/dist/model-change/ModelChanges.js +89 -0
  82. package/dist/model-change/changeElementStyle.d.ts +16 -0
  83. package/dist/model-change/changeElementStyle.js +136 -0
  84. package/dist/model-change/changeViewLayout.d.ts +12 -0
  85. package/dist/model-change/changeViewLayout.js +32 -0
  86. package/dist/model-change/saveManualLayout.d.ts +11 -0
  87. package/dist/model-change/saveManualLayout.js +27 -0
  88. package/dist/module.d.ts +62 -0
  89. package/dist/module.js +123 -0
  90. package/dist/protocol.d.ts +20 -23
  91. package/dist/protocol.js +14 -0
  92. package/dist/references/index.d.ts +3 -0
  93. package/dist/references/index.js +3 -0
  94. package/dist/references/name-provider.d.ts +9 -0
  95. package/dist/references/name-provider.js +33 -0
  96. package/dist/references/scope-computation.d.ts +20 -0
  97. package/dist/references/scope-computation.js +281 -0
  98. package/dist/references/scope-provider.d.ts +16 -0
  99. package/dist/references/scope-provider.js +165 -0
  100. package/dist/shared/NodeKindProvider.d.ts +15 -0
  101. package/dist/shared/NodeKindProvider.js +108 -0
  102. package/dist/shared/WorkspaceManager.d.ts +18 -0
  103. package/dist/shared/WorkspaceManager.js +36 -0
  104. package/dist/shared/WorkspaceSymbolProvider.d.ts +3 -0
  105. package/dist/shared/WorkspaceSymbolProvider.js +3 -0
  106. package/dist/shared/index.d.ts +3 -0
  107. package/dist/shared/index.js +3 -0
  108. package/dist/test/index.d.ts +1 -0
  109. package/dist/test/index.js +1 -0
  110. package/dist/test/setup.d.ts +1 -0
  111. package/dist/test/setup.js +7 -0
  112. package/dist/test/testServices.d.ts +22 -0
  113. package/dist/test/testServices.js +119 -0
  114. package/dist/utils/elementRef.d.ts +11 -0
  115. package/dist/utils/elementRef.js +15 -0
  116. package/dist/utils/fqnRef.d.ts +8 -0
  117. package/dist/utils/fqnRef.js +46 -0
  118. package/dist/utils/index.d.ts +1 -0
  119. package/dist/utils/index.js +1 -0
  120. package/dist/utils/printDocs.d.ts +2 -0
  121. package/dist/utils/printDocs.js +1 -0
  122. package/dist/utils/stringHash.d.ts +1 -0
  123. package/dist/utils/stringHash.js +5 -0
  124. package/dist/validation/_shared.d.ts +3 -0
  125. package/dist/validation/_shared.js +22 -0
  126. package/dist/validation/deployment-checks.d.ts +6 -0
  127. package/dist/validation/deployment-checks.js +114 -0
  128. package/dist/validation/dynamic-view-rule.d.ts +4 -0
  129. package/dist/validation/dynamic-view-rule.js +16 -0
  130. package/dist/validation/dynamic-view-step.d.ts +4 -0
  131. package/dist/validation/dynamic-view-step.js +33 -0
  132. package/dist/validation/element.d.ts +4 -0
  133. package/dist/validation/element.js +49 -0
  134. package/dist/validation/index.d.ts +15 -0
  135. package/dist/validation/index.js +152 -0
  136. package/dist/validation/property-checks.d.ts +6 -0
  137. package/dist/validation/property-checks.js +38 -0
  138. package/dist/validation/relation.d.ts +5 -0
  139. package/dist/validation/relation.js +56 -0
  140. package/dist/validation/specification.d.ts +11 -0
  141. package/dist/validation/specification.js +136 -0
  142. package/dist/validation/view-predicates/element-with.d.ts +4 -0
  143. package/dist/validation/view-predicates/element-with.js +30 -0
  144. package/dist/validation/view-predicates/expanded-element.d.ts +4 -0
  145. package/dist/validation/view-predicates/expanded-element.js +11 -0
  146. package/dist/validation/view-predicates/expression-v2.d.ts +5 -0
  147. package/dist/validation/view-predicates/expression-v2.js +83 -0
  148. package/dist/validation/view-predicates/incoming.d.ts +4 -0
  149. package/dist/validation/view-predicates/incoming.js +15 -0
  150. package/dist/validation/view-predicates/index.d.ts +6 -0
  151. package/dist/validation/view-predicates/index.js +6 -0
  152. package/dist/validation/view-predicates/outgoing.d.ts +4 -0
  153. package/dist/validation/view-predicates/outgoing.js +15 -0
  154. package/dist/validation/view-predicates/relation-with.d.ts +4 -0
  155. package/dist/validation/view-predicates/relation-with.js +12 -0
  156. package/dist/validation/view.d.ts +4 -0
  157. package/dist/validation/view.js +23 -0
  158. package/dist/view-utils/assignNavigateTo.d.ts +2 -0
  159. package/dist/view-utils/assignNavigateTo.js +25 -0
  160. package/dist/view-utils/index.d.ts +2 -0
  161. package/dist/view-utils/index.js +2 -0
  162. package/dist/view-utils/manual-layout.d.ts +7 -0
  163. package/dist/view-utils/manual-layout.js +99 -0
  164. package/dist/view-utils/resolve-relative-paths.d.ts +2 -0
  165. package/dist/view-utils/resolve-relative-paths.js +78 -0
  166. package/package.json +36 -53
  167. package/src/LikeC4FileSystem.ts +22 -21
  168. package/src/ast.ts +44 -133
  169. package/src/browser.ts +10 -11
  170. package/src/generated/ast.ts +177 -177
  171. package/src/generated/grammar.ts +1 -1
  172. package/src/index.ts +10 -8
  173. package/src/like-c4.langium +37 -34
  174. package/src/logger.ts +34 -55
  175. package/src/lsp/CompletionProvider.ts +4 -4
  176. package/src/lsp/HoverProvider.ts +5 -3
  177. package/src/lsp/SemanticTokenProvider.ts +2 -2
  178. package/src/model/deployments-index.ts +18 -14
  179. package/src/model/model-builder.ts +10 -8
  180. package/src/model/model-parser.ts +62 -1574
  181. package/src/model/parser/Base.ts +107 -0
  182. package/src/model/parser/DeploymentModelParser.ts +192 -0
  183. package/src/model/parser/DeploymentViewParser.ts +116 -0
  184. package/src/model/parser/FqnRefParser.ts +118 -0
  185. package/src/model/parser/GlobalsParser.ts +96 -0
  186. package/src/model/parser/ModelParser.ts +141 -0
  187. package/src/model/parser/PredicatesParser.ts +291 -0
  188. package/src/model/parser/SpecificationParser.ts +133 -0
  189. package/src/model/parser/ViewsParser.ts +428 -0
  190. package/src/module.ts +17 -18
  191. package/src/references/scope-provider.ts +13 -7
  192. package/src/utils/{deploymentRef.ts → fqnRef.ts} +27 -2
  193. package/src/validation/_shared.ts +0 -1
  194. package/src/validation/deployment-checks.ts +49 -62
  195. package/src/validation/index.ts +100 -9
  196. package/src/validation/view-predicates/expression-v2.ts +101 -0
  197. package/src/validation/view-predicates/index.ts +1 -1
  198. package/src/view-utils/assignNavigateTo.ts +1 -1
  199. package/src/view-utils/manual-layout.ts +25 -0
  200. package/dist/browser.cjs +0 -25
  201. package/dist/browser.d.cts +0 -23
  202. package/dist/browser.d.mts +0 -23
  203. package/dist/browser.mjs +0 -20
  204. package/dist/index.cjs +0 -53
  205. package/dist/index.d.cts +0 -34
  206. package/dist/index.d.mts +0 -34
  207. package/dist/index.mjs +0 -46
  208. package/dist/likec4lib.cjs +0 -1546
  209. package/dist/likec4lib.d.cts +0 -6
  210. package/dist/likec4lib.d.mts +0 -6
  211. package/dist/protocol.cjs +0 -25
  212. package/dist/protocol.d.cts +0 -48
  213. package/dist/protocol.d.mts +0 -48
  214. package/dist/protocol.mjs +0 -17
  215. package/dist/shared/language-server.CO_nmHiL.cjs +0 -7689
  216. package/dist/shared/language-server.Da6ey08o.d.cts +0 -1619
  217. package/dist/shared/language-server.De7S3e5Z.d.ts +0 -1619
  218. package/dist/shared/language-server.Dj4iDjtB.d.mts +0 -1619
  219. package/dist/shared/language-server.oO_9JoAG.mjs +0 -7666
  220. package/src/validation/view-predicates/deployments.ts +0 -56
@@ -1,1594 +1,82 @@
1
- import type * as c4 from '@likec4/core'
2
- import { type HexColorLiteral, invariant, isNonEmptyArray, nameFromFqn, nonexhaustive, nonNullable } from '@likec4/core'
3
- import type { AstNode, LangiumDocument } from 'langium'
4
- import { AstUtils, CstUtils } from 'langium'
5
- import { filter, first, flatMap, isArray, isDefined, isNonNullish, isTruthy, map, mapToObj, pipe } from 'remeda'
6
- import stripIndent from 'strip-indent'
7
- import type { Writable } from 'type-fest'
8
- import type {
9
- ChecksFromDiagnostics,
10
- FqnIndexedDocument,
11
- ParsedAstDeployment,
12
- ParsedAstDeploymentRelation,
13
- ParsedAstDeploymentView,
14
- ParsedAstDynamicView,
15
- ParsedAstElement,
16
- ParsedAstElementView,
17
- ParsedAstGlobals,
18
- ParsedAstRelation,
19
- ParsedLikeC4LangiumDocument,
20
- ParsedLink
21
- } from '../ast'
22
- import {
23
- ast,
24
- checksFromDiagnostics,
25
- cleanParsedModel,
26
- isFqnIndexedDocument,
27
- parseAstOpacityProperty,
28
- resolveRelationPoints,
29
- streamModel,
30
- toAutoLayout,
31
- toColor,
32
- toElementStyle,
33
- toRelationshipStyleExcludeDefaults,
34
- ViewOps
35
- } from '../ast'
36
- import { type NotationProperty } from '../generated/ast'
37
- import { logger, logWarnError } from '../logger'
1
+ import { invariant } from '@likec4/core'
2
+ import type { LangiumDocument } from 'langium'
3
+ import DefaultWeakMap from 'mnemonist/default-weak-map'
4
+ import { pipe } from 'remeda'
5
+ import type { LikeC4DocumentProps, ParsedLikeC4LangiumDocument } from '../ast'
6
+ import { isFqnIndexedDocument } from '../ast'
38
7
  import type { LikeC4Services } from '../module'
39
- import { stringHash } from '../utils'
40
- import { instanceRef } from '../utils/deploymentRef'
41
- import { elementRef, getFqnElementRef } from '../utils/elementRef'
42
- import { deserializeFromComment, hasManualLayout } from '../view-utils/manual-layout'
43
- import type { FqnIndex } from './fqn-index'
44
- import { parseWhereClause } from './model-parser-where'
45
-
46
- const { getDocument } = AstUtils
8
+ import { BaseParser } from './parser/Base'
9
+ import { DeploymentModelParser } from './parser/DeploymentModelParser'
10
+ import { DeploymentViewParser } from './parser/DeploymentViewParser'
11
+ import { ExpressionV2Parser } from './parser/FqnRefParser'
12
+ import { GlobalsParser } from './parser/GlobalsParser'
13
+ import { ModelParser } from './parser/ModelParser'
14
+ import { PredicatesParser } from './parser/PredicatesParser'
15
+ import { SpecificationParser } from './parser/SpecificationParser'
16
+ import { ViewsParser } from './parser/ViewsParser'
47
17
 
48
18
  export type ModelParsedListener = () => void
49
19
 
50
- function toSingleLine<T extends string | undefined | null>(str: T): T {
51
- return (isNonNullish(str) ? removeIndent(str).split('\n').join(' ') : undefined) as T
52
- }
53
-
54
- function removeIndent<T extends string | undefined | null>(str: T): T {
55
- return (isNonNullish(str) ? stripIndent(str).trim() : undefined) as T
20
+ const DocumentParserFromMixins = pipe(
21
+ BaseParser,
22
+ ExpressionV2Parser,
23
+ ModelParser,
24
+ DeploymentModelParser,
25
+ DeploymentViewParser,
26
+ PredicatesParser,
27
+ SpecificationParser,
28
+ ViewsParser,
29
+ GlobalsParser
30
+ )
31
+
32
+ export class DocumentParser extends DocumentParserFromMixins {
56
33
  }
57
34
 
58
- export type IsValidFn = ChecksFromDiagnostics['isValid']
59
-
60
35
  export class LikeC4ModelParser {
61
- private fqnIndex: FqnIndex
36
+ private cachedParsers = new DefaultWeakMap<LangiumDocument, DocumentParser>((doc: LangiumDocument) =>
37
+ new DocumentParser(this.services, doc as ParsedLikeC4LangiumDocument)
38
+ )
39
+
62
40
  constructor(private services: LikeC4Services) {
63
- this.fqnIndex = services.likec4.FqnIndex
64
- logger.debug(`[ModelParser] Created`)
65
41
  }
66
42
 
67
43
  parse(doc: LangiumDocument): ParsedLikeC4LangiumDocument {
68
44
  invariant(isFqnIndexedDocument(doc), `Not a FqnIndexedDocument: ${doc.uri.toString(true)}`)
69
45
  try {
70
- return this.parseLikeC4Document(doc)
46
+ const props: Required<Omit<LikeC4DocumentProps, 'c4fqnIndex' | 'diagnostics'>> = {
47
+ c4Specification: {
48
+ tags: new Set(),
49
+ elements: {},
50
+ relationships: {},
51
+ colors: {},
52
+ deployments: {}
53
+ },
54
+ c4Elements: [],
55
+ c4Relations: [],
56
+ c4Deployments: [],
57
+ c4DeploymentRelations: [],
58
+ c4Globals: {
59
+ predicates: {},
60
+ dynamicPredicates: {},
61
+ styles: {}
62
+ },
63
+ c4Views: []
64
+ }
65
+ doc = Object.assign(doc, props)
66
+ const parser = this.cachedParsers.get(doc)
67
+ parser.parseSpecification()
68
+ parser.parseModel()
69
+ parser.parseGlobals()
70
+ parser.parseDeployment()
71
+ parser.parseViews()
72
+ return parser.doc
71
73
  } catch (cause) {
72
74
  throw new Error(`Error parsing document ${doc.uri.toString()}`, { cause })
73
75
  }
74
76
  }
75
77
 
76
- protected parseLikeC4Document(_doc: FqnIndexedDocument) {
77
- const doc = cleanParsedModel(_doc)
78
- const { isValid } = checksFromDiagnostics(doc)
79
- this.parseSpecification(doc, isValid)
80
- this.parseModel(doc, isValid)
81
- this.parseGlobal(doc, isValid)
82
- this.parseDeployment(doc, isValid)
83
- this.parseViews(doc, isValid)
84
- return doc
85
- }
86
-
87
- private parseSpecification(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
88
- const {
89
- parseResult: {
90
- value: {
91
- specifications
92
- }
93
- },
94
- c4Specification
95
- } = doc
96
-
97
- const element_specs = specifications.flatMap(s => s.elements.filter(isValid))
98
- for (const { kind, props } of element_specs) {
99
- try {
100
- const kindName = kind.name as c4.ElementKind
101
- if (!isTruthy(kindName)) {
102
- continue
103
- }
104
- if (kindName in c4Specification.elements) {
105
- logger.warn(`Element kind "${kindName}" is already defined`)
106
- continue
107
- }
108
- const style = props.find(ast.isElementStyleProperty)
109
- const bodyProps = pipe(
110
- props.filter(ast.isSpecificationElementStringProperty) ?? [],
111
- filter(p => isValid(p) && isNonNullish(p.value)),
112
- mapToObj(p => [p.key, removeIndent(p.value)] satisfies [string, string])
113
- )
114
- c4Specification.elements[kindName] = {
115
- ...bodyProps,
116
- style: {
117
- ...toElementStyle(style?.props, isValid)
118
- }
119
- }
120
- } catch (e) {
121
- logWarnError(e)
122
- }
123
- }
124
-
125
- const relations_specs = specifications.flatMap(s => s.relationships.filter(isValid))
126
- for (const { kind, props } of relations_specs) {
127
- try {
128
- const kindName = kind.name as c4.RelationshipKind
129
- if (!isTruthy(kindName)) {
130
- continue
131
- }
132
- if (kindName in c4Specification.relationships) {
133
- logger.warn(`Relationship kind "${kindName}" is already defined`)
134
- continue
135
- }
136
- const bodyProps = pipe(
137
- props.filter(ast.isSpecificationRelationshipStringProperty) ?? [],
138
- filter(p => isValid(p) && isNonNullish(p.value)),
139
- mapToObj(p => [p.key, removeIndent(p.value)] satisfies [string, string])
140
- )
141
- c4Specification.relationships[kindName] = {
142
- ...bodyProps,
143
- ...toRelationshipStyleExcludeDefaults(props, isValid)
144
- }
145
- } catch (e) {
146
- logWarnError(e)
147
- }
148
- }
149
-
150
- const tags_specs = specifications.flatMap(s => s.tags.filter(isValid))
151
- for (const tagSpec of tags_specs) {
152
- const tag = tagSpec.tag.name as c4.Tag
153
- if (isTruthy(tag)) {
154
- c4Specification.tags.add(tag)
155
- }
156
- }
157
-
158
- const deploymentNodes_specs = specifications.flatMap(s => s.deploymentNodes.filter(isValid))
159
- for (const deploymentNode of deploymentNodes_specs) {
160
- try {
161
- Object.assign(c4Specification.deployments, this.parseSpecificationDeploymentNodeKind(deploymentNode, isValid))
162
- } catch (e) {
163
- logWarnError(e)
164
- }
165
- }
166
-
167
- const colors_specs = specifications.flatMap(s => s.colors.filter(isValid))
168
- for (const { name, color } of colors_specs) {
169
- try {
170
- const colorName = name.name as c4.CustomColor
171
- if (colorName in c4Specification.colors) {
172
- logger.warn(`Custom color "${colorName}" is already defined`)
173
- continue
174
- }
175
-
176
- c4Specification.colors[colorName] = {
177
- color: color as HexColorLiteral
178
- }
179
- } catch (e) {
180
- logWarnError(e)
181
- }
182
- }
183
- }
184
-
185
- private parseSpecificationDeploymentNodeKind(
186
- { kind, props }: ast.SpecificationDeploymentNodeKind,
187
- isValid: IsValidFn
188
- ): { [key: c4.DeploymentNodeKind]: c4.DeploymentNodeKindSpecification } {
189
- const kindName = kind.name as c4.DeploymentNodeKind
190
- if (!isTruthy(kindName)) {
191
- throw new Error('DeploymentNodeKind name is not resolved')
192
- }
193
-
194
- const style = props.find(ast.isElementStyleProperty)
195
- const bodyProps = pipe(
196
- props.filter(ast.isSpecificationElementStringProperty) ?? [],
197
- filter(p => isValid(p) && isNonNullish(p.value)),
198
- mapToObj(p => [p.key, removeIndent(p.value)] satisfies [string, string])
199
- )
200
- return {
201
- [kindName]: {
202
- ...bodyProps,
203
- style: {
204
- ...toElementStyle(style?.props, isValid)
205
- }
206
- }
207
- }
208
- }
209
-
210
- private parseModel(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
211
- for (const el of streamModel(doc, isValid)) {
212
- if (ast.isElement(el)) {
213
- try {
214
- doc.c4Elements.push(this.parseElement(el, isValid))
215
- } catch (e) {
216
- logWarnError(e)
217
- }
218
- continue
219
- }
220
- if (ast.isRelation(el)) {
221
- try {
222
- doc.c4Relations.push(this.parseRelation(el, isValid))
223
- } catch (e) {
224
- logWarnError(e)
225
- }
226
- continue
227
- }
228
- nonexhaustive(el)
229
- }
230
- }
231
-
232
- private parseElement(astNode: ast.Element, isValid: IsValidFn): ParsedAstElement {
233
- const id = this.resolveFqn(astNode)
234
- const kind = nonNullable(astNode.kind.ref, 'Element kind is not resolved').name as c4.ElementKind
235
- const tags = this.convertTags(astNode.body)
236
- const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
237
- const style = toElementStyle(stylePropsAst, isValid)
238
- const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
239
- const astPath = this.getAstNodePath(astNode)
240
-
241
- let [title, description, technology] = astNode.props ?? []
242
-
243
- const bodyProps = pipe(
244
- astNode.body?.props ?? [],
245
- filter(isValid),
246
- filter(ast.isElementStringProperty),
247
- mapToObj(p => [p.key, p.value || undefined])
248
- )
249
-
250
- title = removeIndent(title ?? bodyProps.title)
251
- description = removeIndent(bodyProps.description ?? description)
252
- technology = toSingleLine(bodyProps.technology ?? technology)
253
-
254
- const links = this.convertLinks(astNode.body)
255
-
256
- // Property has higher priority than from style
257
- const iconProp = astNode.body?.props.find(ast.isIconProperty)
258
- if (iconProp && isValid(iconProp)) {
259
- const value = iconProp.libicon?.ref?.name ?? iconProp.value
260
- if (isTruthy(value)) {
261
- style.icon = value as c4.IconUrl
262
- }
263
- }
264
-
265
- return {
266
- id,
267
- kind,
268
- astPath,
269
- title: title ?? astNode.name,
270
- ...(metadata && { metadata }),
271
- ...(tags && { tags }),
272
- ...(links && isNonEmptyArray(links) && { links }),
273
- ...(isTruthy(technology) && { technology }),
274
- ...(isTruthy(description) && { description }),
275
- style
276
- }
277
- }
278
-
279
- private parseRelation(astNode: ast.Relation, isValid: IsValidFn): ParsedAstRelation {
280
- const coupling = resolveRelationPoints(astNode)
281
- const target = this.resolveFqn(coupling.target)
282
- const source = this.resolveFqn(coupling.source)
283
- const tags = this.convertTags(astNode) ?? this.convertTags(astNode.body)
284
- const links = this.convertLinks(astNode.body)
285
- const kind = astNode.kind?.ref?.name as (c4.RelationshipKind | undefined)
286
- const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
287
- const astPath = this.getAstNodePath(astNode)
288
-
289
- const bodyProps = mapToObj(
290
- astNode.body?.props.filter(ast.isRelationStringProperty).filter(p => isNonNullish(p.value)) ?? [],
291
- p => [p.key, p.value]
292
- )
293
-
294
- const navigateTo = pipe(
295
- astNode.body?.props ?? [],
296
- filter(ast.isRelationNavigateToProperty),
297
- map(p => p.value.view.ref?.name),
298
- filter(isTruthy),
299
- first()
300
- )
301
-
302
- const title = removeIndent(astNode.title ?? bodyProps.title) ?? ''
303
- const description = removeIndent(bodyProps.description)
304
- const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology)
305
-
306
- const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty)
307
- const id = stringHash(
308
- astPath,
309
- source,
310
- target
311
- ) as c4.RelationId
312
- return {
313
- id,
314
- astPath,
315
- source,
316
- target,
317
- title,
318
- ...(metadata && { metadata }),
319
- ...(isTruthy(technology) && { technology }),
320
- ...(isTruthy(description) && { description }),
321
- ...(kind && { kind }),
322
- ...(tags && { tags }),
323
- ...(isNonEmptyArray(links) && { links }),
324
- ...toRelationshipStyleExcludeDefaults(styleProp?.props, isValid),
325
- ...(navigateTo && { navigateTo: navigateTo as c4.ViewId })
326
- }
327
- }
328
-
329
- private parseGlobal(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
330
- const { parseResult, c4Globals } = doc
331
-
332
- const globals = parseResult.value.globals.filter(isValid)
333
-
334
- const elRelPredicates = globals.flatMap(r => r.predicates.filter(isValid))
335
- for (const predicate of elRelPredicates) {
336
- try {
337
- const globalPredicateId = predicate.name as c4.GlobalPredicateId
338
- if (!isTruthy(globalPredicateId)) {
339
- continue
340
- }
341
- if (globalPredicateId in c4Globals.predicates) {
342
- logger.warn(`Global predicate named "${globalPredicateId}" is already defined`)
343
- continue
344
- }
345
-
346
- this.parseAndStoreGlobalPredicateGroupOrDynamic(predicate, globalPredicateId, c4Globals, isValid)
347
- } catch (e) {
348
- logWarnError(e)
349
- }
350
- }
351
-
352
- const styles = globals.flatMap(r => r.styles.filter(isValid))
353
- for (const style of styles) {
354
- try {
355
- const globalStyleId = style.id.name as c4.GlobalStyleID
356
- if (!isTruthy(globalStyleId)) {
357
- continue
358
- }
359
- if (globalStyleId in c4Globals.styles) {
360
- logger.warn(`Global style named "${globalStyleId}" is already defined`)
361
- continue
362
- }
363
-
364
- const styles = this.parseGlobalStyleOrGroup(style, isValid)
365
- if (styles.length > 0) {
366
- c4Globals.styles[globalStyleId] = styles as c4.NonEmptyArray<c4.ViewRuleStyle>
367
- }
368
- } catch (e) {
369
- logWarnError(e)
370
- }
371
- }
372
- }
373
-
374
- private parseAndStoreGlobalPredicateGroupOrDynamic(
375
- astRule: ast.GlobalPredicateGroup | ast.GlobalDynamicPredicateGroup,
376
- id: c4.GlobalPredicateId,
377
- c4Globals: ParsedAstGlobals,
378
- isValid: IsValidFn
379
- ) {
380
- if (ast.isGlobalPredicateGroup(astRule)) {
381
- const predicates = this.parseGlobalPredicateGroup(astRule, isValid)
382
- if (predicates.length > 0) {
383
- c4Globals.predicates[id] = predicates as c4.NonEmptyArray<c4.ViewRulePredicate>
384
- }
385
- return
386
- }
387
- if (ast.isGlobalDynamicPredicateGroup(astRule)) {
388
- const predicates = this.parseGlobalDynamicPredicateGroup(astRule, isValid)
389
- if (predicates.length > 0) {
390
- c4Globals.dynamicPredicates[id] = predicates as c4.NonEmptyArray<c4.DynamicViewIncludeRule>
391
- }
392
- return
393
- }
394
- nonexhaustive(astRule)
395
- }
396
-
397
- private parseGlobalPredicateGroup(
398
- astRule: ast.GlobalPredicateGroup,
399
- isValid: IsValidFn
400
- ): c4.ViewRulePredicate[] {
401
- return astRule.predicates.map(p => this.parseViewRulePredicate(p, isValid))
402
- }
403
-
404
- private parseGlobalDynamicPredicateGroup(
405
- astRule: ast.GlobalDynamicPredicateGroup,
406
- isValid: IsValidFn
407
- ): c4.DynamicViewIncludeRule[] {
408
- return astRule.predicates.map(p => this.parseDynamicViewIncludePredicate(p, isValid))
409
- }
410
-
411
- private parseGlobalStyleOrGroup(
412
- astRule: ast.GlobalStyle | ast.GlobalStyleGroup,
413
- isValid: IsValidFn
414
- ): c4.ViewRuleStyle[] {
415
- if (ast.isGlobalStyle(astRule)) {
416
- return [this.parseViewRuleStyle(astRule, isValid)]
417
- }
418
- if (ast.isGlobalStyleGroup(astRule)) {
419
- return astRule.styles.map(s => this.parseViewRuleStyle(s, isValid))
420
- }
421
- nonexhaustive(astRule)
422
- }
423
-
424
- private parseViews(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
425
- for (const viewBlock of doc.parseResult.value.views) {
426
- const localStyles = viewBlock.styles.flatMap(s => {
427
- try {
428
- return isValid(s) ? this.parseViewRuleStyleOrGlobalRef(s, isValid) : []
429
- } catch (e) {
430
- logWarnError(e)
431
- return []
432
- }
433
- })
434
-
435
- for (const view of viewBlock.views) {
436
- try {
437
- if (!isValid(view)) {
438
- continue
439
- }
440
- switch (true) {
441
- case ast.isElementView(view):
442
- doc.c4Views.push(this.parseElementView(view, localStyles, isValid))
443
- break
444
- case ast.isDynamicView(view):
445
- doc.c4Views.push(this.parseDynamicElementView(view, localStyles, isValid))
446
- break
447
- case ast.isDeploymentView(view):
448
- doc.c4Views.push(this.parseDeploymentView(view, isValid))
449
- break
450
- default:
451
- nonexhaustive(view)
452
- }
453
- } catch (e) {
454
- logWarnError(e)
455
- }
456
- }
457
- }
458
- }
459
-
460
- // TODO validate view rules
461
- private parseViewRulePredicate(astNode: ast.ViewRulePredicate, _isValid: IsValidFn): c4.ViewRulePredicate {
462
- const exprs = [] as c4.Expression[]
463
- let predicate = astNode.predicates
464
- while (predicate) {
465
- const { value, prev } = predicate
466
- try {
467
- if (isTruthy(value) && _isValid(value as any)) {
468
- exprs.unshift(this.parsePredicate(value, _isValid))
469
- }
470
- } catch (e) {
471
- logWarnError(e)
472
- }
473
- if (!prev) {
474
- break
475
- }
476
- predicate = prev
477
- }
478
- return ast.isIncludePredicate(astNode) ? { include: exprs } : { exclude: exprs }
479
- }
480
-
481
- private parsePredicate(astNode: ast.Predicate, _isValid: IsValidFn): c4.Expression {
482
- if (ast.isElementPredicate(astNode)) {
483
- return this.parseElementPredicate(astNode, _isValid)
484
- }
485
- if (ast.isRelationPredicate(astNode)) {
486
- return this.parseRelationPredicate(astNode, _isValid)
487
- }
488
- nonexhaustive(astNode)
489
- }
490
-
491
- private parseElementExpressionsIterator(astNode: ast.ElementExpressionsIterator): c4.ElementExpression[] {
492
- const exprs = [] as c4.ElementExpression[]
493
- let iter: ast.ElementExpressionsIterator['prev'] = astNode
494
- while (iter) {
495
- try {
496
- if (iter.value) {
497
- exprs.unshift(this.parseElementExpr(iter.value))
498
- }
499
- } catch (e) {
500
- logWarnError(e)
501
- }
502
- iter = iter.prev
503
- }
504
- return exprs
505
- }
506
-
507
- private parseElementPredicate(astNode: ast.ElementPredicate, _isValid: IsValidFn): c4.ElementPredicateExpression {
508
- if (ast.isElementPredicateWith(astNode)) {
509
- return this.parseElementPredicateWith(astNode, _isValid)
510
- }
511
- if (ast.isElementPredicateWhere(astNode)) {
512
- return this.parseElementPredicateWhere(astNode)
513
- }
514
- if (ast.isElementExpression(astNode)) {
515
- return this.parseElementExpr(astNode)
516
- }
517
- nonexhaustive(astNode)
518
- }
519
-
520
- private parseElementExpr(astNode: ast.ElementExpression): c4.ElementExpression {
521
- if (ast.isWildcardExpression(astNode)) {
522
- return {
523
- wildcard: true
524
- }
525
- }
526
- if (ast.isElementKindExpression(astNode)) {
527
- invariant(astNode.kind?.ref, 'ElementKindExpr kind is not resolved: ' + astNode.$cstNode?.text)
528
- return {
529
- elementKind: astNode.kind.ref.name as c4.ElementKind,
530
- isEqual: astNode.isEqual
531
- }
532
- }
533
- if (ast.isElementTagExpression(astNode)) {
534
- invariant(astNode.tag?.ref, 'ElementTagExpr tag is not resolved: ' + astNode.$cstNode?.text)
535
- let elementTag = astNode.tag.$refText
536
- if (elementTag.startsWith('#')) {
537
- elementTag = elementTag.slice(1)
538
- }
539
- return {
540
- elementTag: elementTag as c4.Tag,
541
- isEqual: astNode.isEqual
542
- }
543
- }
544
- if (ast.isExpandElementExpression(astNode)) {
545
- const elementNode = elementRef(astNode.expand)
546
- invariant(elementNode, 'Element not found ' + astNode.expand.$cstNode?.text)
547
- const expanded = this.resolveFqn(elementNode)
548
- return {
549
- expanded
550
- }
551
- }
552
- if (ast.isElementDescedantsExpression(astNode)) {
553
- const elementNode = elementRef(astNode.parent)
554
- invariant(elementNode, 'Element not found ' + astNode.parent.$cstNode?.text)
555
- const element = this.resolveFqn(elementNode)
556
- return {
557
- element,
558
- isChildren: astNode.suffix === '.*',
559
- isDescendants: astNode.suffix === '.**'
560
- }
561
- }
562
- if (ast.isElementRef(astNode)) {
563
- const elementNode = elementRef(astNode)
564
- invariant(elementNode, 'Element not found ' + astNode.$cstNode?.text)
565
- const element = this.resolveFqn(elementNode)
566
- return {
567
- element
568
- }
569
- }
570
- nonexhaustive(astNode)
571
- }
572
-
573
- private parseElementPredicateWith(
574
- astNode: ast.ElementPredicateWith,
575
- _isValid: IsValidFn
576
- ): c4.CustomElementExpr {
577
- const expr = this.parseElementPredicate(astNode.subject, _isValid)
578
- const props = astNode.custom?.props ?? []
579
- return props.reduce(
580
- (acc, prop) => {
581
- if (!_isValid(prop)) {
582
- return acc
583
- }
584
- if (ast.isNavigateToProperty(prop)) {
585
- const viewId = prop.value.view.$refText
586
- if (isTruthy(viewId)) {
587
- acc.custom.navigateTo = viewId as c4.ViewId
588
- }
589
- return acc
590
- }
591
- if (ast.isElementStringProperty(prop)) {
592
- if (isDefined(prop.value)) {
593
- acc.custom[prop.key] = removeIndent(prop.value) || ''
594
- }
595
- return acc
596
- }
597
- if (ast.isIconProperty(prop)) {
598
- const value = prop.libicon?.ref?.name ?? prop.value
599
- if (isDefined(value)) {
600
- acc.custom[prop.key] = value as c4.IconUrl
601
- }
602
- return acc
603
- }
604
- if (ast.isColorProperty(prop)) {
605
- const value = toColor(prop)
606
- if (isDefined(value)) {
607
- acc.custom[prop.key] = value
608
- }
609
- return acc
610
- }
611
- if (ast.isShapeProperty(prop)) {
612
- if (isDefined(prop.value)) {
613
- acc.custom[prop.key] = prop.value
614
- }
615
- return acc
616
- }
617
- if (ast.isBorderProperty(prop)) {
618
- if (isDefined(prop.value)) {
619
- acc.custom[prop.key] = prop.value
620
- }
621
- return acc
622
- }
623
- if (ast.isOpacityProperty(prop)) {
624
- if (isDefined(prop.value)) {
625
- acc.custom[prop.key] = parseAstOpacityProperty(prop)
626
- }
627
- return acc
628
- }
629
- if (ast.isNotationProperty(prop)) {
630
- if (isTruthy(prop.value)) {
631
- acc.custom[prop.key] = removeIndent(prop.value)
632
- }
633
- return acc
634
- }
635
- nonexhaustive(prop)
636
- },
637
- {
638
- custom: {
639
- expr
640
- }
641
- } as c4.CustomElementExpr
642
- )
643
- }
644
- private parseElementPredicateWhere(
645
- astNode: ast.ElementPredicateWhere
646
- ): c4.ElementWhereExpr {
647
- const expr = this.parseElementExpr(astNode.subject)
648
- return {
649
- where: {
650
- expr,
651
- condition: astNode.where ? parseWhereClause(astNode.where) : {
652
- kind: { neq: '--always-true--' }
653
- }
654
- }
655
- }
656
- }
657
-
658
- private parseRelationPredicate(astNode: ast.RelationPredicate, _isValid: IsValidFn): c4.RelationPredicateExpression {
659
- if (ast.isRelationPredicateWith(astNode)) {
660
- let relation = ast.isRelationPredicateWhere(astNode.subject)
661
- ? this.parseRelationPredicateWhere(astNode.subject)
662
- : this.parseRelationExpr(astNode.subject)
663
-
664
- return this.parseRelationPredicateWith(astNode, relation)
665
- }
666
- if (ast.isRelationPredicateWhere(astNode)) {
667
- return this.parseRelationPredicateWhere(astNode)
668
- }
669
- if (ast.isRelationExpression(astNode)) {
670
- return this.parseRelationExpr(astNode)
671
- }
672
- nonexhaustive(astNode)
673
- }
674
-
675
- private parseRelationPredicateWhere(
676
- astNode: ast.RelationPredicateWhere
677
- ): c4.RelationWhereExpr {
678
- const expr = this.parseRelationExpr(astNode.subject)
679
- return {
680
- where: {
681
- expr,
682
- condition: astNode.where ? parseWhereClause(astNode.where) : {
683
- kind: { neq: '--always-true--' }
684
- }
685
- }
686
- }
687
- }
688
-
689
- private parseRelationPredicateWith(
690
- astNode: ast.RelationPredicateWith,
691
- relation: c4.RelationExpression | c4.RelationWhereExpr
692
- ): c4.CustomRelationExpr {
693
- const props = astNode.custom?.props ?? []
694
- return props.reduce(
695
- (acc, prop) => {
696
- if (ast.isRelationStringProperty(prop) || ast.isNotationProperty(prop) || ast.isNotesProperty(prop)) {
697
- if (isDefined(prop.value)) {
698
- acc.customRelation[prop.key] = removeIndent(prop.value) ?? ''
699
- }
700
- return acc
701
- }
702
- if (ast.isArrowProperty(prop)) {
703
- if (isTruthy(prop.value)) {
704
- acc.customRelation[prop.key] = prop.value
705
- }
706
- return acc
707
- }
708
- if (ast.isColorProperty(prop)) {
709
- const value = toColor(prop)
710
- if (isTruthy(value)) {
711
- acc.customRelation[prop.key] = value
712
- }
713
- return acc
714
- }
715
- if (ast.isLineProperty(prop)) {
716
- if (isTruthy(prop.value)) {
717
- acc.customRelation[prop.key] = prop.value
718
- }
719
- return acc
720
- }
721
- if (ast.isRelationNavigateToProperty(prop)) {
722
- const viewId = prop.value.view.ref?.name
723
- if (isTruthy(viewId)) {
724
- acc.customRelation.navigateTo = viewId as c4.ViewId
725
- }
726
- return acc
727
- }
728
- nonexhaustive(prop)
729
- },
730
- {
731
- customRelation: {
732
- relation
733
- }
734
- } as c4.CustomRelationExpr
735
- )
736
- }
737
-
738
- private parseRelationExpr(astNode: ast.RelationExpression): c4.RelationExpression {
739
- if (ast.isDirectedRelationExpression(astNode)) {
740
- return {
741
- source: this.parseElementExpr(astNode.source.from),
742
- target: this.parseElementExpr(astNode.target),
743
- isBidirectional: astNode.source.isBidirectional
744
- }
745
- }
746
- if (ast.isInOutRelationExpression(astNode)) {
747
- return {
748
- inout: this.parseElementExpr(astNode.inout.to)
749
- }
750
- }
751
- if (ast.isOutgoingRelationExpression(astNode)) {
752
- return {
753
- outgoing: this.parseElementExpr(astNode.from)
754
- }
755
- }
756
- if (ast.isIncomingRelationExpression(astNode)) {
757
- return {
758
- incoming: this.parseElementExpr(astNode.to)
759
- }
760
- }
761
- nonexhaustive(astNode)
762
- }
763
-
764
- private parseViewRule(astRule: ast.ViewRule, isValid: IsValidFn): c4.ViewRule {
765
- if (ast.isViewRulePredicate(astRule)) {
766
- return this.parseViewRulePredicate(astRule, isValid)
767
- }
768
- if (ast.isViewRuleGlobalPredicateRef(astRule)) {
769
- return this.parseViewRuleGlobalPredicateRef(astRule, isValid)
770
- }
771
- if (ast.isViewRuleStyleOrGlobalRef(astRule)) {
772
- return this.parseViewRuleStyleOrGlobalRef(astRule, isValid)
773
- }
774
- if (ast.isViewRuleAutoLayout(astRule)) {
775
- return toAutoLayout(astRule)
776
- }
777
- if (ast.isViewRuleGroup(astRule)) {
778
- return this.parseViewRuleGroup(astRule, isValid)
779
- }
780
- nonexhaustive(astRule)
781
- }
782
-
783
- private parseViewRuleGlobalPredicateRef(
784
- astRule: ast.ViewRuleGlobalPredicateRef | ast.DynamicViewGlobalPredicateRef,
785
- _isValid: IsValidFn
786
- ): c4.ViewRuleGlobalPredicateRef {
787
- return {
788
- predicateId: astRule.predicate.$refText as c4.GlobalPredicateId
789
- }
790
- }
791
-
792
- private parseViewRuleStyleOrGlobalRef(
793
- astRule: ast.ViewRuleStyleOrGlobalRef,
794
- isValid: IsValidFn
795
- ): c4.ViewRuleStyleOrGlobalRef {
796
- if (ast.isViewRuleStyle(astRule)) {
797
- return this.parseViewRuleStyle(astRule, isValid)
798
- }
799
- if (ast.isViewRuleGlobalStyle(astRule)) {
800
- return this.parseViewRuleGlobalStyle(astRule, isValid)
801
- }
802
- nonexhaustive(astRule)
803
- }
804
-
805
- private parseViewRuleStyle(astRule: ast.ViewRuleStyle | ast.GlobalStyle, isValid: IsValidFn): c4.ViewRuleStyle {
806
- const styleProps = astRule.props.filter(ast.isStyleProperty)
807
- const targets = astRule.target
808
- const notation = astRule.props.find(ast.isNotationProperty)
809
- return this.parseRuleStyle(styleProps, targets, isValid, notation)
810
- }
811
-
812
- private parseViewRuleGroup(astNode: ast.ViewRuleGroup, _isValid: IsValidFn): c4.ViewRuleGroup {
813
- const groupRules = [] as c4.ViewRuleGroup['groupRules']
814
- for (const rule of astNode.groupRules) {
815
- try {
816
- if (!_isValid(rule)) {
817
- continue
818
- }
819
- if (ast.isViewRulePredicate(rule)) {
820
- groupRules.push(this.parseViewRulePredicate(rule, _isValid))
821
- continue
822
- }
823
- if (ast.isViewRuleGroup(rule)) {
824
- groupRules.push(this.parseViewRuleGroup(rule, _isValid))
825
- continue
826
- }
827
- nonexhaustive(rule)
828
- } catch (e) {
829
- logWarnError(e)
830
- }
831
- }
832
- return {
833
- title: toSingleLine(astNode.title) ?? null,
834
- groupRules,
835
- ...toElementStyle(astNode.props, _isValid)
836
- }
837
- }
838
-
839
- private parseRuleStyle(
840
- styleProperties: ast.StyleProperty[],
841
- elementExpressionsIterator: ast.ElementExpressionsIterator,
842
- isValid: IsValidFn,
843
- notationProperty?: NotationProperty
844
- ): c4.ViewRuleStyle {
845
- const styleProps = toElementStyle(styleProperties, isValid)
846
- const notation = removeIndent(notationProperty?.value)
847
- const targets = this.parseElementExpressionsIterator(elementExpressionsIterator)
848
- return {
849
- targets,
850
- ...(notation && { notation }),
851
- style: {
852
- ...styleProps
853
- }
854
- }
855
- }
856
-
857
- private parseViewRuleGlobalStyle(astRule: ast.ViewRuleGlobalStyle, _isValid: IsValidFn): c4.ViewRuleGlobalStyle {
858
- return {
859
- styleId: astRule.style.$refText as c4.GlobalStyleID
860
- }
861
- }
862
-
863
- private parseViewManualLayout(node: ast.LikeC4View): c4.ViewManualLayout | undefined {
864
- const commentNode = CstUtils.findCommentNode(node.$cstNode, ['BLOCK_COMMENT'])
865
- if (!commentNode || !hasManualLayout(commentNode.text)) {
866
- return undefined
867
- }
868
- try {
869
- return deserializeFromComment(commentNode.text)
870
- } catch (e) {
871
- const doc = getDocument(node)
872
- logger.warn(e)
873
- logger.warn(
874
- `Ignoring manual layout of "${node.name ?? 'unnamed'}" at ${doc.uri.fsPath}:${
875
- 1 + (commentNode.range.start.line || 0)
876
- }`
877
- )
878
- return undefined
879
- }
880
- }
881
-
882
- private parseDynamicParallelSteps(node: ast.DynamicViewParallelSteps): c4.DynamicViewParallelSteps {
883
- return {
884
- __parallel: node.steps.map(step => this.parseDynamicStep(step))
885
- }
886
- }
887
-
888
- private parseDynamicStep(node: ast.DynamicViewStep): c4.DynamicViewStep {
889
- const sourceEl = elementRef(node.source)
890
- if (!sourceEl) {
891
- throw new Error('Invalid reference to source')
892
- }
893
- const targetEl = elementRef(node.target)
894
- if (!targetEl) {
895
- throw new Error('Invalid reference to target')
896
- }
897
- let source = this.resolveFqn(sourceEl)
898
- let target = this.resolveFqn(targetEl)
899
- const title = removeIndent(node.title) ?? null
900
-
901
- let step: Writable<c4.DynamicViewStep> = {
902
- source,
903
- target,
904
- title
905
- }
906
- if (node.isBackward) {
907
- step = {
908
- source: target,
909
- target: source,
910
- title,
911
- isBackward: true
912
- }
913
- }
914
- if (!isArray(node.custom?.props)) {
915
- return step
916
- }
917
- for (const prop of node.custom.props) {
918
- try {
919
- switch (true) {
920
- case ast.isRelationNavigateToProperty(prop): {
921
- const viewId = prop.value.view.ref?.name
922
- if (isTruthy(viewId)) {
923
- step.navigateTo = viewId as c4.ViewId
924
- }
925
- break
926
- }
927
- case ast.isRelationStringProperty(prop):
928
- case ast.isNotationProperty(prop):
929
- case ast.isNotesProperty(prop): {
930
- if (isDefined(prop.value)) {
931
- step[prop.key] = removeIndent(prop.value) ?? ''
932
- }
933
- break
934
- }
935
- case ast.isArrowProperty(prop): {
936
- if (isDefined(prop.value)) {
937
- step[prop.key] = prop.value
938
- }
939
- break
940
- }
941
- case ast.isColorProperty(prop): {
942
- const value = toColor(prop)
943
- if (isDefined(value)) {
944
- step[prop.key] = value
945
- }
946
- break
947
- }
948
- case ast.isLineProperty(prop): {
949
- if (isDefined(prop.value)) {
950
- step[prop.key] = prop.value
951
- }
952
- break
953
- }
954
- default:
955
- nonexhaustive(prop)
956
- }
957
- }
958
- catch (e) {
959
- logWarnError(e)
960
- }
961
- }
962
- return step
963
- }
964
-
965
- private parseElementView(
966
- astNode: ast.ElementView,
967
- additionalStyles: c4.ViewRuleStyleOrGlobalRef[],
968
- isValid: IsValidFn
969
- ): ParsedAstElementView {
970
- const body = astNode.body
971
- invariant(body, 'ElementView body is not defined')
972
- const astPath = this.getAstNodePath(astNode)
973
-
974
- let viewOf = null as c4.Fqn | null
975
- if ('viewOf' in astNode) {
976
- const viewOfEl = elementRef(astNode.viewOf)
977
- const _viewOf = viewOfEl && this.resolveFqn(viewOfEl)
978
- if (!_viewOf) {
979
- logger.warn('viewOf is not resolved: ' + astNode.$cstNode?.text)
980
- } else {
981
- viewOf = _viewOf
982
- }
983
- }
984
-
985
- let id = astNode.name
986
- if (!id) {
987
- id = 'view_' + stringHash(
988
- getDocument(astNode).uri.toString(),
989
- astPath,
990
- viewOf ?? ''
991
- ) as c4.ViewId
992
- }
993
-
994
- const title = toSingleLine(body.props.find(p => p.key === 'title')?.value) ?? null
995
- const description = removeIndent(body.props.find(p => p.key === 'description')?.value) ?? null
996
-
997
- const tags = this.convertTags(body)
998
- const links = this.convertLinks(body)
999
-
1000
- const manualLayout = this.parseViewManualLayout(astNode)
1001
-
1002
- const view: ParsedAstElementView = {
1003
- __: 'element',
1004
- id: id as c4.ViewId,
1005
- astPath,
1006
- title,
1007
- description,
1008
- tags,
1009
- links: isNonEmptyArray(links) ? links : null,
1010
- rules: [
1011
- ...additionalStyles,
1012
- ...body.rules.flatMap(n => {
1013
- try {
1014
- return isValid(n) ? this.parseViewRule(n, isValid) : []
1015
- } catch (e) {
1016
- logWarnError(e)
1017
- return []
1018
- }
1019
- })
1020
- ],
1021
- ...(viewOf && { viewOf }),
1022
- ...(manualLayout && { manualLayout })
1023
- }
1024
- ViewOps.writeId(astNode, view.id)
1025
-
1026
- if ('extends' in astNode) {
1027
- const extendsView = astNode.extends.view.ref
1028
- invariant(extendsView?.name, 'view extends is not resolved: ' + astNode.$cstNode?.text)
1029
- return Object.assign(view, {
1030
- extends: extendsView.name as c4.ViewId
1031
- })
1032
- }
1033
-
1034
- return view
1035
- }
1036
-
1037
- private parseDynamicElementView(
1038
- astNode: ast.DynamicView,
1039
- additionalStyles: c4.ViewRuleStyleOrGlobalRef[],
1040
- isValid: IsValidFn
1041
- ): ParsedAstDynamicView {
1042
- const body = astNode.body
1043
- invariant(body, 'DynamicElementView body is not defined')
1044
- // only valid props
1045
- const props = body.props.filter(isValid)
1046
- const astPath = this.getAstNodePath(astNode)
1047
-
1048
- let id = astNode.name
1049
- if (!id) {
1050
- id = 'dynamic_' + stringHash(
1051
- getDocument(astNode).uri.toString(),
1052
- astPath
1053
- ) as c4.ViewId
1054
- }
1055
-
1056
- const title = toSingleLine(props.find(p => p.key === 'title')?.value) ?? null
1057
- const description = removeIndent(props.find(p => p.key === 'description')?.value) ?? null
1058
-
1059
- const tags = this.convertTags(body)
1060
- const links = this.convertLinks(body)
1061
-
1062
- ViewOps.writeId(astNode, id as c4.ViewId)
1063
-
1064
- const manualLayout = this.parseViewManualLayout(astNode)
1065
-
1066
- return {
1067
- __: 'dynamic',
1068
- id: id as c4.ViewId,
1069
- astPath,
1070
- title,
1071
- description,
1072
- tags,
1073
- links: isNonEmptyArray(links) ? links : null,
1074
- rules: [
1075
- ...additionalStyles,
1076
- ...body.rules.flatMap(n => {
1077
- try {
1078
- return isValid(n) ? this.parseDynamicViewRule(n, isValid) : []
1079
- } catch (e) {
1080
- logWarnError(e)
1081
- return []
1082
- }
1083
- }, [] as Array<c4.DynamicViewRule>)
1084
- ],
1085
- steps: body.steps.reduce((acc, n) => {
1086
- try {
1087
- if (isValid(n)) {
1088
- if (ast.isDynamicViewParallelSteps(n)) {
1089
- acc.push(this.parseDynamicParallelSteps(n))
1090
- } else {
1091
- acc.push(this.parseDynamicStep(n))
1092
- }
1093
- }
1094
- } catch (e) {
1095
- logWarnError(e)
1096
- }
1097
- return acc
1098
- }, [] as c4.DynamicViewStepOrParallel[]),
1099
- ...(manualLayout && { manualLayout })
1100
- }
1101
- }
1102
-
1103
- private parseDynamicViewRule(astRule: ast.DynamicViewRule, isValid: IsValidFn): c4.DynamicViewRule {
1104
- if (ast.isDynamicViewIncludePredicate(astRule)) {
1105
- return this.parseDynamicViewIncludePredicate(astRule, isValid)
1106
- }
1107
- if (ast.isDynamicViewGlobalPredicateRef(astRule)) {
1108
- return this.parseViewRuleGlobalPredicateRef(astRule, isValid)
1109
- }
1110
- if (ast.isViewRuleStyleOrGlobalRef(astRule)) {
1111
- return this.parseViewRuleStyleOrGlobalRef(astRule, isValid)
1112
- }
1113
- if (ast.isViewRuleAutoLayout(astRule)) {
1114
- return toAutoLayout(astRule)
1115
- }
1116
- nonexhaustive(astRule)
1117
- }
1118
-
1119
- private parseDynamicViewIncludePredicate(
1120
- astRule: ast.DynamicViewIncludePredicate,
1121
- isValid: IsValidFn
1122
- ): c4.DynamicViewIncludeRule {
1123
- const include = [] as c4.ElementPredicateExpression[]
1124
- let iter: ast.DynamicViewPredicateIterator | undefined = astRule.predicates
1125
- while (iter) {
1126
- try {
1127
- if (isNonNullish(iter.value) && isValid(iter.value as any)) {
1128
- const c4expr = this.parseElementPredicate(iter.value, isValid)
1129
- include.unshift(c4expr)
1130
- }
1131
- } catch (e) {
1132
- logWarnError(e)
1133
- }
1134
- iter = iter.prev
1135
- }
1136
- return { include }
1137
- }
1138
-
1139
- private parseDeployment(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
1140
- type TraversePair = ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentRelation
1141
- const traverseStack: TraversePair[] = doc.parseResult.value.deployments.flatMap(d => d.elements)
1142
-
1143
- let next: TraversePair | undefined
1144
- while ((next = traverseStack.shift())) {
1145
- if (ast.isDeploymentRelation(next)) {
1146
- doc.c4DeploymentRelations.push(this.parseDeploymentRelation(next, isValid))
1147
- continue
1148
- }
1149
- if (!isValid(next)) {
1150
- continue
1151
- }
1152
- try {
1153
- switch (true) {
1154
- case ast.isDeployedInstance(next):
1155
- doc.c4Deployments.push(this.parseDeployedInstance(next, isValid))
1156
- break
1157
- case ast.isDeploymentNode(next): {
1158
- doc.c4Deployments.push(this.parseDeploymentNode(next, isValid))
1159
- if (next.body && next.body.elements.length > 0) {
1160
- traverseStack.push(...next.body.elements)
1161
- }
1162
- break
1163
- }
1164
- default:
1165
- nonexhaustive(next)
1166
- }
1167
- } catch (e) {
1168
- logWarnError(e)
1169
- }
1170
- }
1171
- }
1172
-
1173
- public parseDeploymentNode(
1174
- astNode: ast.DeploymentNode,
1175
- isValid: IsValidFn
1176
- ): ParsedAstDeployment.Node {
1177
- const id = this.resolveFqn(astNode)
1178
- const kind = nonNullable(astNode.kind.ref, 'DeploymentKind not resolved').name as c4.DeploymentNodeKind
1179
- const tags = this.convertTags(astNode.body)
1180
- const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
1181
- const style = toElementStyle(stylePropsAst, isValid)
1182
- const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
1183
-
1184
- const bodyProps = pipe(
1185
- astNode.body?.props ?? [],
1186
- filter(isValid),
1187
- filter(ast.isElementStringProperty),
1188
- mapToObj(p => [p.key, p.value || undefined])
1189
- )
1190
-
1191
- const title = removeIndent(astNode.title ?? bodyProps.title)
1192
- const description = removeIndent(bodyProps.description)
1193
- const technology = toSingleLine(bodyProps.technology)
1194
-
1195
- const links = this.convertLinks(astNode.body)
1196
-
1197
- // Property has higher priority than from style
1198
- const iconProp = astNode.body?.props.find(ast.isIconProperty)
1199
- if (iconProp && isValid(iconProp)) {
1200
- const value = iconProp.libicon?.ref?.name ?? iconProp.value
1201
- if (isTruthy(value)) {
1202
- style.icon = value as c4.IconUrl
1203
- }
1204
- }
1205
-
1206
- return {
1207
- id,
1208
- kind,
1209
- title: title ?? nameFromFqn(id),
1210
- ...(metadata && { metadata }),
1211
- ...(tags && { tags }),
1212
- ...(links && isNonEmptyArray(links) && { links }),
1213
- ...(isTruthy(technology) && { technology }),
1214
- ...(isTruthy(description) && { description }),
1215
- style
1216
- }
1217
- }
1218
-
1219
- public parseDeployedInstance(
1220
- astNode: ast.DeployedInstance,
1221
- isValid: IsValidFn
1222
- ): ParsedAstDeployment.Instance {
1223
- const id = this.resolveFqn(astNode)
1224
- const element = this.resolveFqn(nonNullable(elementRef(astNode.element), 'DeployedInstance element not found'))
1225
-
1226
- const tags = this.convertTags(astNode.body)
1227
- const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
1228
- const style = toElementStyle(stylePropsAst, isValid)
1229
- const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
1230
-
1231
- const bodyProps = pipe(
1232
- astNode.body?.props ?? [],
1233
- filter(isValid),
1234
- filter(ast.isElementStringProperty),
1235
- mapToObj(p => [p.key, p.value || undefined])
1236
- )
1237
-
1238
- const title = removeIndent(astNode.title ?? bodyProps.title)
1239
- const description = removeIndent(bodyProps.description)
1240
- const technology = toSingleLine(bodyProps.technology)
1241
-
1242
- const links = this.convertLinks(astNode.body)
1243
-
1244
- // Property has higher priority than from style
1245
- const iconProp = astNode.body?.props.find(ast.isIconProperty)
1246
- if (iconProp && isValid(iconProp)) {
1247
- const value = iconProp.libicon?.ref?.name ?? iconProp.value
1248
- if (isTruthy(value)) {
1249
- style.icon = value as c4.IconUrl
1250
- }
1251
- }
1252
-
1253
- return {
1254
- id,
1255
- element,
1256
- ...(metadata && { metadata }),
1257
- ...(title && { title }),
1258
- ...(tags && { tags }),
1259
- ...(links && isNonEmptyArray(links) && { links }),
1260
- ...(isTruthy(technology) && { technology }),
1261
- ...(isTruthy(description) && { description }),
1262
- style
1263
- }
1264
- }
1265
-
1266
- public parseDeploymentRelation(
1267
- astNode: ast.DeploymentRelation,
1268
- isValid: IsValidFn
1269
- ): ParsedAstDeploymentRelation {
1270
- const astPath = this.getAstNodePath(astNode)
1271
- const source = this.parseDeploymentDef(astNode.source)
1272
- const target = this.parseDeploymentDef(astNode.target)
1273
-
1274
- const tags = this.convertTags(astNode) ?? this.convertTags(astNode.body)
1275
- const links = this.convertLinks(astNode.body)
1276
- const kind = astNode.kind?.ref?.name as (c4.RelationshipKind | undefined)
1277
- const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
1278
-
1279
- const bodyProps = mapToObj(
1280
- astNode.body?.props.filter(ast.isRelationStringProperty) ?? [],
1281
- p => [p.key, p.value as string | undefined]
1282
- )
1283
-
1284
- const navigateTo = pipe(
1285
- astNode.body?.props ?? [],
1286
- filter(ast.isRelationNavigateToProperty),
1287
- map(p => p.value.view.ref?.name),
1288
- filter(isTruthy),
1289
- first()
1290
- )
1291
-
1292
- const title = removeIndent(astNode.title ?? bodyProps.title)
1293
- const description = removeIndent(bodyProps.description)
1294
- const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology)
1295
-
1296
- const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty)
1297
-
1298
- const id = stringHash(
1299
- 'deployment',
1300
- astPath,
1301
- source.id,
1302
- target.id
1303
- ) as c4.RelationId
1304
-
1305
- return {
1306
- id,
1307
- source,
1308
- target,
1309
- ...title && { title },
1310
- ...(metadata && { metadata }),
1311
- ...(isTruthy(technology) && { technology }),
1312
- ...(isTruthy(description) && { description }),
1313
- ...(kind && { kind }),
1314
- ...(tags && { tags }),
1315
- ...(isNonEmptyArray(links) && { links }),
1316
- ...toRelationshipStyleExcludeDefaults(styleProp?.props, isValid),
1317
- ...(navigateTo && { navigateTo: navigateTo as c4.ViewId }),
1318
- astPath
1319
- }
1320
- }
1321
-
1322
- private parseDeploymentView(
1323
- astNode: ast.DeploymentView,
1324
- isValid: IsValidFn
1325
- ): ParsedAstDeploymentView {
1326
- const body = astNode.body
1327
- invariant(body, 'DynamicElementView body is not defined')
1328
- // only valid props
1329
- const props = body.props.filter(isValid)
1330
- const astPath = this.getAstNodePath(astNode)
1331
-
1332
- let id = astNode.name
1333
- if (!id) {
1334
- id = 'deployment_' + stringHash(
1335
- getDocument(astNode).uri.toString(),
1336
- astPath
1337
- ) as c4.ViewId
1338
- }
1339
-
1340
- const title = toSingleLine(props.find(p => p.key === 'title')?.value) ?? null
1341
- const description = removeIndent(props.find(p => p.key === 'description')?.value) ?? null
1342
-
1343
- const tags = this.convertTags(body)
1344
- const links = this.convertLinks(body)
1345
-
1346
- ViewOps.writeId(astNode, id as c4.ViewId)
1347
-
1348
- const manualLayout = this.parseViewManualLayout(astNode)
1349
-
1350
- return {
1351
- __: 'deployment',
1352
- id: id as c4.ViewId,
1353
- astPath,
1354
- title,
1355
- description,
1356
- tags,
1357
- links: isNonEmptyArray(links) ? links : null,
1358
- rules: body.rules.flatMap(n => {
1359
- try {
1360
- return isValid(n) ? this.parseDeploymentViewRule(n, isValid) : []
1361
- } catch (e) {
1362
- logWarnError(e)
1363
- return []
1364
- }
1365
- }),
1366
- ...(manualLayout && { manualLayout })
1367
- }
1368
- }
1369
-
1370
- private parseDeploymentViewRule(astRule: ast.DeploymentViewRule, isValid: IsValidFn): c4.DeploymentViewRule {
1371
- if (ast.isDeploymentViewRulePredicate(astRule)) {
1372
- return this.parseDeploymentViewRulePredicate(astRule, isValid)
1373
- }
1374
- if (ast.isViewRuleAutoLayout(astRule)) {
1375
- return toAutoLayout(astRule)
1376
- }
1377
- if (ast.isDeploymentViewRuleStyle(astRule)) {
1378
- return this.parseDeploymentViewRuleStyle(astRule, isValid)
1379
- }
1380
- nonexhaustive(astRule)
1381
- }
1382
-
1383
- private parseDeploymentViewRulePredicate(
1384
- astRule: ast.DeploymentViewRulePredicate,
1385
- isValid: IsValidFn
1386
- ): c4.DeploymentViewRulePredicate {
1387
- const exprs = [] as c4.DeploymentExpression[]
1388
- let iterator: ast.DeploymentViewRulePredicateExpression | undefined = astRule.expr
1389
- while (iterator) {
1390
- try {
1391
- const expr = iterator.value
1392
- if (isNonNullish(expr) && isValid(expr)) {
1393
- switch (true) {
1394
- case ast.isDeploymentElementExpression(expr):
1395
- exprs.unshift(this.parseDeploymentElementExpression(expr))
1396
- break
1397
- case ast.isDeploymentRelationExpression(expr):
1398
- exprs.unshift(this.parseDeploymentRelationExpression(expr))
1399
- break
1400
- default:
1401
- nonexhaustive(expr)
1402
- }
1403
- }
1404
- } catch (e) {
1405
- logWarnError(e)
1406
- }
1407
- iterator = iterator.prev
1408
- }
1409
- return astRule.isInclude ? { include: exprs } : { exclude: exprs }
1410
- }
1411
-
1412
- private parseDeploymentElementExpression(astNode: ast.DeploymentElementExpression): c4.DeploymentExpression {
1413
- if (ast.isWildcardExpression(astNode)) {
1414
- return {
1415
- wildcard: true
1416
- }
1417
- }
1418
- if (ast.isDeploymentRefExpression(astNode)) {
1419
- const ref = this.parseDeploymentDef(astNode.ref)
1420
- switch (true) {
1421
- case astNode.selector === '._':
1422
- return {
1423
- ref,
1424
- selector: 'expanded'
1425
- }
1426
- case astNode.selector === '.**':
1427
- return {
1428
- ref,
1429
- selector: 'descendants'
1430
- }
1431
- case astNode.selector === '.*':
1432
- return {
1433
- ref,
1434
- selector: 'children'
1435
- }
1436
- default:
1437
- return { ref }
1438
- }
1439
- }
1440
- nonexhaustive(astNode)
1441
- }
1442
-
1443
- private parseDeploymentRelationExpression(
1444
- astNode: ast.DeploymentRelationExpression
1445
- ): c4.DeploymentRelationExpression {
1446
- if (ast.isDirectedDeploymentRelationExpression(astNode)) {
1447
- return {
1448
- source: this.parseDeploymentElementExpression(astNode.source.from),
1449
- target: this.parseDeploymentElementExpression(astNode.target),
1450
- isBidirectional: astNode.source.isBidirectional
1451
- }
1452
- }
1453
- if (ast.isInOutDeploymentRelationExpression(astNode)) {
1454
- return {
1455
- inout: this.parseDeploymentElementExpression(astNode.inout.to)
1456
- }
1457
- }
1458
- if (ast.isOutgoingDeploymentRelationExpression(astNode)) {
1459
- return {
1460
- outgoing: this.parseDeploymentElementExpression(astNode.from)
1461
- }
1462
- }
1463
- if (ast.isIncomingDeploymentRelationExpression(astNode)) {
1464
- return {
1465
- incoming: this.parseDeploymentElementExpression(astNode.to)
1466
- }
1467
- }
1468
- nonexhaustive(astNode)
1469
- }
1470
-
1471
- private parseDeploymentExpressionIterator(
1472
- astNode: ast.DeploymentExpressionIterator,
1473
- isValid: IsValidFn
1474
- ): c4.DeploymentExpression[] {
1475
- const exprs = [] as c4.DeploymentExpression[]
1476
- let iter: ast.DeploymentExpressionIterator['prev'] = astNode
1477
- while (iter) {
1478
- try {
1479
- if (isNonNullish(iter.value) && isValid(iter.value)) {
1480
- exprs.unshift(this.parseDeploymentElementExpression(iter.value))
1481
- }
1482
- } catch (e) {
1483
- logWarnError(e)
1484
- }
1485
- iter = iter.prev
1486
- }
1487
- return exprs
1488
- }
1489
-
1490
- private parseDeploymentDef(astNode: ast.DeploymentRef): c4.DeploymentRef {
1491
- const refValue = nonNullable(
1492
- astNode.value.ref,
1493
- `Deployment ref is empty ${astNode.$cstNode?.range.start.line}:${astNode.$cstNode?.range.start.character}`
1494
- )
1495
- if (ast.isDeploymentNode(refValue)) {
1496
- return {
1497
- id: this.resolveFqn(refValue)
1498
- }
1499
- }
1500
- const deployedInstanceAst = nonNullable(instanceRef(astNode), 'Instance ref not found')
1501
- const id = this.resolveFqn(deployedInstanceAst)
1502
-
1503
- if (ast.isElement(refValue)) {
1504
- const element = this.resolveFqn(refValue)
1505
- return {
1506
- id,
1507
- element
1508
- }
1509
- }
1510
-
1511
- // To ensure with compiler we processed all types
1512
- invariant(ast.isDeployedInstance(refValue), 'Invalid deployment ref: ' + this.getAstNodePath(astNode))
1513
- return {
1514
- id
1515
- }
1516
- }
1517
-
1518
- protected parseDeploymentViewRuleStyle(
1519
- astRule: ast.DeploymentViewRuleStyle,
1520
- isValid: IsValidFn
1521
- ): c4.DeploymentViewRuleStyle {
1522
- const styleProps = astRule.props.filter(ast.isStyleProperty)
1523
- const notationProperty = astRule.props.find(ast.isNotationProperty)
1524
- const notation = removeIndent(notationProperty?.value)
1525
- const targets = this.parseDeploymentExpressionIterator(astRule.target, isValid)
1526
- return {
1527
- targets,
1528
- ...(notation && { notation }),
1529
- style: {
1530
- ...toElementStyle(styleProps, isValid)
1531
- }
1532
- }
1533
- }
1534
-
1535
- protected resolveFqn(node: ast.FqnReferenceable): c4.Fqn {
1536
- if (ast.isDeploymentElement(node)) {
1537
- return this.services.likec4.DeploymentsIndex.getFqnName(node)
1538
- }
1539
- if (ast.isExtendElement(node)) {
1540
- return getFqnElementRef(node.element)
1541
- }
1542
- const fqn = this.fqnIndex.getFqn(node)
1543
- invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`)
1544
- return fqn
1545
- }
1546
-
1547
- private getAstNodePath(node: AstNode) {
1548
- return this.services.workspace.AstNodeLocator.getAstNodePath(node)
1549
- }
1550
-
1551
- private getMetadata(metadataAstNode: ast.MetadataProperty | undefined): { [key: string]: string } | undefined {
1552
- return metadataAstNode?.props != null
1553
- ? mapToObj(metadataAstNode.props, (p) => [p.key, removeIndent(p.value)] as [string, string])
1554
- : undefined
1555
- }
1556
-
1557
- private convertTags<E extends { tags?: ast.Tags }>(withTags?: E) {
1558
- let iter = withTags?.tags
1559
- if (!iter) {
1560
- return null
1561
- }
1562
- const tags = [] as c4.Tag[]
1563
- while (iter) {
1564
- try {
1565
- const values = iter.values.map(t => t.ref?.name).filter(isTruthy) as c4.Tag[]
1566
- if (values.length > 0) {
1567
- tags.unshift(...values)
1568
- }
1569
- } catch (e) {
1570
- // ignore
1571
- }
1572
- iter = iter.prev
1573
- }
1574
- return isNonEmptyArray(tags) ? tags : null
1575
- }
1576
-
1577
- private convertLinks(source?: ast.LinkProperty['$container']): ParsedLink[] | undefined {
1578
- if (!source?.props || source.props.length === 0) {
1579
- return undefined
1580
- }
1581
- return pipe(
1582
- source.props,
1583
- filter(ast.isLinkProperty),
1584
- flatMap(p => {
1585
- const url = p.value
1586
- if (isTruthy(url)) {
1587
- const title = isTruthy(p.title) ? toSingleLine(p.title) : undefined
1588
- return title ? { url, title } : { url }
1589
- }
1590
- return []
1591
- })
1592
- )
78
+ forDocument(doc: LangiumDocument): DocumentParser {
79
+ invariant(isFqnIndexedDocument(doc), `Not a FqnIndexedDocument: ${doc.uri.toString(true)}`)
80
+ return this.cachedParsers.get(doc)
1593
81
  }
1594
82
  }