@likec4/language-server 1.19.0 → 1.19.1

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 (59) hide show
  1. package/dist/Rpc.js +6 -0
  2. package/dist/browser.d.ts +1 -0
  3. package/dist/documentation/documentation-provider.d.ts +8 -0
  4. package/dist/documentation/documentation-provider.js +46 -0
  5. package/dist/documentation/index.d.ts +1 -0
  6. package/dist/documentation/index.js +1 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +3 -2
  9. package/dist/lsp/DocumentSymbolProvider.d.ts +11 -2
  10. package/dist/lsp/DocumentSymbolProvider.js +78 -6
  11. package/dist/model/fqn-computation.js +2 -2
  12. package/dist/model/model-builder.js +3 -3
  13. package/dist/module.d.ts +9 -1
  14. package/dist/module.js +47 -8
  15. package/dist/protocol.d.ts +19 -1
  16. package/dist/protocol.js +1 -0
  17. package/dist/references/scope-computation.d.ts +2 -2
  18. package/dist/references/scope-computation.js +9 -9
  19. package/dist/validation/dynamic-view-rule.js +3 -2
  20. package/dist/validation/dynamic-view-step.js +23 -27
  21. package/dist/validation/property-checks.js +3 -2
  22. package/dist/validation/specification.js +14 -14
  23. package/dist/validation/view-predicates/element-with.js +3 -2
  24. package/dist/validation/view-predicates/expanded-element.js +3 -2
  25. package/dist/validation/view-predicates/incoming.js +3 -2
  26. package/dist/validation/view-predicates/outgoing.js +3 -2
  27. package/dist/validation/view-predicates/relation-with.js +3 -2
  28. package/dist/validation/view.js +3 -3
  29. package/dist/views/configurable-layouter.d.ts +7 -0
  30. package/dist/views/configurable-layouter.js +55 -0
  31. package/dist/views/index.d.ts +1 -0
  32. package/dist/views/index.js +1 -0
  33. package/dist/views/likec4-views.d.ts +26 -0
  34. package/dist/views/likec4-views.js +113 -0
  35. package/package.json +13 -10
  36. package/src/Rpc.ts +13 -7
  37. package/src/browser.ts +1 -0
  38. package/src/documentation/documentation-provider.ts +52 -0
  39. package/src/documentation/index.ts +1 -0
  40. package/src/index.ts +4 -2
  41. package/src/lsp/DocumentSymbolProvider.ts +110 -28
  42. package/src/model/fqn-computation.ts +8 -8
  43. package/src/model/model-builder.ts +52 -52
  44. package/src/module.ts +56 -9
  45. package/src/protocol.ts +29 -4
  46. package/src/references/scope-computation.ts +35 -35
  47. package/src/validation/dynamic-view-rule.ts +5 -4
  48. package/src/validation/dynamic-view-step.ts +23 -26
  49. package/src/validation/property-checks.ts +11 -10
  50. package/src/validation/specification.ts +38 -38
  51. package/src/validation/view-predicates/element-with.ts +6 -5
  52. package/src/validation/view-predicates/expanded-element.ts +6 -5
  53. package/src/validation/view-predicates/incoming.ts +6 -5
  54. package/src/validation/view-predicates/outgoing.ts +6 -5
  55. package/src/validation/view-predicates/relation-with.ts +6 -5
  56. package/src/validation/view.ts +5 -5
  57. package/src/views/configurable-layouter.ts +65 -0
  58. package/src/views/index.ts +1 -0
  59. package/src/views/likec4-views.ts +139 -0
@@ -0,0 +1,52 @@
1
+ import { type AstNode, type DocumentationProvider, AstUtils } from 'langium'
2
+ import { ast } from '../ast'
3
+ import { logWarnError } from '../logger'
4
+ import type { LikeC4ModelLocator, LikeC4ModelParser } from '../model'
5
+ import type { LikeC4Services } from '../module'
6
+
7
+ export class LikeC4DocumentationProvider implements DocumentationProvider {
8
+ private parser: LikeC4ModelParser
9
+ private locator: LikeC4ModelLocator
10
+
11
+ constructor(services: LikeC4Services) {
12
+ this.parser = services.likec4.ModelParser
13
+ this.locator = services.likec4.ModelLocator
14
+ }
15
+ getDocumentation(node: AstNode): string | undefined {
16
+ try {
17
+ if (ast.isDeploymentNode(node)) {
18
+ const doc = AstUtils.getDocument(node)
19
+ const el = this.parser.forDocument(doc).parseDeploymentNode(node)
20
+ const lines = [el.id as string]
21
+ if (el.title !== node.name) {
22
+ lines.push(' ', `**${el.title}**`)
23
+ }
24
+ return lines.join(' \n')
25
+ }
26
+
27
+ if (ast.isDeployedInstance(node)) {
28
+ const doc = AstUtils.getDocument(node)
29
+ const instance = this.parser.forDocument(doc).parseDeployedInstance(node)
30
+ const el = this.locator.getParsedElement(instance.element)
31
+ const lines = [instance.id, `_instance of_ ${instance.element}`]
32
+ if (el) {
33
+ lines.push(' ', `**${el.title}**`)
34
+ }
35
+ return lines.join(' \n')
36
+ }
37
+
38
+ if (ast.isElement(node)) {
39
+ const doc = AstUtils.getDocument(node)
40
+ const el = this.parser.forDocument(doc).parseElement(node)
41
+ if (!el) {
42
+ return
43
+ }
44
+ const lines = [el.id, ' ', `**${el.title}**`]
45
+ return lines.join(' \n')
46
+ }
47
+ } catch (e) {
48
+ logWarnError(e)
49
+ }
50
+ return
51
+ }
52
+ }
@@ -0,0 +1 @@
1
+ export { LikeC4DocumentationProvider } from './documentation-provider'
package/src/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { startLanguageServer as startLanguim } from 'langium/lsp'
2
2
  import { createConnection, ProposedFeatures } from 'vscode-languageserver/node'
3
3
  import { LikeC4FileSystem } from './LikeC4FileSystem'
4
- import { createLanguageServices, type LikeC4Services, type LikeC4SharedServices } from './module'
4
+ import { type LikeC4Services, type LikeC4SharedServices, createCustomLanguageServices } from './module'
5
+ import { ConfigurableLayouter } from './views/configurable-layouter'
5
6
 
6
7
  export { logger as lspLogger, setLogLevel } from './logger'
7
8
 
@@ -9,6 +10,7 @@ export type { DocumentParser, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4Mode
9
10
 
10
11
  export { createCustomLanguageServices, createLanguageServices, LikeC4Module } from './module'
11
12
  export type { LikeC4Services, LikeC4SharedServices } from './module'
13
+ export type { LikeC4Views } from './views'
12
14
  export { LikeC4FileSystem }
13
15
 
14
16
  export function startLanguageServer(): {
@@ -19,7 +21,7 @@ export function startLanguageServer(): {
19
21
  const connection = createConnection(ProposedFeatures.all)
20
22
 
21
23
  // Inject the shared services and language-specific services
22
- const services = createLanguageServices({ connection, ...LikeC4FileSystem })
24
+ const services = createCustomLanguageServices({ connection, ...LikeC4FileSystem }, ConfigurableLayouter)
23
25
 
24
26
  // Start the language server with the shared services
25
27
  startLanguim(services.shared)
@@ -1,35 +1,44 @@
1
1
  import { nonexhaustive } from '@likec4/core'
2
- import { type AstNode, GrammarUtils, type MaybePromise } from 'langium'
2
+ import { type AstNode, type MaybePromise, AstUtils, GrammarUtils } from 'langium'
3
3
  import type { DocumentSymbolProvider, NodeKindProvider } from 'langium/lsp'
4
4
  import { filter, isEmpty, isTruthy, map, pipe } from 'remeda'
5
5
  import { type DocumentSymbol, SymbolKind } from 'vscode-languageserver-types'
6
- import { ast, type LikeC4LangiumDocument } from '../ast'
7
- import { logError } from '../logger'
6
+ import { type LikeC4LangiumDocument, ast } from '../ast'
7
+ import { logError, logWarnError } from '../logger'
8
+ import type { LikeC4ModelLocator, LikeC4ModelParser } from '../model'
8
9
  import type { LikeC4Services } from '../module'
10
+ import type { LikeC4NameProvider } from '../references'
9
11
  import { getFqnElementRef } from '../utils/elementRef'
10
12
 
11
13
  export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
12
14
  protected readonly nodeKindProvider: NodeKindProvider
15
+ protected readonly nameProvider: LikeC4NameProvider
16
+ protected readonly parser: LikeC4ModelParser
17
+ protected readonly locator: LikeC4ModelLocator
13
18
 
14
19
  constructor(private services: LikeC4Services) {
15
20
  this.nodeKindProvider = services.shared.lsp.NodeKindProvider
21
+ this.parser = services.likec4.ModelParser
22
+ this.locator = services.likec4.ModelLocator
23
+ this.nameProvider = services.references.NameProvider
16
24
  }
17
25
 
18
26
  getSymbols({
19
27
  parseResult: {
20
- value: { specifications, models, views, likec4lib }
21
- }
28
+ value: { specifications, models, deployments, views, likec4lib },
29
+ },
22
30
  }: LikeC4LangiumDocument): MaybePromise<DocumentSymbol[]> {
23
31
  return [
24
32
  ...likec4lib.map(l => () => this.getLikec4LibSymbol(l)),
25
33
  ...specifications.map(s => () => this.getSpecSymbol(s)),
26
34
  ...models.map(s => () => this.getModelSymbol(s)),
27
- ...views.map(s => () => this.getModelViewsSymbol(s))
35
+ ...deployments.map(s => () => this.getDeploymentModelSymbol(s)),
36
+ ...views.map(s => () => this.getModelViewsSymbol(s)),
28
37
  ].flatMap(fn => {
29
38
  try {
30
39
  return fn() ?? []
31
40
  } catch (e) {
32
- logError(e)
41
+ logWarnError(e)
33
42
  return []
34
43
  }
35
44
  })
@@ -46,8 +55,8 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
46
55
  name: 'icons',
47
56
  range: cstModel.range,
48
57
  selectionRange: GrammarUtils.findNodeForKeyword(cstModel, 'icons')?.range ?? cstModel.range,
49
- children
50
- }
58
+ children,
59
+ },
51
60
  ]
52
61
  }
53
62
 
@@ -71,12 +80,12 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
71
80
  return this.getTagSymbol(nd)
72
81
  }
73
82
  } catch (e) {
74
- logError(e)
83
+ logWarnError(e)
75
84
  return null
76
85
  }
77
86
  nonexhaustive(nd)
78
87
  }),
79
- filter(isTruthy)
88
+ filter(isTruthy),
80
89
  )
81
90
 
82
91
  if (specSymbols.length === 0) return []
@@ -87,8 +96,8 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
87
96
  name: astSpec.name,
88
97
  range: cstModel.range,
89
98
  selectionRange: specKeywordNode.range,
90
- children: specSymbols
91
- }
99
+ children: specSymbols,
100
+ },
92
101
  ]
93
102
  }
94
103
 
@@ -103,13 +112,29 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
103
112
  name: astModel.name,
104
113
  range: cstModel.range,
105
114
  selectionRange: nameNode.range,
106
- children: astModel.elements.flatMap(e => this.getElementsSymbol(e))
107
- }
115
+ children: astModel.elements.flatMap(e => this.getElementsSymbol(e)),
116
+ },
117
+ ]
118
+ }
119
+
120
+ protected getDeploymentModelSymbol(astModel: ast.ModelDeployments): DocumentSymbol[] {
121
+ const cstModel = astModel.$cstNode
122
+ if (!cstModel) return []
123
+ const nameNode = GrammarUtils.findNodeForProperty(cstModel, 'name')
124
+ if (!nameNode) return []
125
+ return [
126
+ {
127
+ kind: this.symbolKind(astModel),
128
+ name: astModel.name,
129
+ range: cstModel.range,
130
+ selectionRange: nameNode.range,
131
+ children: astModel.elements.flatMap(e => this.getDeploymentElementSymbol(e)),
132
+ },
108
133
  ]
109
134
  }
110
135
 
111
136
  protected getElementsSymbol(
112
- el: ast.Element | ast.Relation | ast.ExtendElement
137
+ el: ast.Element | ast.Relation | ast.ExtendElement,
113
138
  ): DocumentSymbol[] {
114
139
  try {
115
140
  if (ast.isExtendElement(el)) {
@@ -119,7 +144,7 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
119
144
  return this.getElementSymbol(el)
120
145
  }
121
146
  } catch (e) {
122
- logError(e)
147
+ logWarnError(e)
123
148
  }
124
149
  return []
125
150
  }
@@ -136,8 +161,8 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
136
161
  name: getFqnElementRef(astElement.element),
137
162
  range: cst.range,
138
163
  selectionRange: nameNode.range,
139
- children: body.elements.flatMap(e => this.getElementsSymbol(e))
140
- }
164
+ children: body.elements.flatMap(e => this.getElementsSymbol(e)),
165
+ },
141
166
  ]
142
167
  }
143
168
 
@@ -157,8 +182,8 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
157
182
  range: cst.range,
158
183
  selectionRange: nameNode.range,
159
184
  detail,
160
- children: astElement.body?.elements.flatMap(e => this.getElementsSymbol(e)) ?? []
161
- }
185
+ children: astElement.body?.elements.flatMap(e => this.getElementsSymbol(e)) ?? [],
186
+ },
162
187
  ]
163
188
  }
164
189
  protected getModelViewsSymbol(astViews: ast.ModelViews): DocumentSymbol[] {
@@ -171,13 +196,13 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
171
196
  name: astViews.name,
172
197
  range: cst.range,
173
198
  selectionRange: nameNode.range,
174
- children: astViews.views.flatMap(e => this.getViewSymbol(e))
175
- }
199
+ children: astViews.views.flatMap(e => this.getViewSymbol(e)),
200
+ },
176
201
  ]
177
202
  }
178
203
 
179
204
  protected getKindSymbol(
180
- astKind: ast.SpecificationElementKind | ast.SpecificationRelationshipKind
205
+ astKind: ast.SpecificationElementKind | ast.SpecificationRelationshipKind,
181
206
  ): DocumentSymbol | null {
182
207
  if (!astKind.$cstNode || !astKind.kind.$cstNode || isEmpty(astKind.kind.name)) return null
183
208
 
@@ -185,7 +210,7 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
185
210
  kind: this.symbolKind(astKind),
186
211
  name: astKind.kind.name,
187
212
  range: astKind.$cstNode.range,
188
- selectionRange: astKind.kind.$cstNode.range
213
+ selectionRange: astKind.kind.$cstNode.range,
189
214
  }
190
215
  }
191
216
 
@@ -195,7 +220,7 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
195
220
  kind: this.symbolKind(astTag),
196
221
  name: '#' + astTag.tag.name,
197
222
  range: astTag.$cstNode.range,
198
- selectionRange: astTag.tag.$cstNode.range
223
+ selectionRange: astTag.tag.$cstNode.range,
199
224
  }
200
225
  }
201
226
 
@@ -205,7 +230,7 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
205
230
  kind: this.symbolKind(astTag),
206
231
  name: astTag.name,
207
232
  range: astTag.$cstNode.range,
208
- selectionRange: astTag.$cstNode.range
233
+ selectionRange: astTag.$cstNode.range,
209
234
  }
210
235
  }
211
236
 
@@ -220,8 +245,65 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
220
245
  name: nameNode.text,
221
246
  range: cst.range,
222
247
  selectionRange: nameNode.range,
223
- children: []
248
+ children: [],
249
+ },
250
+ ]
251
+ }
252
+
253
+ protected getDeploymentElementSymbol(el: ast.DeploymentElement | ast.DeploymentRelation): DocumentSymbol[] {
254
+ try {
255
+ if (ast.isDeploymentNode(el)) {
256
+ return this.getDeploymentNodeSymbol(el)
224
257
  }
258
+ if (ast.isDeployedInstance(el)) {
259
+ return this.getDeployedInstanceSymbol(el)
260
+ }
261
+ } catch (e) {
262
+ logWarnError(e)
263
+ }
264
+ return []
265
+ }
266
+
267
+ protected getDeploymentNodeSymbol(astElement: ast.DeploymentNode): DocumentSymbol[] {
268
+ const cst = astElement.$cstNode
269
+ const nameNode = this.nameProvider.getNameNode(astElement)
270
+ if (!nameNode || !cst) return []
271
+
272
+ const name = this.nameProvider.getNameStrict(astElement)
273
+ const kind = astElement.kind.$refText
274
+ // TODO: return the title as well
275
+ const detail = kind // + (astElement.title ? ': ' + astElement.title : '').replaceAll('\n', ' ').trim()
276
+ return [
277
+ {
278
+ kind: this.symbolKind(astElement),
279
+ name: name,
280
+ range: cst.range,
281
+ selectionRange: nameNode.range,
282
+ detail,
283
+ children: astElement.body?.elements.flatMap(e => this.getDeploymentElementSymbol(e)) ?? [],
284
+ },
285
+ ]
286
+ }
287
+
288
+ protected getDeployedInstanceSymbol(astElement: ast.DeployedInstance): DocumentSymbol[] {
289
+ const cst = astElement.$cstNode
290
+ const nameNode = this.nameProvider.getNameNode(astElement)
291
+ if (!nameNode || !cst) return []
292
+
293
+ const doc = AstUtils.getDocument(astElement)
294
+ const instance = this.parser.forDocument(doc).parseDeployedInstance(astElement)
295
+
296
+ const name = this.nameProvider.getNameStrict(astElement)
297
+ const detail = 'instance of ' + instance.element
298
+ return [
299
+ {
300
+ kind: this.symbolKind(astElement),
301
+ name: name,
302
+ range: cst.range,
303
+ selectionRange: nameNode.range,
304
+ detail,
305
+ children: [],
306
+ },
225
307
  ]
226
308
  }
227
309
 
@@ -1,8 +1,8 @@
1
- import { AsFqn, nonexhaustive } from '@likec4/core'
2
1
  import type * as c4 from '@likec4/core'
2
+ import { AsFqn, LinkedList, nonexhaustive } from '@likec4/core'
3
3
  import { type AstNodeDescription, type AstNodeLocator, AstUtils, CstUtils, GrammarUtils, MultiMap } from 'langium'
4
4
  import { isDefined, isEmpty } from 'remeda'
5
- import { ast, ElementOps, type LikeC4LangiumDocument } from '../ast'
5
+ import { type LikeC4LangiumDocument, ast, ElementOps } from '../ast'
6
6
  import { logError } from '../logger'
7
7
  import type { LikeC4Services } from '../module'
8
8
  import { getFqnElementRef } from '../utils/elementRef'
@@ -16,20 +16,20 @@ type TraversePair = [el: ast.Element | ast.ExtendElement | ast.Relation, parent:
16
16
  function toAstNodeDescription(
17
17
  locator: AstNodeLocator,
18
18
  entry: ast.Element,
19
- doc: LikeC4LangiumDocument
19
+ doc: LikeC4LangiumDocument,
20
20
  ): AstNodeDescription {
21
21
  const $cstNode = findNodeForProperty(entry.$cstNode, 'name')
22
22
  return {
23
23
  documentUri: doc.uri,
24
24
  name: entry.name,
25
25
  ...(entry.$cstNode && {
26
- selectionSegment: toDocumentSegment(entry.$cstNode)
26
+ selectionSegment: toDocumentSegment(entry.$cstNode),
27
27
  }),
28
28
  ...($cstNode && {
29
- nameSegment: toDocumentSegment($cstNode)
29
+ nameSegment: toDocumentSegment($cstNode),
30
30
  }),
31
31
  path: locator.getAstNodePath(entry),
32
- type: ast.Element
32
+ type: ast.Element,
33
33
  }
34
34
  }
35
35
 
@@ -40,7 +40,7 @@ export function computeDocumentFqn(document: LikeC4LangiumDocument, services: Li
40
40
  return
41
41
  }
42
42
  const locator = services.workspace.AstNodeLocator
43
- const traverseStack: TraversePair[] = elements.map(el => [el, null])
43
+ const traverseStack = LinkedList.from(elements.map(el => [el, null] as TraversePair))
44
44
  let pair
45
45
  while ((pair = traverseStack.shift())) {
46
46
  try {
@@ -63,7 +63,7 @@ export function computeDocumentFqn(document: LikeC4LangiumDocument, services: Li
63
63
  const fqn = AsFqn(el.name, parent)
64
64
  c4fqnIndex.add(fqn, {
65
65
  ...toAstNodeDescription(locator, el, document),
66
- fqn
66
+ fqn,
67
67
  })
68
68
  ElementOps.writeId(el, fqn)
69
69
  if (isDefined(el.body) && !isEmpty(el.body.elements)) {