@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
@@ -6,21 +6,38 @@ import {
6
6
  MultiMap,
7
7
  type PrecomputedScopes
8
8
  } from 'langium'
9
- import { isNullish, isTruthy } from 'remeda'
9
+ import { entries, filter, flatMap, forEach, forEachObj, groupBy, isNullish, isTruthy, pipe } from 'remeda'
10
10
  import type { CancellationToken } from 'vscode-languageserver'
11
11
  import { ast, type LikeC4LangiumDocument } from '../ast'
12
- import { logError } from '../logger'
12
+ import { logError, logWarnError } from '../logger'
13
+ import type { LikeC4Services } from '../module'
13
14
 
14
15
  type ElementsContainer = ast.Model | ast.ElementBody | ast.ExtendElementBody
16
+ type DeploymentsContainer = ast.ModelDeployments | ast.DeploymentNodeBody
17
+
18
+ function uniqueDescriptions(
19
+ descs: AstNodeDescription[]
20
+ ): AstNodeDescription[] {
21
+ return pipe(
22
+ descs,
23
+ groupBy(desc => `${desc.type}.${desc.name}`),
24
+ entries(),
25
+ flatMap(([_, descs]) => descs.length === 1 ? descs : [])
26
+ )
27
+ }
15
28
 
16
29
  export class LikeC4ScopeComputation extends DefaultScopeComputation {
30
+ constructor(services: LikeC4Services) {
31
+ super(services)
32
+ }
33
+
17
34
  override async computeExports(
18
35
  document: LikeC4LangiumDocument,
19
36
  _cancelToken?: CancellationToken
20
37
  ): Promise<AstNodeDescription[]> {
21
38
  const docExports: AstNodeDescription[] = []
22
39
  try {
23
- const { specifications, models, views, globals, likec4lib } = document.parseResult.value
40
+ const { specifications, models, views, globals, likec4lib, deployments } = document.parseResult.value
24
41
 
25
42
  // Process library
26
43
  this.exportLibrary(likec4lib, docExports, document)
@@ -36,6 +53,8 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
36
53
 
37
54
  // Process global
38
55
  this.exportGlobals(globals, docExports, document)
56
+
57
+ this.exportDeployments(deployments, docExports, document)
39
58
  } catch (e) {
40
59
  logError(e)
41
60
  }
@@ -43,14 +62,15 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
43
62
  }
44
63
 
45
64
  private exportViews(
46
- views: ast.ModelViews[] | undefined,
65
+ modelViews: ast.ModelViews[] | undefined,
47
66
  docExports: AstNodeDescription[],
48
67
  document: LikeC4LangiumDocument
49
68
  ) {
69
+ const views = modelViews?.flatMap(m => m.views)
50
70
  if (isNullish(views) || views.length === 0) {
51
71
  return
52
72
  }
53
- for (const viewAst of views.flatMap(v => v.views)) {
73
+ for (const viewAst of views) {
54
74
  try {
55
75
  if (isTruthy(viewAst.name)) {
56
76
  docExports.push(this.descriptions.createDescription(viewAst, viewAst.name, document))
@@ -139,12 +159,14 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
139
159
  const spec of specifications.flatMap(s => [
140
160
  ...s.elements,
141
161
  ...s.relationships,
162
+ ...s.deploymentNodes,
142
163
  ...s.tags,
143
164
  ...s.colors
144
165
  ])
145
166
  ) {
146
167
  try {
147
168
  switch (true) {
169
+ case ast.isSpecificationDeploymentNodeKind(spec):
148
170
  case ast.isSpecificationElementKind(spec): {
149
171
  if (isTruthy(spec.kind.name)) {
150
172
  docExports.push(
@@ -188,21 +210,58 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
188
210
  }
189
211
  }
190
212
 
213
+ private exportDeployments(
214
+ modelDeployments: ast.ModelDeployments[] | undefined,
215
+ docExports: AstNodeDescription[],
216
+ document: LikeC4LangiumDocument
217
+ ) {
218
+ const nodes = modelDeployments?.flatMap(m => m.elements)
219
+ if (isNullish(nodes) || nodes.length === 0) {
220
+ return
221
+ }
222
+ for (const node of nodes) {
223
+ try {
224
+ if (ast.isDeploymentNode(node) && isTruthy(node.name)) {
225
+ docExports.push(this.descriptions.createDescription(node, node.name, document))
226
+ }
227
+ } catch (e) {
228
+ logWarnError(e)
229
+ }
230
+ }
231
+ }
232
+
191
233
  override computeLocalScopes(
192
234
  document: LikeC4LangiumDocument,
193
235
  _cancelToken?: CancellationToken
194
236
  ): Promise<PrecomputedScopes> {
195
237
  return new Promise(resolve => {
196
238
  const root = document.parseResult.value
239
+ const descendants = [] as AstNodeDescription[]
197
240
  const scopes = new MultiMap<AstNode, AstNodeDescription>()
241
+
198
242
  for (const model of root.models) {
199
243
  try {
200
- const nested = this.processContainer(model, scopes, document)
201
- scopes.addAll(root, nested.values())
244
+ descendants.push(
245
+ ...this.processContainer(model, scopes, document)
246
+ )
202
247
  } catch (e) {
203
248
  logError(e)
204
249
  }
205
250
  }
251
+ for (const deployment of root.deployments) {
252
+ try {
253
+ descendants.push(
254
+ ...this.processDeployments(deployment, scopes, document)
255
+ )
256
+ } catch (e) {
257
+ logWarnError(e)
258
+ }
259
+ }
260
+
261
+ uniqueDescriptions(descendants).forEach(desc => {
262
+ scopes.add(root, desc)
263
+ })
264
+
206
265
  resolve(scopes)
207
266
  })
208
267
  }
@@ -211,9 +270,10 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
211
270
  container: ElementsContainer,
212
271
  scopes: PrecomputedScopes,
213
272
  document: LikeC4LangiumDocument
214
- ) {
273
+ ): AstNodeDescription[] {
215
274
  const localScope = new MultiMap<string, AstNodeDescription>()
216
- const nestedScopes = new MultiMap<string, AstNodeDescription>()
275
+ const descedants = [] as AstNodeDescription[]
276
+
217
277
  for (const el of container.elements) {
218
278
  if (ast.isRelation(el)) {
219
279
  continue
@@ -231,25 +291,74 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
231
291
 
232
292
  if (subcontainer && subcontainer.elements.length > 0) {
233
293
  try {
234
- const nested = this.processContainer(subcontainer, scopes, document)
235
- for (const [nestedName, desc] of nested) {
236
- nestedScopes.add(nestedName, desc)
237
- }
294
+ descedants.push(
295
+ ...this.processContainer(subcontainer, scopes, document)
296
+ )
238
297
  } catch (e) {
239
- logError(e)
298
+ logWarnError(e)
240
299
  }
241
300
  }
242
301
  }
243
302
 
244
- if (nestedScopes.size > 0) {
245
- for (const [name, descriptions] of nestedScopes.entriesGroupedByKey()) {
246
- // If name is unique for current scope
247
- if (!localScope.has(name) && descriptions.length === 1) {
248
- localScope.add(name, descriptions[0]!)
303
+ if (descedants.length) {
304
+ pipe(
305
+ descedants,
306
+ filter(desc => !localScope.has(desc.name)),
307
+ groupBy(desc => desc.name),
308
+ forEachObj((descs, name) => {
309
+ if (descs.length === 1) {
310
+ localScope.add(name, descs[0])
311
+ }
312
+ })
313
+ )
314
+ }
315
+ const local = [...localScope.values()]
316
+ scopes.addAll(container, local)
317
+ return local
318
+ }
319
+
320
+ protected processDeployments(
321
+ container: DeploymentsContainer,
322
+ scopes: PrecomputedScopes,
323
+ document: LikeC4LangiumDocument
324
+ ): AstNodeDescription[] {
325
+ const localnames = new Set<string>()
326
+ const descedants = [] as AstNodeDescription[]
327
+
328
+ for (const el of container.elements) {
329
+ if (ast.isDeploymentRelation(el)) {
330
+ continue
331
+ }
332
+
333
+ let name = this.nameProvider.getName(el)
334
+ if (isTruthy(name)) {
335
+ const desc = this.descriptions.createDescription(el, name, document)
336
+ scopes.add(container, desc)
337
+ localnames.add(desc.name)
338
+ }
339
+
340
+ if (ast.isDeploymentNode(el) && el.body) {
341
+ try {
342
+ descedants.push(
343
+ ...this.processDeployments(el.body, scopes, document)
344
+ )
345
+ } catch (e) {
346
+ logWarnError(e)
249
347
  }
250
348
  }
251
349
  }
252
- scopes.addAll(container, localScope.values())
253
- return localScope
350
+ if (descedants.length > 0) {
351
+ pipe(
352
+ descedants,
353
+ filter(desc => !localnames.has(desc.name)),
354
+ groupBy(desc => desc.name),
355
+ forEachObj((descs, name) => {
356
+ if (descs.length === 1) {
357
+ scopes.add(container, descs[0])
358
+ }
359
+ })
360
+ )
361
+ }
362
+ return [...scopes.get(container).values()]
254
363
  }
255
364
  }
@@ -1,5 +1,5 @@
1
- import { invariant } from '@likec4/core'
2
1
  import type * as c4 from '@likec4/core'
2
+ import { nonexhaustive } from '@likec4/core'
3
3
  import type { AstNode } from 'langium'
4
4
  import {
5
5
  type AstNodeDescription,
@@ -17,19 +17,21 @@ import {
17
17
  StreamScope
18
18
  } from 'langium'
19
19
  import { ast } from '../ast'
20
- import { elementRef, getFqnElementRef } from '../elementRef'
21
20
  import { logger } from '../logger'
22
- import type { FqnIndex } from '../model/fqn-index'
21
+ import type { DeploymentsIndex, FqnIndex } from '../model'
23
22
  import type { LikeC4Services } from '../module'
23
+ import { elementRef, getFqnElementRef } from '../utils/elementRef'
24
24
 
25
25
  const { getDocument } = AstUtils
26
26
 
27
27
  export class LikeC4ScopeProvider extends DefaultScopeProvider {
28
+ private deploymentsIndex: DeploymentsIndex
28
29
  private fqnIndex: FqnIndex
29
30
 
30
31
  constructor(services: LikeC4Services) {
31
32
  super(services)
32
33
  this.fqnIndex = services.likec4.FqnIndex
34
+ this.deploymentsIndex = services.likec4.DeploymentsIndex
33
35
  }
34
36
 
35
37
  private directChildrenOf(parent: c4.Fqn): Stream<AstNodeDescription> {
@@ -87,15 +89,20 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
87
89
  override getScope(context: ReferenceInfo): Scope {
88
90
  try {
89
91
  const referenceType = this.reflection.getReferenceType(context)
90
- if (referenceType !== ast.Element) {
91
- return this.getGlobalScope(referenceType)
92
- }
93
92
  try {
94
93
  const container = context.container
94
+ if (ast.isFqnRef(container)) {
95
+ return this.getScopeForFqnRef(container, context)
96
+ }
97
+
98
+ if (referenceType !== ast.Element) {
99
+ return this.getGlobalScope(referenceType, context)
100
+ }
101
+
95
102
  if (ast.isFqnElementRef(container) && context.property === 'el') {
96
103
  const parent = container.parent
97
104
  if (!parent) {
98
- return this.getGlobalScope(referenceType)
105
+ return this.getGlobalScope(referenceType, context)
99
106
  }
100
107
  return new StreamScope(this.directChildrenOf(getFqnElementRef(parent)))
101
108
  }
@@ -119,7 +126,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
119
126
  return this.computeScope(context)
120
127
  } catch (e) {
121
128
  logger.warn(e)
122
- return this.getGlobalScope(referenceType)
129
+ return this.getGlobalScope(referenceType, context)
123
130
  }
124
131
  } catch (e) {
125
132
  logger.warn(e)
@@ -127,42 +134,68 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
127
134
  }
128
135
  }
129
136
 
130
- protected computeScope(context: ReferenceInfo) {
131
- const referenceType = this.reflection.getReferenceType(context)
132
- // computeScope is called only for elements
133
- invariant(referenceType === ast.Element, 'Invalid reference type')
137
+ protected getScopeForFqnRef(container: ast.FqnRef, context: ReferenceInfo) {
138
+ const parent = container.parent
139
+ if (!parent) {
140
+ return this.createScope(
141
+ // First preference for deployment nodes
142
+ this.computeScope(context, ast.DeploymentNode).getAllElements(),
143
+ this.createScope(
144
+ // Second preference for deployed instances
145
+ this.computeScope(context, ast.DeployedInstance).getAllElements(),
146
+ // Third preference for elements if we are in deployment view
147
+ AstUtils.hasContainerOfType(container, ast.isDeploymentView)
148
+ ? this.computeScope(context, ast.Element)
149
+ : EMPTY_SCOPE
150
+ )
151
+ )
152
+ }
153
+ const parentRef = parent.value.ref
154
+ if (!parentRef) {
155
+ return EMPTY_SCOPE
156
+ }
157
+ if (ast.isDeploymentNode(parentRef)) {
158
+ return new StreamScope(this.deploymentsIndex.nested(parentRef))
159
+ }
160
+ if (ast.isDeployedInstance(parentRef)) {
161
+ return new StreamScope(this.scopeElementRef(parentRef.element))
162
+ }
163
+ if (ast.isElement(parentRef)) {
164
+ return new StreamScope(this.uniqueDescedants(() => parentRef))
165
+ }
166
+ return nonexhaustive(parentRef)
167
+ }
168
+
169
+ protected computeScope(context: ReferenceInfo, referenceType = this.reflection.getReferenceType(context)) {
170
+ const isElementReference = this.reflection.isSubtype(referenceType, ast.Element)
171
+
134
172
  const scopes: Stream<AstNodeDescription>[] = []
135
173
  const doc = getDocument(context.container)
136
174
  const precomputed = doc.precomputedScopes
137
175
 
138
- if (precomputed) {
139
- const byReferenceType = (desc: AstNodeDescription) => this.reflection.isSubtype(desc.type, referenceType)
140
- let container: AstNode | undefined = context.container
141
- while (container) {
142
- const elements = precomputed.get(container).filter(byReferenceType)
143
- if (elements.length > 0) {
144
- scopes.push(stream(elements))
145
- }
176
+ if (!precomputed) {
177
+ return this.getGlobalScope(referenceType, context)
178
+ }
146
179
 
147
- if (ast.isExtendElementBody(container)) {
148
- scopes.push(this.scopeExtendElement(container.$container))
149
- }
150
- if (ast.isElementViewBody(container)) {
151
- scopes.push(this.scopeElementView(container.$container))
152
- }
153
- container = container.$container
180
+ const byReferenceType = (desc: AstNodeDescription) => this.reflection.isSubtype(desc.type, referenceType)
181
+ let container: AstNode | undefined = context.container
182
+ while (container) {
183
+ const elements = precomputed.get(container).filter(byReferenceType)
184
+ if (elements.length > 0) {
185
+ scopes.push(stream(elements))
154
186
  }
187
+
188
+ if (isElementReference && ast.isExtendElementBody(container)) {
189
+ scopes.push(this.scopeExtendElement(container.$container))
190
+ }
191
+ if (isElementReference && ast.isElementViewBody(container)) {
192
+ scopes.push(this.scopeElementView(container.$container))
193
+ }
194
+ container = container.$container
155
195
  }
156
196
 
157
197
  return scopes.reduceRight((outerScope, elements) => {
158
198
  return this.createScope(elements, outerScope)
159
- }, this.getGlobalScope(referenceType))
160
- }
161
-
162
- /**
163
- * Create a global scope filtered for the given reference type.
164
- */
165
- protected override getGlobalScope(referenceType: string): Scope {
166
- return new StreamScope(this.indexManager.allElements(referenceType))
199
+ }, this.getGlobalScope(referenceType, context))
167
200
  }
168
201
  }
@@ -16,13 +16,17 @@ export class NodeKindProvider implements LspNodeKindProvider {
16
16
  switch (true) {
17
17
  case hasType(
18
18
  ast.Element,
19
- ast.ExtendElement
19
+ ast.ExtendElement,
20
+ ast.DeploymentNode,
21
+ ast.DeployedInstance
20
22
  ):
21
23
  return SymbolKind.Constructor
22
24
 
23
25
  case hasType(
24
26
  ast.Model,
25
27
  ast.ModelViews,
28
+ ast.ModelDeployments,
29
+ ast.Globals,
26
30
  ast.SpecificationRule
27
31
  ):
28
32
  return SymbolKind.Namespace
@@ -46,7 +50,9 @@ export class NodeKindProvider implements LspNodeKindProvider {
46
50
 
47
51
  case hasType(
48
52
  ast.ElementKind,
49
- ast.SpecificationElementKind
53
+ ast.DeploymentNodeKind,
54
+ ast.SpecificationElementKind,
55
+ ast.SpecificationDeploymentNodeKind
50
56
  ):
51
57
  return SymbolKind.TypeParameter
52
58
  }
@@ -66,6 +72,8 @@ export class NodeKindProvider implements LspNodeKindProvider {
66
72
 
67
73
  case hasType(
68
74
  ast.Element,
75
+ ast.DeploymentNode,
76
+ ast.DeployedInstance,
69
77
  ast.ExtendElement
70
78
  ):
71
79
  return CompletionItemKind.Constructor
@@ -73,6 +81,8 @@ export class NodeKindProvider implements LspNodeKindProvider {
73
81
  case hasType(
74
82
  ast.Model,
75
83
  ast.ModelViews,
84
+ ast.ModelDeployments,
85
+ ast.Globals,
76
86
  ast.SpecificationRule
77
87
  ):
78
88
  return CompletionItemKind.Module
@@ -98,7 +108,9 @@ export class NodeKindProvider implements LspNodeKindProvider {
98
108
 
99
109
  case hasType(
100
110
  ast.ElementKind,
101
- ast.SpecificationElementKind
111
+ ast.SpecificationElementKind,
112
+ ast.DeploymentNodeKind,
113
+ ast.SpecificationDeploymentNodeKind
102
114
  ):
103
115
  return CompletionItemKind.TypeParameter
104
116
 
@@ -1,5 +1,5 @@
1
1
  import type * as c4 from '@likec4/core'
2
- import type { ast } from './ast'
2
+ import type { ast } from '../ast'
3
3
  /**
4
4
  * Returns referenced AST Element
5
5
  */
@@ -0,0 +1,56 @@
1
+ import { AstUtils } from 'langium'
2
+ import { isNullish } from 'remeda'
3
+ import { ast } from '../ast'
4
+
5
+ export function instanceRef(deploymentRef: ast.FqnRef): ast.DeployedInstance | null {
6
+ let referenceable
7
+ while ((referenceable = deploymentRef.value?.ref)) {
8
+ if (ast.isDeploymentNode(referenceable)) {
9
+ return null
10
+ }
11
+ if (ast.isDeployedInstance(referenceable)) {
12
+ return referenceable
13
+ }
14
+ if (isNullish(deploymentRef.parent)) {
15
+ return null
16
+ }
17
+ deploymentRef = deploymentRef.parent
18
+ }
19
+ return null
20
+ }
21
+
22
+ export function deploymentNodeRef(deploymentRef: ast.FqnRef): ast.DeploymentNode | null {
23
+ let referenceable = deploymentRef.value.ref ?? null
24
+ if (!referenceable || ast.isDeploymentNode(referenceable)) {
25
+ return referenceable
26
+ }
27
+ const artifact = instanceRef(deploymentRef)
28
+ // Because path in deploymentRef may be omitted,
29
+ // we find artifact first and then its container
30
+ return artifact ? AstUtils.getContainerOfType(artifact, ast.isDeploymentNode) ?? null : null
31
+ }
32
+
33
+ export function isReferenceToLogicalModel(node: ast.FqnRef) {
34
+ // iterate up the root parent
35
+ while (node.parent) {
36
+ node = node.parent
37
+ }
38
+ return ast.isElement(node.value.ref)
39
+ }
40
+
41
+ /**
42
+ * Returns true if node references deployment model
43
+ */
44
+ export function isReferenceToDeploymentModel(node: ast.FqnRef) {
45
+ let referenceable
46
+ while ((referenceable = node.value?.ref)) {
47
+ if (ast.isDeploymentElement(referenceable)) {
48
+ return true
49
+ }
50
+ if (isNullish(node.parent)) {
51
+ return false
52
+ }
53
+ node = node.parent
54
+ }
55
+ return false
56
+ }
@@ -1,6 +1,6 @@
1
- import hash from 'string-hash'
1
+ import { stringHash as hash } from '@likec4/core'
2
2
 
3
3
  export function stringHash(...str: [string, ...string[]]): string {
4
4
  var s = str.length > 1 ? str.join(':::') : str[0]
5
- return hash(s).toString(36)
5
+ return hash(s)
6
6
  }
@@ -1,4 +1,5 @@
1
- import { type AstNode, interruptAndCheck, type ValidationAcceptor, type ValidationCheck } from 'langium'
1
+ import { type AstNode, type ValidationAcceptor, type ValidationCheck } from 'langium'
2
+ import { isPromise } from 'remeda'
2
3
  import type { CancellationToken } from 'vscode-jsonrpc'
3
4
  import { logWarnError } from '../logger'
4
5
 
@@ -13,11 +14,11 @@ export const RESERVED_WORDS = [
13
14
 
14
15
  export function tryOrLog<T extends AstNode>(fn: ValidationCheck<T>): ValidationCheck<T> {
15
16
  return async (node: T, accept: ValidationAcceptor, cancelToken: CancellationToken) => {
16
- if (cancelToken) {
17
- await interruptAndCheck(cancelToken)
18
- }
19
17
  try {
20
- await fn(node, accept, cancelToken)
18
+ const result = fn(node, accept, cancelToken)
19
+ if (isPromise(result)) {
20
+ await result
21
+ }
21
22
  } catch (e) {
22
23
  logWarnError(e)
23
24
  }
@@ -0,0 +1,131 @@
1
+ import { FqnRef, isSameHierarchy, nonNullable } from '@likec4/core'
2
+ import { type ValidationCheck, AstUtils } from 'langium'
3
+ import { ast } from '../ast'
4
+ import type { LikeC4Services } from '../module'
5
+ import type { LikeC4NameProvider } from '../references'
6
+ import { RESERVED_WORDS, tryOrLog } from './_shared'
7
+
8
+ const { getDocument } = AstUtils
9
+
10
+ export const deploymentNodeChecks = (services: LikeC4Services): ValidationCheck<ast.DeploymentNode> => {
11
+ const DeploymentsIndex = services.likec4.DeploymentsIndex
12
+ const Names = services.references.NameProvider
13
+ return tryOrLog((el, accept) => {
14
+ const nodeName = Names.getName(el)
15
+ if (!nodeName) {
16
+ accept('error', 'DeploymentNode must be named', {
17
+ node: el,
18
+ })
19
+ return
20
+ }
21
+ const range = nonNullable(Names.getNameNode(el), 'name CstNode not found').range
22
+
23
+ if (RESERVED_WORDS.includes(nodeName)) {
24
+ accept('error', `Reserved word: ${nodeName}`, {
25
+ node: el,
26
+ range,
27
+ })
28
+ }
29
+ const fqnName = DeploymentsIndex.getFqn(el)
30
+
31
+ const withSameName = DeploymentsIndex.byFqn(fqnName).limit(2).toArray()
32
+ if (withSameName.length > 1) {
33
+ accept(
34
+ 'error',
35
+ `Duplicate node name "${fqnName}"`,
36
+ {
37
+ node: el,
38
+ range,
39
+ },
40
+ )
41
+ }
42
+ })
43
+ }
44
+
45
+ export const deployedInstanceChecks = (services: LikeC4Services): ValidationCheck<ast.DeployedInstance> => {
46
+ const DeploymentsIndex = services.likec4.DeploymentsIndex
47
+ const Names = services.references.NameProvider as LikeC4NameProvider
48
+ // const Locator = services.workspace.AstNodeLocator
49
+ return tryOrLog((el, accept) => {
50
+ const artifactName = Names.getName(el)
51
+ if (!artifactName) {
52
+ accept('error', 'Deployed instance must be named, unique inside node', {
53
+ node: el,
54
+ })
55
+ return
56
+ }
57
+ const range = nonNullable(Names.getNameNode(el), 'name CstNode not found').range
58
+
59
+ if (RESERVED_WORDS.includes(artifactName)) {
60
+ accept('error', `Reserved word: ${artifactName}`, {
61
+ node: el,
62
+ range,
63
+ })
64
+ }
65
+
66
+ const fqnName = DeploymentsIndex.getFqn(el)
67
+
68
+ const withSameName = DeploymentsIndex.byFqn(fqnName).limit(2).toArray()
69
+ if (withSameName.length > 1) {
70
+ accept(
71
+ 'error',
72
+ `Duplicate instance name "${fqnName}"`,
73
+ {
74
+ node: el,
75
+ range,
76
+ },
77
+ )
78
+ }
79
+ })
80
+ }
81
+
82
+ export const deploymentRelationChecks = (services: LikeC4Services): ValidationCheck<ast.DeploymentRelation> => {
83
+ const ModelParser = services.likec4.ModelParser
84
+ return tryOrLog((el, accept) => {
85
+ const source = el.source?.value?.ref
86
+ if (!source) {
87
+ let sourceCstText = el.source?.$cstNode?.text ?? ''
88
+ accept('error', `DeploymentRelation source '${sourceCstText}' not resolved`, {
89
+ node: el,
90
+ property: 'source',
91
+ })
92
+ return
93
+ }
94
+ const target = el.target?.value?.ref
95
+ if (!target) {
96
+ let targetCstText = el.target?.$cstNode?.text ?? ''
97
+ accept('error', `DeploymentRelation target '${targetCstText}' not resolved`, {
98
+ node: el,
99
+ property: 'target',
100
+ })
101
+ return
102
+ }
103
+
104
+ const doc = getDocument(el)
105
+ const parser = ModelParser.forDocument(doc)
106
+
107
+ const sourceFqnRef = parser.parseFqnRef(el.source)
108
+ if (FqnRef.isModelRef(sourceFqnRef)) {
109
+ accept('error', 'DeploymentRelation must refer deployment element', {
110
+ node: el,
111
+ property: 'source',
112
+ })
113
+ return
114
+ }
115
+
116
+ const targetFqnRef = parser.parseFqnRef(el.target)
117
+ if (FqnRef.isModelRef(targetFqnRef)) {
118
+ accept('error', 'DeploymentRelation must refer deployment element', {
119
+ node: el,
120
+ property: 'target',
121
+ })
122
+ return
123
+ }
124
+
125
+ if (isSameHierarchy(sourceFqnRef.deployment, targetFqnRef.deployment)) {
126
+ accept('error', 'Invalid parent-child relationship', {
127
+ node: el,
128
+ })
129
+ }
130
+ })
131
+ }