@likec4/language-server 1.17.1 → 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 (268) hide show
  1. package/contrib/likec4.tmLanguage.json +1 -1
  2. package/dist/LikeC4FileSystem.d.ts +13 -0
  3. package/dist/LikeC4FileSystem.js +27 -0
  4. package/dist/Rpc.d.ts +9 -0
  5. package/dist/Rpc.js +126 -0
  6. package/dist/ast.d.ts +200 -0
  7. package/dist/ast.js +276 -0
  8. package/dist/browser.d.ts +6 -20
  9. package/dist/browser.js +13 -0
  10. package/dist/formatting/LikeC4Formatter.d.ts +27 -0
  11. package/dist/formatting/LikeC4Formatter.js +261 -0
  12. package/dist/formatting/utils.d.ts +6 -0
  13. package/dist/formatting/utils.js +15 -0
  14. package/dist/generated/ast.d.ts +1242 -0
  15. package/dist/generated/ast.js +1945 -0
  16. package/dist/generated/grammar.d.ts +6 -0
  17. package/dist/generated/grammar.js +3 -0
  18. package/dist/generated/module.d.ts +9 -0
  19. package/dist/generated/module.js +23 -0
  20. package/dist/generated-lib/icons.d.ts +1 -0
  21. package/dist/{likec4lib.mjs → generated-lib/icons.js} +1 -6
  22. package/dist/index.d.ts +8 -31
  23. package/dist/index.js +13 -0
  24. package/dist/like-c4.langium +845 -0
  25. package/dist/likec4lib.d.ts +4 -6
  26. package/dist/likec4lib.js +4 -0
  27. package/dist/logger.d.ts +7 -0
  28. package/dist/logger.js +73 -0
  29. package/dist/lsp/CodeLensProvider.d.ts +9 -0
  30. package/dist/lsp/CodeLensProvider.js +40 -0
  31. package/dist/lsp/CompletionProvider.d.ts +6 -0
  32. package/dist/lsp/CompletionProvider.js +135 -0
  33. package/dist/lsp/DocumentHighlightProvider.d.ts +9 -0
  34. package/dist/lsp/DocumentHighlightProvider.js +10 -0
  35. package/dist/lsp/DocumentLinkProvider.d.ts +11 -0
  36. package/dist/lsp/DocumentLinkProvider.js +49 -0
  37. package/dist/lsp/DocumentSymbolProvider.d.ts +23 -0
  38. package/dist/lsp/DocumentSymbolProvider.js +202 -0
  39. package/dist/lsp/HoverProvider.d.ts +10 -0
  40. package/dist/lsp/HoverProvider.js +69 -0
  41. package/dist/lsp/RenameProvider.d.ts +5 -0
  42. package/dist/lsp/RenameProvider.js +6 -0
  43. package/dist/lsp/SemanticTokenProvider.d.ts +7 -0
  44. package/dist/lsp/SemanticTokenProvider.js +297 -0
  45. package/dist/lsp/index.d.ts +7 -0
  46. package/dist/lsp/index.js +7 -0
  47. package/dist/model/deployments-index.d.ts +60 -0
  48. package/dist/model/deployments-index.js +181 -0
  49. package/dist/model/fqn-computation.d.ts +3 -0
  50. package/dist/model/fqn-computation.js +72 -0
  51. package/dist/model/fqn-index.d.ts +25 -0
  52. package/dist/model/fqn-index.js +96 -0
  53. package/dist/model/index.d.ts +6 -0
  54. package/dist/model/index.js +6 -0
  55. package/dist/model/model-builder.d.ts +32 -0
  56. package/dist/model/model-builder.js +598 -0
  57. package/dist/model/model-locator.d.ts +23 -0
  58. package/dist/model/model-locator.js +126 -0
  59. package/dist/model/model-parser-where.d.ts +3 -0
  60. package/dist/model/model-parser-where.js +70 -0
  61. package/dist/model/model-parser.d.ts +292 -0
  62. package/dist/model/model-parser.js +72 -0
  63. package/dist/model/parser/Base.d.ts +28 -0
  64. package/dist/model/parser/Base.js +87 -0
  65. package/dist/model/parser/DeploymentModelParser.d.ts +33 -0
  66. package/dist/model/parser/DeploymentModelParser.js +162 -0
  67. package/dist/model/parser/DeploymentViewParser.d.ts +38 -0
  68. package/dist/model/parser/DeploymentViewParser.js +98 -0
  69. package/dist/model/parser/FqnRefParser.d.ts +29 -0
  70. package/dist/model/parser/FqnRefParser.js +108 -0
  71. package/dist/model/parser/GlobalsParser.d.ts +66 -0
  72. package/dist/model/parser/GlobalsParser.js +80 -0
  73. package/dist/model/parser/ModelParser.d.ts +27 -0
  74. package/dist/model/parser/ModelParser.js +122 -0
  75. package/dist/model/parser/PredicatesParser.d.ts +34 -0
  76. package/dist/model/parser/PredicatesParser.js +272 -0
  77. package/dist/model/parser/SpecificationParser.d.ts +27 -0
  78. package/dist/model/parser/SpecificationParser.js +120 -0
  79. package/dist/model/parser/ViewsParser.d.ts +64 -0
  80. package/dist/model/parser/ViewsParser.js +377 -0
  81. package/dist/model-change/ModelChanges.d.ts +15 -0
  82. package/dist/model-change/ModelChanges.js +89 -0
  83. package/dist/model-change/changeElementStyle.d.ts +16 -0
  84. package/dist/model-change/changeElementStyle.js +136 -0
  85. package/dist/model-change/changeViewLayout.d.ts +12 -0
  86. package/dist/model-change/changeViewLayout.js +32 -0
  87. package/dist/model-change/saveManualLayout.d.ts +11 -0
  88. package/dist/model-change/saveManualLayout.js +27 -0
  89. package/dist/module.d.ts +62 -0
  90. package/dist/module.js +123 -0
  91. package/dist/protocol.d.ts +27 -27
  92. package/dist/protocol.js +14 -0
  93. package/dist/references/index.d.ts +3 -0
  94. package/dist/references/index.js +3 -0
  95. package/dist/references/name-provider.d.ts +9 -0
  96. package/dist/references/name-provider.js +33 -0
  97. package/dist/references/scope-computation.d.ts +20 -0
  98. package/dist/references/scope-computation.js +281 -0
  99. package/dist/references/scope-provider.d.ts +16 -0
  100. package/dist/references/scope-provider.js +165 -0
  101. package/dist/shared/NodeKindProvider.d.ts +15 -0
  102. package/dist/shared/NodeKindProvider.js +108 -0
  103. package/dist/shared/WorkspaceManager.d.ts +18 -0
  104. package/dist/shared/WorkspaceManager.js +36 -0
  105. package/dist/shared/WorkspaceSymbolProvider.d.ts +3 -0
  106. package/dist/shared/WorkspaceSymbolProvider.js +3 -0
  107. package/dist/shared/index.d.ts +3 -0
  108. package/dist/shared/index.js +3 -0
  109. package/dist/test/index.d.ts +1 -0
  110. package/dist/test/index.js +1 -0
  111. package/dist/test/setup.d.ts +1 -0
  112. package/dist/test/setup.js +7 -0
  113. package/dist/test/testServices.d.ts +22 -0
  114. package/dist/test/testServices.js +119 -0
  115. package/dist/utils/elementRef.d.ts +11 -0
  116. package/dist/utils/elementRef.js +15 -0
  117. package/dist/utils/fqnRef.d.ts +8 -0
  118. package/dist/utils/fqnRef.js +46 -0
  119. package/dist/utils/index.d.ts +1 -0
  120. package/dist/utils/index.js +1 -0
  121. package/dist/utils/printDocs.d.ts +2 -0
  122. package/dist/utils/printDocs.js +1 -0
  123. package/dist/utils/stringHash.d.ts +1 -0
  124. package/dist/utils/stringHash.js +5 -0
  125. package/dist/validation/_shared.d.ts +3 -0
  126. package/dist/validation/_shared.js +22 -0
  127. package/dist/validation/deployment-checks.d.ts +6 -0
  128. package/dist/validation/deployment-checks.js +114 -0
  129. package/dist/validation/dynamic-view-rule.d.ts +4 -0
  130. package/dist/validation/dynamic-view-rule.js +16 -0
  131. package/dist/validation/dynamic-view-step.d.ts +4 -0
  132. package/dist/validation/dynamic-view-step.js +33 -0
  133. package/dist/validation/element.d.ts +4 -0
  134. package/dist/validation/element.js +49 -0
  135. package/dist/validation/index.d.ts +15 -0
  136. package/dist/validation/index.js +152 -0
  137. package/dist/validation/property-checks.d.ts +6 -0
  138. package/dist/validation/property-checks.js +38 -0
  139. package/dist/validation/relation.d.ts +5 -0
  140. package/dist/validation/relation.js +56 -0
  141. package/dist/validation/specification.d.ts +11 -0
  142. package/dist/validation/specification.js +136 -0
  143. package/dist/validation/view-predicates/element-with.d.ts +4 -0
  144. package/dist/validation/view-predicates/element-with.js +30 -0
  145. package/dist/validation/view-predicates/expanded-element.d.ts +4 -0
  146. package/dist/validation/view-predicates/expanded-element.js +11 -0
  147. package/dist/validation/view-predicates/expression-v2.d.ts +5 -0
  148. package/dist/validation/view-predicates/expression-v2.js +83 -0
  149. package/dist/validation/view-predicates/incoming.d.ts +4 -0
  150. package/dist/validation/view-predicates/incoming.js +15 -0
  151. package/dist/validation/view-predicates/index.d.ts +6 -0
  152. package/dist/validation/view-predicates/index.js +6 -0
  153. package/dist/validation/view-predicates/outgoing.d.ts +4 -0
  154. package/dist/validation/view-predicates/outgoing.js +15 -0
  155. package/dist/validation/view-predicates/relation-with.d.ts +4 -0
  156. package/dist/validation/view-predicates/relation-with.js +12 -0
  157. package/dist/validation/view.d.ts +4 -0
  158. package/dist/validation/view.js +23 -0
  159. package/dist/view-utils/assignNavigateTo.d.ts +2 -0
  160. package/dist/view-utils/assignNavigateTo.js +25 -0
  161. package/dist/view-utils/index.d.ts +2 -0
  162. package/dist/view-utils/index.js +2 -0
  163. package/dist/view-utils/manual-layout.d.ts +7 -0
  164. package/dist/view-utils/manual-layout.js +99 -0
  165. package/dist/view-utils/resolve-relative-paths.d.ts +2 -0
  166. package/dist/view-utils/resolve-relative-paths.js +78 -0
  167. package/package.json +42 -73
  168. package/src/LikeC4FileSystem.ts +22 -21
  169. package/src/Rpc.ts +6 -3
  170. package/src/ast.ts +136 -172
  171. package/src/browser.ts +10 -11
  172. package/src/generated/ast.ts +656 -40
  173. package/src/generated/grammar.ts +1 -1
  174. package/src/index.ts +11 -8
  175. package/src/like-c4.langium +173 -22
  176. package/src/logger.ts +41 -57
  177. package/src/lsp/CodeLensProvider.ts +0 -1
  178. package/src/lsp/CompletionProvider.ts +20 -5
  179. package/src/lsp/DocumentSymbolProvider.ts +5 -2
  180. package/src/lsp/HoverProvider.ts +37 -3
  181. package/src/lsp/SemanticTokenProvider.ts +58 -32
  182. package/src/model/deployments-index.ts +222 -0
  183. package/src/model/fqn-computation.ts +1 -1
  184. package/src/model/fqn-index.ts +0 -1
  185. package/src/model/index.ts +1 -0
  186. package/src/model/model-builder.ts +176 -39
  187. package/src/model/model-locator.ts +36 -7
  188. package/src/model/model-parser.ts +69 -1119
  189. package/src/model/parser/Base.ts +107 -0
  190. package/src/model/parser/DeploymentModelParser.ts +192 -0
  191. package/src/model/parser/DeploymentViewParser.ts +116 -0
  192. package/src/model/parser/FqnRefParser.ts +118 -0
  193. package/src/model/parser/GlobalsParser.ts +96 -0
  194. package/src/model/parser/ModelParser.ts +141 -0
  195. package/src/model/parser/PredicatesParser.ts +291 -0
  196. package/src/model/parser/SpecificationParser.ts +133 -0
  197. package/src/model/parser/ViewsParser.ts +428 -0
  198. package/src/model-change/changeViewLayout.ts +2 -2
  199. package/src/module.ts +26 -21
  200. package/src/protocol.ts +10 -6
  201. package/src/references/index.ts +1 -0
  202. package/src/references/name-provider.ts +37 -0
  203. package/src/references/scope-computation.ts +130 -21
  204. package/src/references/scope-provider.ts +68 -35
  205. package/src/shared/NodeKindProvider.ts +15 -3
  206. package/src/{elementRef.ts → utils/elementRef.ts} +1 -1
  207. package/src/utils/fqnRef.ts +56 -0
  208. package/src/utils/stringHash.ts +2 -2
  209. package/src/validation/_shared.ts +6 -5
  210. package/src/validation/deployment-checks.ts +131 -0
  211. package/src/validation/dynamic-view-step.ts +1 -1
  212. package/src/validation/index.ts +104 -6
  213. package/src/validation/relation.ts +1 -1
  214. package/src/validation/view-predicates/expression-v2.ts +101 -0
  215. package/src/validation/view-predicates/index.ts +1 -0
  216. package/src/view-utils/assignNavigateTo.ts +6 -5
  217. package/src/view-utils/index.ts +0 -1
  218. package/src/view-utils/manual-layout.ts +25 -0
  219. package/dist/browser.cjs +0 -25
  220. package/dist/browser.d.cts +0 -23
  221. package/dist/browser.d.mts +0 -23
  222. package/dist/browser.mjs +0 -20
  223. package/dist/index.cjs +0 -53
  224. package/dist/index.d.cts +0 -34
  225. package/dist/index.d.mts +0 -34
  226. package/dist/index.mjs +0 -46
  227. package/dist/likec4lib.cjs +0 -1546
  228. package/dist/likec4lib.d.cts +0 -6
  229. package/dist/likec4lib.d.mts +0 -6
  230. package/dist/model-graph/index.cjs +0 -10
  231. package/dist/model-graph/index.d.cts +0 -81
  232. package/dist/model-graph/index.d.mts +0 -81
  233. package/dist/model-graph/index.d.ts +0 -81
  234. package/dist/model-graph/index.mjs +0 -1
  235. package/dist/protocol.cjs +0 -25
  236. package/dist/protocol.d.cts +0 -45
  237. package/dist/protocol.d.mts +0 -45
  238. package/dist/protocol.mjs +0 -17
  239. package/dist/shared/language-server.BIbAD1T-.mjs +0 -6292
  240. package/dist/shared/language-server.BQRvVmE0.d.cts +0 -1303
  241. package/dist/shared/language-server.BysPcTxr.d.ts +0 -1303
  242. package/dist/shared/language-server.D2QdbOJO.cjs +0 -1995
  243. package/dist/shared/language-server.DGrBGmsd.mjs +0 -1981
  244. package/dist/shared/language-server.DKV_FdPN.cjs +0 -6304
  245. package/dist/shared/language-server._wkyPgso.d.mts +0 -1303
  246. package/src/model-graph/LikeC4ModelGraph.ts +0 -338
  247. package/src/model-graph/compute-view/__test__/fixture.ts +0 -630
  248. package/src/model-graph/compute-view/compute.ts +0 -788
  249. package/src/model-graph/compute-view/index.ts +0 -33
  250. package/src/model-graph/compute-view/predicates.ts +0 -509
  251. package/src/model-graph/dynamic-view/__test__/fixture.ts +0 -61
  252. package/src/model-graph/dynamic-view/compute.ts +0 -313
  253. package/src/model-graph/dynamic-view/index.ts +0 -29
  254. package/src/model-graph/index.ts +0 -3
  255. package/src/model-graph/utils/applyCustomElementProperties.ts +0 -65
  256. package/src/model-graph/utils/applyCustomRelationProperties.ts +0 -41
  257. package/src/model-graph/utils/applyViewRuleStyles.ts +0 -49
  258. package/src/model-graph/utils/buildComputeNodes.ts +0 -113
  259. package/src/model-graph/utils/buildElementNotations.ts +0 -63
  260. package/src/model-graph/utils/elementExpressionToPredicate.ts +0 -39
  261. package/src/model-graph/utils/relationExpressionToPredicates.ts +0 -43
  262. package/src/model-graph/utils/sortNodes.ts +0 -105
  263. package/src/model-graph/utils/uniqueTags.test.ts +0 -42
  264. package/src/model-graph/utils/uniqueTags.ts +0 -19
  265. package/src/utils/graphlib.ts +0 -9
  266. package/src/view-utils/resolve-extended-views.ts +0 -66
  267. package/src/view-utils/resolve-global-rules.ts +0 -88
  268. package/src/view-utils/view-hash.ts +0 -27
@@ -0,0 +1,107 @@
1
+ import type * as c4 from '@likec4/core'
2
+ import { invariant, isNonEmptyArray } from '@likec4/core'
3
+ import type { AstNode } from 'langium'
4
+ import { filter, flatMap, isNonNullish, isTruthy, mapToObj, pipe } from 'remeda'
5
+ import stripIndent from 'strip-indent'
6
+ import { type ParsedLikeC4LangiumDocument, type ParsedLink, ast } from '../../ast'
7
+ import type { LikeC4Services } from '../../module'
8
+ import { getFqnElementRef } from '../../utils/elementRef'
9
+ import { type IsValidFn, checksFromDiagnostics } from '../../validation'
10
+
11
+ // the class which this mixin is applied to
12
+ export type GConstructor<T = {}> = new(...args: any[]) => T
13
+
14
+ export function toSingleLine<T extends string | undefined | null>(str: T): T {
15
+ return (isNonNullish(str) ? removeIndent(str).split('\n').join(' ') : undefined) as T
16
+ }
17
+
18
+ export function removeIndent<T extends string | undefined | null>(str: T): T {
19
+ return (isNonNullish(str) ? stripIndent(str).trim() : undefined) as T
20
+ }
21
+
22
+ export type Base = GConstructor<BaseParser>
23
+
24
+ export class BaseParser {
25
+ isValid: IsValidFn
26
+
27
+ constructor(
28
+ public readonly services: LikeC4Services,
29
+ public readonly doc: ParsedLikeC4LangiumDocument,
30
+ ) {
31
+ // do nothing
32
+ this.isValid = checksFromDiagnostics(doc).isValid
33
+ }
34
+
35
+ resolveFqn(node: ast.FqnReferenceable): c4.Fqn {
36
+ if (ast.isDeploymentElement(node)) {
37
+ return this.services.likec4.DeploymentsIndex.getFqn(node)
38
+ }
39
+ if (ast.isExtendElement(node)) {
40
+ return getFqnElementRef(node.element)
41
+ }
42
+ const fqn = this.services.likec4.FqnIndex.getFqn(node)
43
+ invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`)
44
+ return fqn
45
+ }
46
+
47
+ getAstNodePath(node: AstNode) {
48
+ return this.services.workspace.AstNodeLocator.getAstNodePath(node)
49
+ }
50
+
51
+ getMetadata(metadataAstNode: ast.MetadataProperty | undefined): { [key: string]: string } | undefined {
52
+ if (!metadataAstNode || !this.isValid(metadataAstNode)) {
53
+ return undefined
54
+ }
55
+ return mapToObj(metadataAstNode.props, p => [p.key, removeIndent(p.value)] as [string, string])
56
+ }
57
+
58
+ convertTags<E extends { tags?: ast.Tags }>(withTags?: E) {
59
+ return this.parseTags(withTags)
60
+ }
61
+ parseTags<E extends { tags?: ast.Tags }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null {
62
+ let iter = withTags?.tags
63
+ if (!iter) {
64
+ return null
65
+ }
66
+ const tags = [] as c4.Tag[]
67
+ while (iter) {
68
+ try {
69
+ if (this.isValid(iter)) {
70
+ const values = iter.values.map(t => t.ref?.name).filter(isTruthy) as c4.Tag[]
71
+ if (values.length > 0) {
72
+ tags.push(...values)
73
+ }
74
+ }
75
+ } catch (e) {
76
+ // ignore
77
+ }
78
+ iter = iter.prev
79
+ }
80
+ tags.reverse()
81
+ return isNonEmptyArray(tags) ? tags : null
82
+ }
83
+
84
+ convertLinks(source?: ast.LinkProperty['$container']): ParsedLink[] | undefined {
85
+ return this.parseLinks(source)
86
+ }
87
+ parseLinks(source?: ast.LinkProperty['$container']): ParsedLink[] | undefined {
88
+ if (!source?.props || source.props.length === 0) {
89
+ return undefined
90
+ }
91
+ return pipe(
92
+ source.props,
93
+ filter(ast.isLinkProperty),
94
+ flatMap(p => {
95
+ if (!this.isValid(p)) {
96
+ return []
97
+ }
98
+ const url = p.value
99
+ if (isTruthy(url)) {
100
+ const title = isTruthy(p.title) ? toSingleLine(p.title) : undefined
101
+ return title ? { url, title } : { url }
102
+ }
103
+ return []
104
+ }),
105
+ )
106
+ }
107
+ }
@@ -0,0 +1,192 @@
1
+ import type * as c4 from '@likec4/core'
2
+ import { FqnRef, isNonEmptyArray, nameFromFqn, nonexhaustive, nonNullable } from '@likec4/core'
3
+ import { filter, first, isTruthy, map, mapToObj, pipe } from 'remeda'
4
+ import {
5
+ type ParsedAstDeployment,
6
+ type ParsedAstDeploymentRelation,
7
+ ast,
8
+ streamDeploymentModel,
9
+ toElementStyle,
10
+ toRelationshipStyleExcludeDefaults,
11
+ } from '../../ast'
12
+ import { logWarnError } from '../../logger'
13
+ import { elementRef } from '../../utils/elementRef'
14
+ import { stringHash } from '../../utils/stringHash'
15
+ import { removeIndent, toSingleLine } from './Base'
16
+ import type { WithExpressionV2 } from './FqnRefParser'
17
+
18
+ export type WithDeploymentModel = ReturnType<typeof DeploymentModelParser>
19
+
20
+ export function DeploymentModelParser<TBase extends WithExpressionV2>(B: TBase) {
21
+ return class DeploymentModelParser extends B {
22
+ parseDeployment() {
23
+ const doc = this.doc
24
+ for (const el of streamDeploymentModel(doc)) {
25
+ try {
26
+ switch (true) {
27
+ case ast.isDeploymentRelation(el): {
28
+ if (this.isValid(el)) {
29
+ doc.c4DeploymentRelations.push(this.parseDeploymentRelation(el))
30
+ }
31
+ break
32
+ }
33
+ case ast.isDeployedInstance(el):
34
+ doc.c4Deployments.push(this.parseDeployedInstance(el))
35
+ break
36
+ case ast.isDeploymentNode(el): {
37
+ doc.c4Deployments.push(this.parseDeploymentNode(el))
38
+ break
39
+ }
40
+ default:
41
+ nonexhaustive(el)
42
+ }
43
+ } catch (e) {
44
+ logWarnError(e)
45
+ }
46
+ }
47
+ }
48
+
49
+ parseDeploymentNode(astNode: ast.DeploymentNode): ParsedAstDeployment.Node {
50
+ const isValid = this.isValid
51
+ const id = this.resolveFqn(astNode)
52
+ const kind = nonNullable(astNode.kind.ref, 'DeploymentKind not resolved').name as c4.DeploymentNodeKind
53
+ const tags = this.convertTags(astNode.body)
54
+ const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
55
+ const style = toElementStyle(stylePropsAst, isValid)
56
+ const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
57
+
58
+ const bodyProps = pipe(
59
+ astNode.body?.props ?? [],
60
+ filter(isValid),
61
+ filter(ast.isElementStringProperty),
62
+ mapToObj(p => [p.key, p.value || undefined]),
63
+ )
64
+
65
+ const title = removeIndent(astNode.title ?? bodyProps.title)
66
+ const description = removeIndent(bodyProps.description)
67
+ const technology = toSingleLine(bodyProps.technology)
68
+
69
+ const links = this.convertLinks(astNode.body)
70
+
71
+ // Property has higher priority than from style
72
+ const iconProp = astNode.body?.props.find(ast.isIconProperty)
73
+ if (iconProp && isValid(iconProp)) {
74
+ const value = iconProp.libicon?.ref?.name ?? iconProp.value
75
+ if (isTruthy(value)) {
76
+ style.icon = value as c4.IconUrl
77
+ }
78
+ }
79
+
80
+ return {
81
+ id,
82
+ kind,
83
+ title: title ?? nameFromFqn(id),
84
+ ...(metadata && { metadata }),
85
+ ...(tags && { tags }),
86
+ ...(links && isNonEmptyArray(links) && { links }),
87
+ ...(isTruthy(technology) && { technology }),
88
+ ...(isTruthy(description) && { description }),
89
+ style,
90
+ }
91
+ }
92
+
93
+ parseDeployedInstance(astNode: ast.DeployedInstance): ParsedAstDeployment.Instance {
94
+ const isValid = this.isValid
95
+ const id = this.resolveFqn(astNode)
96
+ const element = this.resolveFqn(nonNullable(elementRef(astNode.element), 'DeployedInstance element not found'))
97
+
98
+ const tags = this.convertTags(astNode.body)
99
+ const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
100
+ const style = toElementStyle(stylePropsAst, isValid)
101
+ const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
102
+
103
+ const bodyProps = pipe(
104
+ astNode.body?.props ?? [],
105
+ filter(isValid),
106
+ filter(ast.isElementStringProperty),
107
+ mapToObj(p => [p.key, p.value || undefined]),
108
+ )
109
+
110
+ const title = removeIndent(astNode.title ?? bodyProps.title)
111
+ const description = removeIndent(bodyProps.description)
112
+ const technology = toSingleLine(bodyProps.technology)
113
+
114
+ const links = this.convertLinks(astNode.body)
115
+
116
+ // Property has higher priority than from style
117
+ const iconProp = astNode.body?.props.find(ast.isIconProperty)
118
+ if (iconProp && isValid(iconProp)) {
119
+ const value = iconProp.libicon?.ref?.name ?? iconProp.value
120
+ if (isTruthy(value)) {
121
+ style.icon = value as c4.IconUrl
122
+ }
123
+ }
124
+
125
+ return {
126
+ id,
127
+ element,
128
+ ...(metadata && { metadata }),
129
+ ...(title && { title }),
130
+ ...(tags && { tags }),
131
+ ...(links && isNonEmptyArray(links) && { links }),
132
+ ...(isTruthy(technology) && { technology }),
133
+ ...(isTruthy(description) && { description }),
134
+ style,
135
+ }
136
+ }
137
+
138
+ parseDeploymentRelation(astNode: ast.DeploymentRelation): ParsedAstDeploymentRelation {
139
+ const isValid = this.isValid
140
+ const astPath = this.getAstNodePath(astNode)
141
+ const source = FqnRef.toDeploymentRef(this.parseFqnRef(astNode.source))
142
+ const target = FqnRef.toDeploymentRef(this.parseFqnRef(astNode.target))
143
+
144
+ const tags = this.convertTags(astNode) ?? this.convertTags(astNode.body)
145
+ const links = this.convertLinks(astNode.body)
146
+ const kind = astNode.kind?.ref?.name as (c4.RelationshipKind | undefined)
147
+ const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
148
+
149
+ const bodyProps = mapToObj(
150
+ astNode.body?.props.filter(ast.isRelationStringProperty) ?? [],
151
+ p => [p.key, p.value as string | undefined],
152
+ )
153
+
154
+ const navigateTo = pipe(
155
+ astNode.body?.props ?? [],
156
+ filter(ast.isRelationNavigateToProperty),
157
+ map(p => p.value.view.ref?.name),
158
+ filter(isTruthy),
159
+ first(),
160
+ )
161
+
162
+ const title = removeIndent(astNode.title ?? bodyProps.title)
163
+ const description = removeIndent(bodyProps.description)
164
+ const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology)
165
+
166
+ const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty)
167
+
168
+ const id = stringHash(
169
+ 'deployment',
170
+ astPath,
171
+ source.id,
172
+ target.id,
173
+ ) as c4.RelationId
174
+
175
+ return {
176
+ id,
177
+ source,
178
+ target,
179
+ ...title && { title },
180
+ ...(metadata && { metadata }),
181
+ ...(isTruthy(technology) && { technology }),
182
+ ...(isTruthy(description) && { description }),
183
+ ...(kind && { kind }),
184
+ ...(tags && { tags }),
185
+ ...(isNonEmptyArray(links) && { links }),
186
+ ...toRelationshipStyleExcludeDefaults(styleProp?.props, isValid),
187
+ ...(navigateTo && { navigateTo: navigateTo as c4.ViewId }),
188
+ astPath,
189
+ }
190
+ }
191
+ }
192
+ }
@@ -0,0 +1,116 @@
1
+ import type * as c4 from '@likec4/core'
2
+ import { FqnExpr, invariant, isNonEmptyArray, nonexhaustive } from '@likec4/core'
3
+ import { isNonNullish } from 'remeda'
4
+ import { ast, type ParsedAstDeploymentView, toAutoLayout, toElementStyle, ViewOps } from '../../ast'
5
+ import { logWarnError } from '../../logger'
6
+ import { stringHash } from '../../utils'
7
+ import { parseViewManualLayout } from '../../view-utils/manual-layout'
8
+ import { removeIndent, toSingleLine } from './Base'
9
+ import type { WithDeploymentModel } from './DeploymentModelParser'
10
+ import type { WithExpressionV2 } from './FqnRefParser'
11
+
12
+ export type WithDeploymentView = ReturnType<typeof DeploymentViewParser>
13
+
14
+ export function DeploymentViewParser<TBase extends WithExpressionV2 & WithDeploymentModel>(B: TBase) {
15
+ return class DeploymentViewParser extends B {
16
+ parseDeploymentView(
17
+ astNode: ast.DeploymentView
18
+ ): ParsedAstDeploymentView {
19
+ const body = astNode.body
20
+ invariant(body, 'DynamicElementView body is not defined')
21
+ // only valid props
22
+ const props = body.props.filter(this.isValid)
23
+ const astPath = this.getAstNodePath(astNode)
24
+
25
+ let id = astNode.name
26
+ if (!id) {
27
+ id = 'deployment_' + stringHash(
28
+ this.doc.uri.toString(),
29
+ astPath
30
+ ) as c4.ViewId
31
+ }
32
+
33
+ const title = toSingleLine(props.find(p => p.key === 'title')?.value) ?? null
34
+ const description = removeIndent(props.find(p => p.key === 'description')?.value) ?? null
35
+
36
+ const tags = this.convertTags(body)
37
+ const links = this.convertLinks(body)
38
+
39
+ ViewOps.writeId(astNode, id as c4.ViewId)
40
+
41
+ const manualLayout = parseViewManualLayout(astNode)
42
+
43
+ return {
44
+ __: 'deployment',
45
+ id: id as c4.ViewId,
46
+ astPath,
47
+ title,
48
+ description,
49
+ tags,
50
+ links: isNonEmptyArray(links) ? links : null,
51
+ rules: body.rules.flatMap(n => {
52
+ try {
53
+ return this.isValid(n) ? this.parseDeploymentViewRule(n) : []
54
+ } catch (e) {
55
+ logWarnError(e)
56
+ return []
57
+ }
58
+ }),
59
+ ...(manualLayout && { manualLayout })
60
+ }
61
+ }
62
+
63
+ parseDeploymentViewRule(astRule: ast.DeploymentViewRule): c4.DeploymentViewRule {
64
+ if (ast.isDeploymentViewRulePredicate(astRule)) {
65
+ return this.parseDeploymentViewRulePredicate(astRule)
66
+ }
67
+ if (ast.isViewRuleAutoLayout(astRule)) {
68
+ return toAutoLayout(astRule)
69
+ }
70
+ if (ast.isDeploymentViewRuleStyle(astRule)) {
71
+ return this.parseDeploymentViewRuleStyle(astRule)
72
+ }
73
+ nonexhaustive(astRule)
74
+ }
75
+
76
+ parseDeploymentViewRulePredicate(astRule: ast.DeploymentViewRulePredicate): c4.DeploymentViewRulePredicate {
77
+ const exprs = [] as c4.ExpressionV2[]
78
+ let iterator: ast.DeploymentViewRulePredicateExpression | undefined = astRule.expr
79
+ while (iterator) {
80
+ try {
81
+ const expr = iterator.value
82
+ if (isNonNullish(expr) && this.isValid(expr)) {
83
+ switch (true) {
84
+ case ast.isFqnExpr(expr):
85
+ exprs.unshift(this.parseFqnExpr(expr))
86
+ break
87
+ case ast.isRelationExpr(expr):
88
+ exprs.unshift(this.parseRelationExpr(expr))
89
+ break
90
+ default:
91
+ nonexhaustive(expr)
92
+ }
93
+ }
94
+ } catch (e) {
95
+ logWarnError(e)
96
+ }
97
+ iterator = iterator.prev
98
+ }
99
+ return astRule.isInclude ? { include: exprs } : { exclude: exprs }
100
+ }
101
+
102
+ parseDeploymentViewRuleStyle(astRule: ast.DeploymentViewRuleStyle): c4.DeploymentViewRuleStyle {
103
+ const styleProps = astRule.props.filter(ast.isStyleProperty)
104
+ const notationProperty = astRule.props.find(ast.isNotationProperty)
105
+ const notation = removeIndent(notationProperty?.value)
106
+ const targets = this.parseFqnExpressions(astRule.targets)
107
+ return {
108
+ targets,
109
+ ...(notation && { notation }),
110
+ style: {
111
+ ...toElementStyle(styleProps, this.isValid)
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,118 @@
1
+ import type * as c4 from '@likec4/core'
2
+ import { nonexhaustive, nonNullable } from '@likec4/core'
3
+ import { isNonNullish } from 'remeda'
4
+ import { ast } from '../../ast'
5
+ import { logWarnError } from '../../logger'
6
+ import { instanceRef } from '../../utils/fqnRef'
7
+ import type { Base } from './Base'
8
+
9
+ export type WithExpressionV2 = ReturnType<typeof ExpressionV2Parser>
10
+
11
+ export function ExpressionV2Parser<TBase extends Base>(B: TBase) {
12
+ return class ExpressionV2Parser extends B {
13
+ parseFqnRef(astNode: ast.FqnRef): c4.FqnRef {
14
+ const refValue = nonNullable(
15
+ astNode.value.ref,
16
+ `FqnRef is empty ${astNode.$cstNode?.range.start.line}:${astNode.$cstNode?.range.start.character}`,
17
+ )
18
+ if (ast.isElement(refValue)) {
19
+ const deployedInstanceAst = instanceRef(astNode)
20
+ if (!deployedInstanceAst) {
21
+ return {
22
+ model: this.resolveFqn(refValue),
23
+ }
24
+ }
25
+ const deployment = this.resolveFqn(deployedInstanceAst)
26
+ const element = this.resolveFqn(refValue)
27
+ return {
28
+ deployment,
29
+ element,
30
+ }
31
+ }
32
+
33
+ if (ast.isDeploymentElement(refValue)) {
34
+ return {
35
+ deployment: this.resolveFqn(refValue),
36
+ }
37
+ }
38
+ nonexhaustive(refValue)
39
+ }
40
+
41
+ parseFqnExpr(astNode: ast.FqnExpr): c4.FqnExpr {
42
+ if (ast.isWildcardExpression(astNode)) {
43
+ return {
44
+ wildcard: true,
45
+ }
46
+ }
47
+ if (ast.isFqnRefExpr(astNode)) {
48
+ return this.parseFqnRefExpr(astNode)
49
+ }
50
+ nonexhaustive(astNode)
51
+ }
52
+
53
+ parseFqnRefExpr(astNode: ast.FqnRefExpr): c4.FqnExpr.NonWildcard {
54
+ const ref = this.parseFqnRef(astNode.ref)
55
+ switch (true) {
56
+ case astNode.selector === '._':
57
+ return {
58
+ ref,
59
+ selector: 'expanded',
60
+ } as c4.FqnExpr.NonWildcard
61
+ case astNode.selector === '.**':
62
+ return {
63
+ ref,
64
+ selector: 'descendants',
65
+ } as c4.FqnExpr.NonWildcard
66
+ case astNode.selector === '.*':
67
+ return {
68
+ ref,
69
+ selector: 'children',
70
+ } as c4.FqnExpr.NonWildcard
71
+ default:
72
+ return { ref } as c4.FqnExpr.NonWildcard
73
+ }
74
+ }
75
+
76
+ parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[] {
77
+ const exprs = [] as c4.FqnExpr[]
78
+ let iter: ast.FqnExpressions['prev'] = astNode
79
+ while (iter) {
80
+ try {
81
+ if (isNonNullish(iter.value) && this.isValid(iter.value)) {
82
+ exprs.push(this.parseFqnExpr(iter.value))
83
+ }
84
+ } catch (e) {
85
+ logWarnError(e)
86
+ }
87
+ iter = iter.prev
88
+ }
89
+ return exprs.reverse()
90
+ }
91
+
92
+ parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr {
93
+ if (ast.isDirectedRelationExpr(astNode)) {
94
+ return {
95
+ source: this.parseFqnExpr(astNode.source.from),
96
+ target: this.parseFqnExpr(astNode.target),
97
+ isBidirectional: astNode.source.isBidirectional,
98
+ }
99
+ }
100
+ if (ast.isInOutRelationExpr(astNode)) {
101
+ return {
102
+ inout: this.parseFqnExpr(astNode.inout.to),
103
+ }
104
+ }
105
+ if (ast.isOutgoingRelationExpr(astNode)) {
106
+ return {
107
+ outgoing: this.parseFqnExpr(astNode.from),
108
+ }
109
+ }
110
+ if (ast.isIncomingRelationExpr(astNode)) {
111
+ return {
112
+ incoming: this.parseFqnExpr(astNode.to),
113
+ }
114
+ }
115
+ nonexhaustive(astNode)
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,96 @@
1
+ import type * as c4 from '@likec4/core'
2
+ import { nonexhaustive } from '@likec4/core'
3
+ import { isTruthy } from 'remeda'
4
+ import { ast, type ParsedAstGlobals } from '../../ast'
5
+ import { logger, logWarnError } from '../../logger'
6
+ import type { WithViewsParser } from './ViewsParser'
7
+
8
+ export function GlobalsParser<TBase extends WithViewsParser>(B: TBase) {
9
+ return class GlobalsParser extends B {
10
+ parseGlobals() {
11
+ const { parseResult, c4Globals } = this.doc
12
+ const isValid = this.isValid
13
+
14
+ const globals = parseResult.value.globals.filter(isValid)
15
+
16
+ const elRelPredicates = globals.flatMap(r => r.predicates.filter(isValid))
17
+ for (const predicate of elRelPredicates) {
18
+ try {
19
+ const globalPredicateId = predicate.name as c4.GlobalPredicateId
20
+ if (!isTruthy(globalPredicateId)) {
21
+ continue
22
+ }
23
+ if (globalPredicateId in c4Globals.predicates) {
24
+ logger.warn(`Global predicate named "${globalPredicateId}" is already defined`)
25
+ continue
26
+ }
27
+
28
+ this.parseAndStoreGlobalPredicateGroupOrDynamic(predicate, globalPredicateId, c4Globals)
29
+ } catch (e) {
30
+ logWarnError(e)
31
+ }
32
+ }
33
+
34
+ const styles = globals.flatMap(r => r.styles.filter(isValid))
35
+ for (const style of styles) {
36
+ try {
37
+ const globalStyleId = style.id.name as c4.GlobalStyleID
38
+ if (!isTruthy(globalStyleId)) {
39
+ continue
40
+ }
41
+ if (globalStyleId in c4Globals.styles) {
42
+ logger.warn(`Global style named "${globalStyleId}" is already defined`)
43
+ continue
44
+ }
45
+
46
+ const styles = this.parseGlobalStyleOrGroup(style)
47
+ if (styles.length > 0) {
48
+ c4Globals.styles[globalStyleId] = styles as c4.NonEmptyArray<c4.ViewRuleStyle>
49
+ }
50
+ } catch (e) {
51
+ logWarnError(e)
52
+ }
53
+ }
54
+ }
55
+
56
+ parseAndStoreGlobalPredicateGroupOrDynamic(
57
+ astRule: ast.GlobalPredicateGroup | ast.GlobalDynamicPredicateGroup,
58
+ id: c4.GlobalPredicateId,
59
+ c4Globals: ParsedAstGlobals
60
+ ) {
61
+ if (ast.isGlobalPredicateGroup(astRule)) {
62
+ const predicates = this.parseGlobalPredicateGroup(astRule)
63
+ if (predicates.length > 0) {
64
+ c4Globals.predicates[id] = predicates as c4.NonEmptyArray<c4.ViewRulePredicate>
65
+ }
66
+ return
67
+ }
68
+ if (ast.isGlobalDynamicPredicateGroup(astRule)) {
69
+ const predicates = this.parseGlobalDynamicPredicateGroup(astRule)
70
+ if (predicates.length > 0) {
71
+ c4Globals.dynamicPredicates[id] = predicates as c4.NonEmptyArray<c4.DynamicViewIncludeRule>
72
+ }
73
+ return
74
+ }
75
+ nonexhaustive(astRule)
76
+ }
77
+
78
+ parseGlobalPredicateGroup(astRule: ast.GlobalPredicateGroup): c4.ViewRulePredicate[] {
79
+ return astRule.predicates.map(p => this.parseViewRulePredicate(p))
80
+ }
81
+
82
+ parseGlobalDynamicPredicateGroup(astRule: ast.GlobalDynamicPredicateGroup): c4.DynamicViewIncludeRule[] {
83
+ return astRule.predicates.map(p => this.parseDynamicViewIncludePredicate(p))
84
+ }
85
+
86
+ parseGlobalStyleOrGroup(astRule: ast.GlobalStyle | ast.GlobalStyleGroup): c4.ViewRuleStyle[] {
87
+ if (ast.isGlobalStyle(astRule)) {
88
+ return [this.parseViewRuleStyle(astRule)]
89
+ }
90
+ if (ast.isGlobalStyleGroup(astRule)) {
91
+ return astRule.styles.map(s => this.parseViewRuleStyle(s))
92
+ }
93
+ nonexhaustive(astRule)
94
+ }
95
+ }
96
+ }