@likec4/language-server 1.20.1 → 1.20.2

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 (53) hide show
  1. package/README.md +19 -0
  2. package/bin/likec4-language-server.mjs +5 -0
  3. package/dist/LikeC4FileSystem.js +9 -9
  4. package/dist/Rpc.d.ts +2 -4
  5. package/dist/Rpc.js +27 -36
  6. package/dist/ast.d.ts +1 -0
  7. package/dist/ast.js +5 -1
  8. package/dist/bundled.mjs +5924 -0
  9. package/dist/formatting/LikeC4Formatter.d.ts +9 -0
  10. package/dist/formatting/LikeC4Formatter.js +131 -14
  11. package/dist/generated/ast.d.ts +13 -2
  12. package/dist/generated/ast.js +18 -1
  13. package/dist/generated/grammar.js +1 -1
  14. package/dist/lsp/CompletionProvider.js +11 -3
  15. package/dist/model/deployments-index.d.ts +2 -1
  16. package/dist/model/deployments-index.js +3 -10
  17. package/dist/model/fqn-index.d.ts +2 -1
  18. package/dist/model/fqn-index.js +24 -17
  19. package/dist/model/model-builder.d.ts +2 -1
  20. package/dist/model/model-builder.js +32 -30
  21. package/dist/model/model-parser.d.ts +1 -1
  22. package/dist/model/model-parser.js +9 -6
  23. package/dist/model/parser/PredicatesParser.js +7 -1
  24. package/dist/utils/disposable.d.ts +8 -0
  25. package/dist/utils/disposable.js +25 -0
  26. package/dist/utils/index.d.ts +1 -0
  27. package/dist/utils/index.js +1 -0
  28. package/dist/validation/_shared.js +4 -1
  29. package/dist/validation/index.d.ts +2 -2
  30. package/dist/validation/index.js +4 -1
  31. package/dist/validation/specification.d.ts +1 -0
  32. package/dist/validation/specification.js +30 -0
  33. package/package.json +33 -27
  34. package/src/LikeC4FileSystem.ts +14 -13
  35. package/src/Rpc.ts +28 -38
  36. package/src/ast.ts +6 -1
  37. package/src/formatting/LikeC4Formatter.ts +198 -17
  38. package/src/generated/ast.ts +35 -2
  39. package/src/generated/grammar.ts +1 -1
  40. package/src/like-c4.langium +14 -3
  41. package/src/lsp/CompletionProvider.ts +27 -18
  42. package/src/model/deployments-index.ts +4 -17
  43. package/src/model/fqn-index.ts +26 -19
  44. package/src/model/model-builder.ts +32 -31
  45. package/src/model/model-parser.ts +14 -11
  46. package/src/model/parser/PredicatesParser.ts +30 -24
  47. package/src/utils/disposable.ts +30 -0
  48. package/src/utils/index.ts +1 -0
  49. package/src/validation/_shared.ts +5 -2
  50. package/src/validation/index.ts +6 -2
  51. package/src/validation/specification.ts +34 -0
  52. package/contrib/likec4.tmLanguage.json +0 -73
  53. package/dist/like-c4.langium +0 -852
@@ -0,0 +1,30 @@
1
+ import { Disposable } from 'langium'
2
+ import { logError } from '../logger'
3
+
4
+ export abstract class ADisposable implements Disposable {
5
+ protected toDispose: Disposable[] = []
6
+ protected isDisposed = false
7
+
8
+ onDispose(...disposable: Disposable[]): void {
9
+ this.toDispose.push(...disposable)
10
+ }
11
+
12
+ dispose(): void {
13
+ this.throwIfDisposed()
14
+ this.isDisposed = true
15
+ let item: Disposable | undefined
16
+ while (item = this.toDispose.pop()) {
17
+ try {
18
+ item.dispose()
19
+ } catch (e) {
20
+ logError(e)
21
+ }
22
+ }
23
+ }
24
+
25
+ protected throwIfDisposed(): void {
26
+ if (this.isDisposed) {
27
+ throw new Error('This has already been disposed')
28
+ }
29
+ }
30
+ }
@@ -1 +1,2 @@
1
+ export * from './disposable'
1
2
  export * from './stringHash'
@@ -9,17 +9,20 @@ export const RESERVED_WORDS = [
9
9
  'self',
10
10
  'super',
11
11
  'likec4lib',
12
- 'global'
12
+ 'global',
13
13
  ]
14
14
 
15
15
  export function tryOrLog<T extends AstNode>(fn: ValidationCheck<T>): ValidationCheck<T> {
16
- return async (node: T, accept: ValidationAcceptor, cancelToken: CancellationToken) => {
16
+ return async function tryOrLogFn(node: T, accept: ValidationAcceptor, cancelToken: CancellationToken) {
17
17
  try {
18
18
  const result = fn(node, accept, cancelToken)
19
19
  if (isPromise(result)) {
20
20
  await result
21
21
  }
22
+ return
22
23
  } catch (e) {
24
+ const message = e instanceof Error ? e.message : String(e)
25
+ accept('error', `Validation failed: ${message}`, { node })
23
26
  logWarnError(e)
24
27
  }
25
28
  }
@@ -1,4 +1,4 @@
1
- import type { AstNode } from 'langium'
1
+ import { type AstNode, DocumentState } from 'langium'
2
2
  import { isNullish } from 'remeda'
3
3
  import { DiagnosticSeverity } from 'vscode-languageserver-types'
4
4
  import { type LikeC4AstNode, type LikeC4LangiumDocument, ast } from '../ast'
@@ -11,6 +11,7 @@ import { elementChecks } from './element'
11
11
  import { iconPropertyRuleChecks, notesPropertyRuleChecks, opacityPropertyRuleChecks } from './property-checks'
12
12
  import { relationBodyChecks, relationChecks } from './relation'
13
13
  import {
14
+ deploymentNodeKindChecks,
14
15
  elementKindChecks,
15
16
  globalPredicateChecks,
16
17
  globalsChecks,
@@ -97,7 +98,9 @@ const findInvalidContainer = (node: LikeC4AstNode): ValidatableAstNode | undefin
97
98
  }
98
99
 
99
100
  export function checksFromDiagnostics(doc: LikeC4LangiumDocument) {
100
- const errors = doc.diagnostics?.filter(d => d.severity === DiagnosticSeverity.Error) ?? []
101
+ const errors = doc.state >= DocumentState.Validated
102
+ ? (doc.diagnostics?.filter(d => d.severity === DiagnosticSeverity.Error) ?? [])
103
+ : []
101
104
  const invalidNodes = new WeakSet()
102
105
  for (const { node } of errors) {
103
106
  if (isNullish(node) || invalidNodes.has(node)) {
@@ -123,6 +126,7 @@ export function registerValidationChecks(services: LikeC4Services) {
123
126
  const registry = services.validation.ValidationRegistry
124
127
  registry.register<ast.LikeC4AstType>({
125
128
  DeployedInstance: deployedInstanceChecks(services),
129
+ DeploymentNodeKind: deploymentNodeKindChecks(services),
126
130
  DeploymentNode: deploymentNodeChecks(services),
127
131
  DeploymentRelation: deploymentRelationChecks(services),
128
132
  FqnRefExpr: fqnRefExprChecks(services),
@@ -72,6 +72,40 @@ export const elementKindChecks = (services: LikeC4Services): ValidationCheck<ast
72
72
  })
73
73
  }
74
74
 
75
+ export const deploymentNodeKindChecks = (services: LikeC4Services): ValidationCheck<ast.DeploymentNodeKind> => {
76
+ const index = services.shared.workspace.IndexManager
77
+ return tryOrLog((node, accept) => {
78
+ if (RESERVED_WORDS.includes(node.name)) {
79
+ accept('error', `Reserved word: ${node.name}`, {
80
+ node: node,
81
+ property: 'name',
82
+ })
83
+ }
84
+ const sameKind = index
85
+ .allElements(ast.DeploymentNodeKind)
86
+ .filter(n => n.name === node.name && n.node !== node)
87
+ .head()
88
+ if (sameKind) {
89
+ const isAnotherDoc = sameKind.documentUri !== AstUtils.getDocument(node).uri
90
+ accept('error', `Duplicate deploymentNode kind '${node.name}'`, {
91
+ node: node,
92
+ property: 'name',
93
+ ...isAnotherDoc && {
94
+ relatedInformation: [
95
+ {
96
+ location: {
97
+ range: sameKind.nameSegment!.range,
98
+ uri: sameKind.documentUri.toString(),
99
+ },
100
+ message: `conflicting definition`,
101
+ },
102
+ ],
103
+ },
104
+ })
105
+ }
106
+ })
107
+ }
108
+
75
109
  export const tagChecks = (services: LikeC4Services): ValidationCheck<ast.Tag> => {
76
110
  const index = services.shared.workspace.IndexManager
77
111
  return tryOrLog((node, accept) => {
@@ -1,73 +0,0 @@
1
- {
2
- "name": "likec4",
3
- "scopeName": "source.likec4",
4
- "fileTypes": [
5
- ".c4",
6
- ".likec4",
7
- ".like-c4"
8
- ],
9
- "patterns": [
10
- {
11
- "include": "#comments"
12
- },
13
- {
14
- "name": "keyword.control.likec4",
15
- "match": "\\b(BottomTop|LeftRight|RightLeft|TopBottom|amber|and|autoLayout|blue|border|browser|color|crow|cylinder|dashed|deployment|deploymentNode|description|diamond|dot|dotted|dynamic|dynamicPredicateGroup|element|element\\.kind|element\\.tag|exclude|extend|extends|global|gray|green|group|head|icon|icons|include|indigo|instance|instanceOf|is|kind|likec4lib|line|link|metadata|mobile|model|muted|navigateTo|node|none|normal|not|notation|notes|odiamond|odot|of|onormal|opacity|open|or|par|parallel|person|predicate|predicateGroup|primary|queue|rectangle|red|relationship|secondary|shape|sky|slate|solid|source|specification|storage|style|styleGroup|tag|tail|target|technology|title|vee|view|views|where|with)\\b"
16
- },
17
- {
18
- "name": "string.quoted.double.likec4",
19
- "begin": "\"",
20
- "end": "\"",
21
- "patterns": [
22
- {
23
- "include": "#string-character-escape"
24
- }
25
- ]
26
- },
27
- {
28
- "name": "string.quoted.single.likec4",
29
- "begin": "'",
30
- "end": "'",
31
- "patterns": [
32
- {
33
- "include": "#string-character-escape"
34
- }
35
- ]
36
- }
37
- ],
38
- "repository": {
39
- "comments": {
40
- "patterns": [
41
- {
42
- "name": "comment.block.likec4",
43
- "begin": "/\\*",
44
- "beginCaptures": {
45
- "0": {
46
- "name": "punctuation.definition.comment.likec4"
47
- }
48
- },
49
- "end": "\\*/",
50
- "endCaptures": {
51
- "0": {
52
- "name": "punctuation.definition.comment.likec4"
53
- }
54
- }
55
- },
56
- {
57
- "begin": "//",
58
- "beginCaptures": {
59
- "1": {
60
- "name": "punctuation.whitespace.comment.leading.likec4"
61
- }
62
- },
63
- "end": "(?=$)",
64
- "name": "comment.line.likec4"
65
- }
66
- ]
67
- },
68
- "string-character-escape": {
69
- "name": "constant.character.escape.likec4",
70
- "match": "\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|u\\{[0-9A-Fa-f]+\\}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.|$)"
71
- }
72
- }
73
- }