@likec4/language-server 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/contrib/likec4.tmLanguage.json +1 -1
  2. package/dist/browser.cjs +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.mts +2 -2
  5. package/dist/browser.d.ts +2 -2
  6. package/dist/browser.mjs +2 -2
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.d.cts +2 -2
  9. package/dist/index.d.mts +2 -2
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.mjs +2 -2
  12. package/dist/protocol.d.cts +8 -5
  13. package/dist/protocol.d.mts +8 -5
  14. package/dist/protocol.d.ts +8 -5
  15. package/dist/shared/{language-server.DZRuJVSg.cjs → language-server.CO_nmHiL.cjs} +5605 -4215
  16. package/dist/shared/{language-server.DJo88TnT.d.cts → language-server.Da6ey08o.d.cts} +391 -110
  17. package/dist/shared/{language-server.PEjk7U9s.d.ts → language-server.De7S3e5Z.d.ts} +391 -110
  18. package/dist/shared/{language-server.BgDKnNok.d.mts → language-server.Dj4iDjtB.d.mts} +391 -110
  19. package/dist/shared/{language-server.B8qSDsWW.mjs → language-server.oO_9JoAG.mjs} +5594 -4215
  20. package/package.json +17 -31
  21. package/src/Rpc.ts +6 -3
  22. package/src/ast.ts +124 -71
  23. package/src/formatting/LikeC4Formatter.ts +9 -4
  24. package/src/generated/ast.ts +656 -40
  25. package/src/generated/grammar.ts +2 -2
  26. package/src/generated/module.ts +3 -2
  27. package/src/index.ts +1 -0
  28. package/src/like-c4.langium +170 -22
  29. package/src/logger.ts +7 -2
  30. package/src/lsp/CodeLensProvider.ts +0 -1
  31. package/src/lsp/CompletionProvider.ts +17 -2
  32. package/src/lsp/DocumentSymbolProvider.ts +5 -2
  33. package/src/lsp/HoverProvider.ts +34 -2
  34. package/src/lsp/SemanticTokenProvider.ts +58 -32
  35. package/src/model/deployments-index.ts +218 -0
  36. package/src/model/fqn-computation.ts +1 -1
  37. package/src/model/fqn-index.ts +0 -1
  38. package/src/model/index.ts +1 -0
  39. package/src/model/model-builder.ts +172 -37
  40. package/src/model/model-locator.ts +36 -7
  41. package/src/model/model-parser.ts +554 -92
  42. package/src/model-change/changeViewLayout.ts +2 -2
  43. package/src/module.ts +10 -4
  44. package/src/protocol.ts +10 -6
  45. package/src/references/index.ts +1 -0
  46. package/src/references/name-provider.ts +37 -0
  47. package/src/references/scope-computation.ts +130 -21
  48. package/src/references/scope-provider.ts +63 -36
  49. package/src/shared/NodeKindProvider.ts +15 -3
  50. package/src/utils/deploymentRef.ts +31 -0
  51. package/src/{elementRef.ts → utils/elementRef.ts} +1 -1
  52. package/src/utils/stringHash.ts +2 -2
  53. package/src/validation/_shared.ts +7 -5
  54. package/src/validation/deployment-checks.ts +144 -0
  55. package/src/validation/dynamic-view-step.ts +1 -1
  56. package/src/validation/index.ts +7 -0
  57. package/src/validation/relation.ts +1 -1
  58. package/src/validation/view-predicates/deployments.ts +56 -0
  59. package/src/validation/view-predicates/index.ts +1 -0
  60. package/src/view-utils/assignNavigateTo.ts +6 -5
  61. package/src/view-utils/index.ts +0 -1
  62. package/dist/model-graph/index.cjs +0 -10
  63. package/dist/model-graph/index.d.cts +0 -81
  64. package/dist/model-graph/index.d.mts +0 -81
  65. package/dist/model-graph/index.d.ts +0 -81
  66. package/dist/model-graph/index.mjs +0 -1
  67. package/dist/shared/language-server.BGGRJRnr.d.mts +0 -1338
  68. package/dist/shared/language-server.BXFhlTPo.mjs +0 -1953
  69. package/dist/shared/language-server.Bmpq16Gw.d.ts +0 -1338
  70. package/dist/shared/language-server.C1ZfM22X.d.cts +0 -1338
  71. package/dist/shared/language-server.N8HLDQqz.cjs +0 -1967
  72. package/src/model-graph/LikeC4ModelGraph.ts +0 -338
  73. package/src/model-graph/compute-view/__test__/fixture.ts +0 -630
  74. package/src/model-graph/compute-view/compute.ts +0 -788
  75. package/src/model-graph/compute-view/index.ts +0 -33
  76. package/src/model-graph/compute-view/predicates.ts +0 -509
  77. package/src/model-graph/dynamic-view/__test__/fixture.ts +0 -61
  78. package/src/model-graph/dynamic-view/compute.ts +0 -281
  79. package/src/model-graph/dynamic-view/index.ts +0 -29
  80. package/src/model-graph/index.ts +0 -3
  81. package/src/model-graph/utils/applyCustomElementProperties.ts +0 -65
  82. package/src/model-graph/utils/applyCustomRelationProperties.ts +0 -41
  83. package/src/model-graph/utils/applyViewRuleStyles.ts +0 -49
  84. package/src/model-graph/utils/buildComputeNodes.ts +0 -113
  85. package/src/model-graph/utils/buildElementNotations.ts +0 -63
  86. package/src/model-graph/utils/elementExpressionToPredicate.ts +0 -39
  87. package/src/model-graph/utils/relationExpressionToPredicates.ts +0 -43
  88. package/src/model-graph/utils/sortNodes.ts +0 -105
  89. package/src/model-graph/utils/uniqueTags.test.ts +0 -42
  90. package/src/model-graph/utils/uniqueTags.ts +0 -19
  91. package/src/utils/graphlib.ts +0 -9
  92. package/src/view-utils/resolve-extended-views.ts +0 -66
  93. package/src/view-utils/resolve-global-rules.ts +0 -88
  94. package/src/view-utils/view-hash.ts +0 -27
@@ -1,6 +1,6 @@
1
1
  import { invariant, type ViewChange } from '@likec4/core'
2
2
  import { GrammarUtils } from 'langium'
3
- import { isNumber } from 'remeda'
3
+ import { findLast, isNumber } from 'remeda'
4
4
  import { TextEdit } from 'vscode-languageserver-types'
5
5
  import { ast, type ParsedAstView, type ParsedLikeC4LangiumDocument, toAstViewLayoutDirection } from '../ast'
6
6
  import type { LikeC4Services } from '../module'
@@ -24,7 +24,7 @@ export function changeViewLayout(_services: LikeC4Services, {
24
24
  const viewCstNode = viewAst.$cstNode
25
25
  invariant(viewCstNode, 'viewCstNode')
26
26
  const newdirection = toAstViewLayoutDirection(layout.direction)
27
- const existingRule = viewAst.body.rules.findLast(ast.isViewRuleAutoLayout) as ast.ViewRuleAutoLayout | undefined
27
+ const existingRule = findLast(viewAst.body.rules, ast.isViewRuleAutoLayout) as ast.ViewRuleAutoLayout | undefined
28
28
 
29
29
  let newRule = `autoLayout ${newdirection}`
30
30
 
package/src/module.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { EmptyFileSystem, inject, type Module, WorkspaceCache } from 'langium'
1
+ import { DocumentCache, EmptyFileSystem, inject, type Module, WorkspaceCache } from 'langium'
2
2
  import {
3
3
  createDefaultModule,
4
4
  createDefaultSharedModule,
@@ -8,6 +8,7 @@ import {
8
8
  type PartialLangiumServices,
9
9
  type PartialLangiumSharedServices
10
10
  } from 'langium/lsp'
11
+ import { LikeC4Formatter } from './formatting/LikeC4Formatter'
11
12
  import { LikeC4GeneratedModule, LikeC4GeneratedSharedModule } from './generated/module'
12
13
  import { logErrorToTelemetry, logToLspConnection } from './logger'
13
14
  import {
@@ -19,13 +20,12 @@ import {
19
20
  LikeC4HoverProvider,
20
21
  LikeC4SemanticTokenProvider
21
22
  } from './lsp'
22
- import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model'
23
+ import { DeploymentsIndex, FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model'
23
24
  import { LikeC4ModelChanges } from './model-change/ModelChanges'
24
- import { LikeC4ScopeComputation, LikeC4ScopeProvider } from './references'
25
+ import { LikeC4NameProvider, LikeC4ScopeComputation, LikeC4ScopeProvider } from './references'
25
26
  import { Rpc } from './Rpc'
26
27
  import { LikeC4WorkspaceManager, NodeKindProvider, WorkspaceSymbolProvider } from './shared'
27
28
  import { registerValidationChecks } from './validation'
28
- import { LikeC4Formatter } from './formatting/LikeC4Formatter'
29
29
 
30
30
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
31
  type Constructor<T, Arguments extends unknown[] = any[]> = new(...arguments_: Arguments) => T
@@ -61,8 +61,10 @@ const LikeC4SharedModule: Module<
61
61
  export interface LikeC4AddedServices {
62
62
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
63
  WorkspaceCache: WorkspaceCache<string, any>
64
+ DocumentCache: DocumentCache<string, any>
64
65
  Rpc: Rpc
65
66
  likec4: {
67
+ DeploymentsIndex: DeploymentsIndex
66
68
  FqnIndex: FqnIndex
67
69
  ModelParser: LikeC4ModelParser
68
70
  ModelBuilder: LikeC4ModelBuilder
@@ -80,6 +82,7 @@ export interface LikeC4AddedServices {
80
82
  DocumentLinkProvider: LikeC4DocumentLinkProvider
81
83
  }
82
84
  references: {
85
+ NameProvider: LikeC4NameProvider
83
86
  ScopeComputation: LikeC4ScopeComputation
84
87
  ScopeProvider: LikeC4ScopeProvider
85
88
  }
@@ -94,8 +97,10 @@ function bind<T>(Type: Constructor<T, [LikeC4Services]>) {
94
97
 
95
98
  export const LikeC4Module: Module<LikeC4Services, PartialLangiumServices & LikeC4AddedServices> = {
96
99
  WorkspaceCache: (services: LikeC4Services) => new WorkspaceCache(services.shared),
100
+ DocumentCache: (services: LikeC4Services) => new DocumentCache(services.shared),
97
101
  Rpc: bind(Rpc),
98
102
  likec4: {
103
+ DeploymentsIndex: bind(DeploymentsIndex),
99
104
  ModelChanges: bind(LikeC4ModelChanges),
100
105
  FqnIndex: bind(FqnIndex),
101
106
  ModelParser: bind(LikeC4ModelParser),
@@ -114,6 +119,7 @@ export const LikeC4Module: Module<LikeC4Services, PartialLangiumServices & LikeC
114
119
  Formatter: bind(LikeC4Formatter)
115
120
  },
116
121
  references: {
122
+ NameProvider: bind(LikeC4NameProvider),
117
123
  ScopeComputation: bind(LikeC4ScopeComputation),
118
124
  ScopeProvider: bind(LikeC4ScopeProvider)
119
125
  }
package/src/protocol.ts CHANGED
@@ -3,9 +3,9 @@ import type {
3
3
  ComputedView,
4
4
  Fqn,
5
5
  ParsedLikeC4Model,
6
- RelationID,
6
+ RelationId,
7
7
  ViewChange,
8
- ViewID
8
+ ViewId
9
9
  } from '@likec4/core'
10
10
  import { NotificationType, RequestType, RequestType0 } from 'vscode-jsonrpc'
11
11
  import type { DocumentUri, Location } from 'vscode-languageserver-types'
@@ -30,7 +30,7 @@ export const fetchComputedModel = new RequestType<
30
30
  )
31
31
  export type FetchComputedModelRequest = typeof fetchComputedModel
32
32
 
33
- export const computeView = new RequestType<{ viewId: ViewID }, { view: ComputedView | null }, void>(
33
+ export const computeView = new RequestType<{ viewId: ViewId }, { view: ComputedView | null }, void>(
34
34
  'likec4/computeView'
35
35
  )
36
36
  export type ComputeViewRequest = typeof computeView
@@ -47,17 +47,21 @@ export type LocateParams =
47
47
  property?: string
48
48
  }
49
49
  | {
50
- relation: RelationID
50
+ relation: RelationId
51
51
  }
52
52
  | {
53
- view: ViewID
53
+ deployment: Fqn
54
+ property?: string
55
+ }
56
+ | {
57
+ view: ViewId
54
58
  }
55
59
  export const locate = new RequestType<LocateParams, Location | null, void>('likec4/locate')
56
60
  export type LocateRequest = typeof locate
57
61
  // #endregion
58
62
 
59
63
  export interface ChangeViewRequestParams {
60
- viewId: ViewID
64
+ viewId: ViewId
61
65
  change: ViewChange
62
66
  }
63
67
  export const changeView = new RequestType<ChangeViewRequestParams, Location | null, void>('likec4/change-view')
@@ -1,2 +1,3 @@
1
+ export * from './name-provider'
1
2
  export * from './scope-computation'
2
3
  export * from './scope-provider'
@@ -0,0 +1,37 @@
1
+ import { nonNullable } from '@likec4/core'
2
+ import { type AstNode, type CstNode, DefaultNameProvider, isNamed, type NamedAstNode } from 'langium'
3
+ import { ast } from '../ast'
4
+ import type { LikeC4Services } from '../module'
5
+
6
+ export class LikeC4NameProvider extends DefaultNameProvider {
7
+ constructor(protected services: LikeC4Services) {
8
+ super()
9
+ }
10
+
11
+ public getNameStrict(node: AstNode): string {
12
+ return nonNullable(
13
+ this.getName(node),
14
+ `Failed getName for ${this.services.workspace.AstNodeLocator.getAstNodePath(node)}`
15
+ )
16
+ }
17
+
18
+ override getName(node: AstNode): string | undefined {
19
+ if (isNamed(node)) {
20
+ return node.name
21
+ }
22
+ if (ast.isDeployedInstance(node)) {
23
+ return node.element.el.$refText
24
+ }
25
+ return undefined
26
+ }
27
+
28
+ override getNameNode(node: AstNode): CstNode | undefined {
29
+ if (isNamed(node)) {
30
+ return super.getNameNode(node)
31
+ }
32
+ if (ast.isDeployedInstance(node)) {
33
+ return node.element.el.$refNode
34
+ }
35
+ return undefined
36
+ }
37
+ }
@@ -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,
@@ -16,20 +16,22 @@ import {
16
16
  StreamImpl,
17
17
  StreamScope
18
18
  } from 'langium'
19
- import { ast } from '../ast'
20
- import { elementRef, getFqnElementRef } from '../elementRef'
19
+ import { ast, isLikeC4LangiumDocument } from '../ast'
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.isDeploymentRef(container)) {
95
+ return this.getScopeForDeploymentRef(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,62 @@ 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 getScopeForDeploymentRef(container: ast.DeploymentRef, context: ReferenceInfo) {
138
+ const parent = container.parent
139
+ if (!parent) {
140
+ return new MapScope(
141
+ // First preference for deployment nodes
142
+ this.computeScope(context, ast.DeploymentNode).getAllElements(),
143
+ // Second preference for deployed instances
144
+ this.computeScope(context, ast.DeployedInstance)
145
+ )
146
+ }
147
+ const parentRef = parent.value.ref
148
+ if (!parentRef) {
149
+ return EMPTY_SCOPE
150
+ }
151
+ if (ast.isDeploymentNode(parentRef)) {
152
+ return new StreamScope(this.deploymentsIndex.nested(parentRef))
153
+ }
154
+ if (ast.isDeployedInstance(parentRef)) {
155
+ return new StreamScope(this.scopeElementRef(parentRef.element))
156
+ }
157
+ if (ast.isElement(parentRef)) {
158
+ return new StreamScope(this.uniqueDescedants(() => parentRef))
159
+ }
160
+ return nonexhaustive(parentRef)
161
+ }
162
+
163
+ protected computeScope(context: ReferenceInfo, referenceType = this.reflection.getReferenceType(context)) {
164
+ const isElementReference = this.reflection.isSubtype(referenceType, ast.Element)
165
+
134
166
  const scopes: Stream<AstNodeDescription>[] = []
135
167
  const doc = getDocument(context.container)
136
168
  const precomputed = doc.precomputedScopes
137
169
 
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
- }
170
+ if (!precomputed) {
171
+ return this.getGlobalScope(referenceType, context)
172
+ }
146
173
 
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
174
+ const byReferenceType = (desc: AstNodeDescription) => this.reflection.isSubtype(desc.type, referenceType)
175
+ let container: AstNode | undefined = context.container
176
+ while (container) {
177
+ const elements = precomputed.get(container).filter(byReferenceType)
178
+ if (elements.length > 0) {
179
+ scopes.push(stream(elements))
154
180
  }
181
+
182
+ if (isElementReference && ast.isExtendElementBody(container)) {
183
+ scopes.push(this.scopeExtendElement(container.$container))
184
+ }
185
+ if (isElementReference && ast.isElementViewBody(container)) {
186
+ scopes.push(this.scopeElementView(container.$container))
187
+ }
188
+ container = container.$container
155
189
  }
156
190
 
157
191
  return scopes.reduceRight((outerScope, elements) => {
158
192
  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))
193
+ }, this.getGlobalScope(referenceType, context))
167
194
  }
168
195
  }
@@ -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
 
@@ -0,0 +1,31 @@
1
+ import { AstUtils } from 'langium'
2
+ import { isNullish } from 'remeda'
3
+ import { ast } from '../ast'
4
+
5
+ export function instanceRef(deploymentRef: ast.DeploymentRef): 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.DeploymentRef): 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
+ }
@@ -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
  */
@@ -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
  }