@likec4/language-server 1.2.0 → 1.2.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 (173) hide show
  1. package/package.json +8 -10
  2. package/src/Rpc.ts +108 -0
  3. package/src/ast.ts +443 -0
  4. package/src/browser/index.ts +30 -0
  5. package/src/elementRef.ts +26 -0
  6. package/src/generated/ast.ts +1632 -0
  7. package/src/generated/grammar.ts +10 -0
  8. package/src/generated/module.ts +32 -0
  9. package/src/index.ts +4 -0
  10. package/src/like-c4.langium +395 -0
  11. package/src/logger.ts +54 -0
  12. package/src/lsp/CodeLensProvider.ts +51 -0
  13. package/src/lsp/DocumentHighlightProvider.ts +12 -0
  14. package/src/lsp/DocumentLinkProvider.test.ts +66 -0
  15. package/src/lsp/DocumentLinkProvider.ts +53 -0
  16. package/src/lsp/DocumentSymbolProvider.ts +201 -0
  17. package/src/lsp/HoverProvider.ts +58 -0
  18. package/{dist/lsp/SemanticTokenProvider.js → src/lsp/SemanticTokenProvider.ts} +57 -42
  19. package/src/lsp/index.ts +6 -0
  20. package/src/model/fqn-computation.ts +47 -0
  21. package/src/model/fqn-index.ts +161 -0
  22. package/src/model/index.ts +5 -0
  23. package/src/model/model-builder.ts +447 -0
  24. package/src/model/model-locator.ts +130 -0
  25. package/src/model/model-parser.ts +580 -0
  26. package/src/model-change/ModelChanges.ts +120 -0
  27. package/src/model-change/changeElementStyle.ts +176 -0
  28. package/src/model-change/changeViewLayout.ts +41 -0
  29. package/src/module.ts +197 -0
  30. package/src/node/index.ts +20 -0
  31. package/src/protocol.ts +87 -0
  32. package/src/references/index.ts +2 -0
  33. package/src/references/scope-computation.ts +142 -0
  34. package/src/references/scope-provider.ts +166 -0
  35. package/src/shared/NodeKindProvider.ts +67 -0
  36. package/src/shared/WorkspaceManager.ts +39 -0
  37. package/src/shared/WorkspaceSymbolProvider.ts +3 -0
  38. package/src/shared/index.ts +3 -0
  39. package/src/test/index.ts +1 -0
  40. package/src/test/testServices.ts +119 -0
  41. package/src/utils/index.ts +1 -0
  42. package/src/utils/printDocs.ts +3 -0
  43. package/src/utils/stringHash.ts +6 -0
  44. package/{dist/validation/dynamic-view-rule.js → src/validation/dynamic-view-rule.ts} +14 -11
  45. package/src/validation/dynamic-view-step.ts +39 -0
  46. package/src/validation/element.ts +52 -0
  47. package/{dist/validation/index.js → src/validation/index.ts} +22 -18
  48. package/src/validation/property-checks.ts +17 -0
  49. package/src/validation/relation.ts +57 -0
  50. package/src/validation/specification.ts +118 -0
  51. package/src/validation/view-predicates/custom-element-expr.ts +21 -0
  52. package/{dist/validation/view-predicates/expanded-element.js → src/validation/view-predicates/expanded-element.ts} +18 -13
  53. package/src/validation/view-predicates/incoming.ts +19 -0
  54. package/src/validation/view-predicates/index.ts +4 -0
  55. package/src/validation/view-predicates/outgoing.ts +19 -0
  56. package/src/validation/view.ts +26 -0
  57. package/src/view-utils/assignNavigateTo.ts +30 -0
  58. package/src/view-utils/index.ts +3 -0
  59. package/src/view-utils/resolve-extended-views.ts +57 -0
  60. package/src/view-utils/resolve-relative-paths.ts +84 -0
  61. package/dist/Rpc.d.ts +0 -10
  62. package/dist/Rpc.js +0 -98
  63. package/dist/ast.d.ts +0 -149
  64. package/dist/ast.js +0 -271
  65. package/dist/browser/index.d.ts +0 -9
  66. package/dist/browser/index.js +0 -16
  67. package/dist/elementRef.d.ts +0 -12
  68. package/dist/elementRef.js +0 -15
  69. package/dist/generated/ast.d.ts +0 -615
  70. package/dist/generated/ast.js +0 -957
  71. package/dist/generated/grammar.d.ts +0 -7
  72. package/dist/generated/grammar.js +0 -3
  73. package/dist/generated/module.d.ts +0 -14
  74. package/dist/generated/module.js +0 -22
  75. package/dist/index.d.ts +0 -5
  76. package/dist/index.js +0 -2
  77. package/dist/logger.d.ts +0 -12
  78. package/dist/logger.js +0 -51
  79. package/dist/lsp/CodeLensProvider.d.ts +0 -10
  80. package/dist/lsp/CodeLensProvider.js +0 -40
  81. package/dist/lsp/DocumentHighlightProvider.d.ts +0 -10
  82. package/dist/lsp/DocumentHighlightProvider.js +0 -10
  83. package/dist/lsp/DocumentLinkProvider.d.ts +0 -11
  84. package/dist/lsp/DocumentLinkProvider.js +0 -41
  85. package/dist/lsp/DocumentLinkProvider.test.d.ts +0 -2
  86. package/dist/lsp/DocumentLinkProvider.test.js +0 -54
  87. package/dist/lsp/DocumentSymbolProvider.d.ts +0 -22
  88. package/dist/lsp/DocumentSymbolProvider.js +0 -189
  89. package/dist/lsp/HoverProvider.d.ts +0 -10
  90. package/dist/lsp/HoverProvider.js +0 -36
  91. package/dist/lsp/SemanticTokenProvider.d.ts +0 -8
  92. package/dist/lsp/index.d.ts +0 -7
  93. package/dist/lsp/index.js +0 -6
  94. package/dist/model/fqn-computation.d.ts +0 -4
  95. package/dist/model/fqn-computation.js +0 -43
  96. package/dist/model/fqn-index.d.ts +0 -26
  97. package/dist/model/fqn-index.js +0 -114
  98. package/dist/model/index.d.ts +0 -6
  99. package/dist/model/index.js +0 -5
  100. package/dist/model/model-builder.d.ts +0 -20
  101. package/dist/model/model-builder.js +0 -365
  102. package/dist/model/model-locator.d.ts +0 -22
  103. package/dist/model/model-locator.js +0 -115
  104. package/dist/model/model-parser.d.ts +0 -29
  105. package/dist/model/model-parser.js +0 -520
  106. package/dist/model-change/ModelChanges.d.ts +0 -16
  107. package/dist/model-change/ModelChanges.js +0 -106
  108. package/dist/model-change/changeElementStyle.d.ts +0 -18
  109. package/dist/model-change/changeElementStyle.js +0 -141
  110. package/dist/model-change/changeViewLayout.d.ts +0 -13
  111. package/dist/model-change/changeViewLayout.js +0 -29
  112. package/dist/module.d.ts +0 -59
  113. package/dist/module.js +0 -121
  114. package/dist/node/index.d.ts +0 -6
  115. package/dist/node/index.js +0 -13
  116. package/dist/protocol.d.ts +0 -58
  117. package/dist/protocol.js +0 -14
  118. package/dist/references/index.d.ts +0 -3
  119. package/dist/references/index.js +0 -2
  120. package/dist/references/scope-computation.d.ts +0 -11
  121. package/dist/references/scope-computation.js +0 -111
  122. package/dist/references/scope-provider.d.ts +0 -18
  123. package/dist/references/scope-provider.js +0 -136
  124. package/dist/shared/NodeKindProvider.d.ts +0 -16
  125. package/dist/shared/NodeKindProvider.js +0 -60
  126. package/dist/shared/WorkspaceManager.d.ts +0 -17
  127. package/dist/shared/WorkspaceManager.js +0 -29
  128. package/dist/shared/WorkspaceSymbolProvider.d.ts +0 -4
  129. package/dist/shared/WorkspaceSymbolProvider.js +0 -3
  130. package/dist/shared/index.d.ts +0 -4
  131. package/dist/shared/index.js +0 -3
  132. package/dist/test/index.d.ts +0 -2
  133. package/dist/test/index.js +0 -1
  134. package/dist/test/testServices.d.ts +0 -23
  135. package/dist/test/testServices.js +0 -102
  136. package/dist/utils/index.d.ts +0 -2
  137. package/dist/utils/index.js +0 -1
  138. package/dist/utils/printDocs.d.ts +0 -3
  139. package/dist/utils/printDocs.js +0 -1
  140. package/dist/utils/stringHash.d.ts +0 -2
  141. package/dist/utils/stringHash.js +0 -5
  142. package/dist/validation/dynamic-view-rule.d.ts +0 -5
  143. package/dist/validation/dynamic-view-step.d.ts +0 -5
  144. package/dist/validation/dynamic-view-step.js +0 -33
  145. package/dist/validation/element.d.ts +0 -6
  146. package/dist/validation/element.js +0 -38
  147. package/dist/validation/index.d.ts +0 -3
  148. package/dist/validation/property-checks.d.ts +0 -5
  149. package/dist/validation/property-checks.js +0 -11
  150. package/dist/validation/relation.d.ts +0 -5
  151. package/dist/validation/relation.js +0 -50
  152. package/dist/validation/specification.d.ts +0 -10
  153. package/dist/validation/specification.js +0 -97
  154. package/dist/validation/view-predicates/custom-element-expr.d.ts +0 -5
  155. package/dist/validation/view-predicates/custom-element-expr.js +0 -16
  156. package/dist/validation/view-predicates/expanded-element.d.ts +0 -5
  157. package/dist/validation/view-predicates/incoming.d.ts +0 -5
  158. package/dist/validation/view-predicates/incoming.js +0 -14
  159. package/dist/validation/view-predicates/index.d.ts +0 -5
  160. package/dist/validation/view-predicates/index.js +0 -4
  161. package/dist/validation/view-predicates/outgoing.d.ts +0 -5
  162. package/dist/validation/view-predicates/outgoing.js +0 -14
  163. package/dist/validation/view.d.ts +0 -5
  164. package/dist/validation/view.js +0 -16
  165. package/dist/view-utils/assignNavigateTo.d.ts +0 -3
  166. package/dist/view-utils/assignNavigateTo.js +0 -24
  167. package/dist/view-utils/index.d.ts +0 -4
  168. package/dist/view-utils/index.js +0 -3
  169. package/dist/view-utils/resolve-extended-views.d.ts +0 -7
  170. package/dist/view-utils/resolve-extended-views.js +0 -41
  171. package/dist/view-utils/resolve-relative-paths.d.ts +0 -3
  172. package/dist/view-utils/resolve-relative-paths.js +0 -75
  173. /package/{dist → src}/reset.d.ts +0 -0
@@ -0,0 +1,52 @@
1
+ import { AstUtils, type ValidationCheck } from 'langium'
2
+ import type { ast } from '../ast'
3
+ import type { LikeC4Services } from '../module'
4
+
5
+ const { getDocument } = AstUtils
6
+
7
+ export const elementChecks = (services: LikeC4Services): ValidationCheck<ast.Element> => {
8
+ const fqnIndex = services.likec4.FqnIndex
9
+ return (el, accept) => {
10
+ const fqn = fqnIndex.getFqn(el)
11
+ if (!fqn) {
12
+ accept('error', 'Not indexed element', {
13
+ node: el,
14
+ property: 'name'
15
+ })
16
+ return
17
+ }
18
+ const withSameFqn = fqnIndex
19
+ .byFqn(fqn)
20
+ .filter(v => v.el !== el)
21
+ .head()
22
+ if (withSameFqn) {
23
+ const isAnotherDoc = withSameFqn.doc.uri !== getDocument(el).uri
24
+ accept(
25
+ 'error',
26
+ `Duplicate element name ${el.name !== fqn ? el.name + ' (' + fqn + ')' : el.name}`,
27
+ {
28
+ node: el,
29
+ property: 'name',
30
+ ...isAnotherDoc && {
31
+ relatedInformation: [
32
+ {
33
+ location: {
34
+ range: withSameFqn.el.$cstNode!.range,
35
+ uri: withSameFqn.doc.uri.toString()
36
+ },
37
+ message: `conflicting element`
38
+ }
39
+ ]
40
+ }
41
+ }
42
+ )
43
+ }
44
+ // for (let i = 3; i < el.props.length; i++) {
45
+ // accept('error', `Too many properties, max 3 allowed`, {
46
+ // node: el,
47
+ // property: 'props',
48
+ // index: i
49
+ // })
50
+ // }
51
+ }
52
+ }
@@ -1,9 +1,11 @@
1
- import { logger } from "../logger.js";
2
- import { dynamicViewRulePredicate } from "./dynamic-view-rule.js";
3
- import { dynamicViewStep } from "./dynamic-view-step.js";
4
- import { elementChecks } from "./element.js";
5
- import { opacityPropertyRuleChecks } from "./property-checks.js";
6
- import { relationChecks } from "./relation.js";
1
+ import { type ast } from '../ast'
2
+ import { logger } from '../logger'
3
+ import type { LikeC4Services } from '../module'
4
+ import { dynamicViewRulePredicate } from './dynamic-view-rule'
5
+ import { dynamicViewStep } from './dynamic-view-step'
6
+ import { elementChecks } from './element'
7
+ import { opacityPropertyRuleChecks } from './property-checks'
8
+ import { relationChecks } from './relation'
7
9
  import {
8
10
  elementKindChecks,
9
11
  modelRuleChecks,
@@ -11,18 +13,19 @@ import {
11
13
  relationshipChecks,
12
14
  specificationRuleChecks,
13
15
  tagChecks
14
- } from "./specification.js";
15
- import { viewChecks } from "./view.js";
16
+ } from './specification'
17
+ import { viewChecks } from './view'
16
18
  import {
17
19
  customElementExprChecks,
18
20
  expandElementExprChecks,
19
21
  incomingExpressionChecks,
20
22
  outgoingExpressionChecks
21
- } from "./view-predicates/index.js";
22
- export function registerValidationChecks(services) {
23
- logger.info("registerValidationChecks");
24
- const registry = services.validation.ValidationRegistry;
25
- registry.register({
23
+ } from './view-predicates'
24
+
25
+ export function registerValidationChecks(services: LikeC4Services) {
26
+ logger.info('registerValidationChecks')
27
+ const registry = services.validation.ValidationRegistry
28
+ registry.register<ast.LikeC4AstType>({
26
29
  OpacityProperty: opacityPropertyRuleChecks(services),
27
30
  SpecificationRule: specificationRuleChecks(services),
28
31
  Model: modelRuleChecks(services),
@@ -39,17 +42,18 @@ export function registerValidationChecks(services) {
39
42
  RelationshipKind: relationshipChecks(services),
40
43
  IncomingExpr: incomingExpressionChecks(services),
41
44
  OutgoingExpr: outgoingExpressionChecks(services)
42
- });
43
- const connection = services.shared.lsp.Connection;
45
+ })
46
+ const connection = services.shared.lsp.Connection
44
47
  if (connection) {
48
+ // workaround for bug in langium
45
49
  services.shared.workspace.DocumentBuilder.onUpdate((_, deleted) => {
46
50
  for (const uri of deleted) {
47
- logger.debug(`clear diagnostics for deleted ${uri.path}`);
51
+ logger.debug(`clear diagnostics for deleted ${uri.path}`)
48
52
  void connection.sendDiagnostics({
49
53
  uri: uri.toString(),
50
54
  diagnostics: []
51
- });
55
+ })
52
56
  }
53
- });
57
+ })
54
58
  }
55
59
  }
@@ -0,0 +1,17 @@
1
+ import type { ValidationCheck } from 'langium'
2
+ import type { ast } from '../ast'
3
+ import type { LikeC4Services } from '../module'
4
+
5
+ export const opacityPropertyRuleChecks = (
6
+ _: LikeC4Services
7
+ ): ValidationCheck<ast.OpacityProperty> => {
8
+ return (node, accept) => {
9
+ const opacity = parseFloat(node.value)
10
+ if (isNaN(opacity) || opacity < 0 || opacity > 100) {
11
+ accept('warning', `Value ignored, must be between 0% and 100%`, {
12
+ node,
13
+ property: 'value'
14
+ })
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,57 @@
1
+ import { isSameHierarchy } from '@likec4/core'
2
+ import type { ValidationCheck } from 'langium'
3
+ import { ast } from '../ast'
4
+ import { elementRef } from '../elementRef'
5
+ import { logError } from '../logger'
6
+ import type { LikeC4Services } from '../module'
7
+
8
+ export const relationChecks = (services: LikeC4Services): ValidationCheck<ast.Relation> => {
9
+ const fqnIndex = services.likec4.FqnIndex
10
+ return (el, accept) => {
11
+ try {
12
+ const targetEl: ast.Element | undefined = elementRef(el.target)
13
+ const target = targetEl && fqnIndex.getFqn(targetEl)
14
+ if (!target) {
15
+ accept('error', 'Target not found (not parsed/indexed yet)', {
16
+ node: el,
17
+ property: 'target'
18
+ })
19
+ }
20
+ let sourceEl
21
+ if (ast.isExplicitRelation(el)) {
22
+ sourceEl = elementRef(el.source)
23
+ if (!sourceEl) {
24
+ return accept('error', 'Source not found (not parsed/indexed yet)', {
25
+ node: el,
26
+ property: 'source'
27
+ })
28
+ }
29
+ } else {
30
+ sourceEl = el.$container.$container
31
+ }
32
+
33
+ const source = fqnIndex.getFqn(sourceEl)
34
+
35
+ if (!source) {
36
+ accept('error', 'Source not found (not parsed/indexed yet)', {
37
+ node: el
38
+ })
39
+ }
40
+
41
+ if (source && target && isSameHierarchy(source, target)) {
42
+ accept('error', 'Invalid parent-child relationship', {
43
+ node: el
44
+ })
45
+ }
46
+
47
+ if (el.tags?.value && el.body?.tags?.value) {
48
+ accept('error', 'Relation cannot have tags in both header and body', {
49
+ node: el,
50
+ property: 'tags'
51
+ })
52
+ }
53
+ } catch (e) {
54
+ logError(e)
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,118 @@
1
+ import { AstUtils, type ValidationCheck } from 'langium'
2
+ import { ast } from '../ast'
3
+ import type { LikeC4Services } from '../module'
4
+
5
+ export const specificationRuleChecks = (
6
+ _: LikeC4Services
7
+ ): ValidationCheck<ast.SpecificationRule> => {
8
+ return (node, accept) => {
9
+ if (node.$containerIndex && node.$containerIndex > 0) {
10
+ accept('error', `Only one specification per document is allowed`, {
11
+ node: node,
12
+ property: 'name'
13
+ })
14
+ }
15
+ }
16
+ }
17
+
18
+ export const modelRuleChecks = (_: LikeC4Services): ValidationCheck<ast.Model> => {
19
+ return (node, accept) => {
20
+ if (node.$containerIndex && node.$containerIndex > 0) {
21
+ accept('error', `Only one model per document is allowed`, {
22
+ node: node,
23
+ property: 'name'
24
+ })
25
+ }
26
+ }
27
+ }
28
+
29
+ export const modelViewsChecks = (_: LikeC4Services): ValidationCheck<ast.ModelViews> => {
30
+ return (node, accept) => {
31
+ if (node.$containerIndex && node.$containerIndex > 0) {
32
+ accept('error', `Only one views block per document is allowed`, {
33
+ node: node,
34
+ property: 'name'
35
+ })
36
+ }
37
+ }
38
+ }
39
+
40
+ export const elementKindChecks = (services: LikeC4Services): ValidationCheck<ast.ElementKind> => {
41
+ const index = services.shared.workspace.IndexManager
42
+ return (node, accept) => {
43
+ const sameKind = index
44
+ .allElements(ast.ElementKind)
45
+ .filter(n => n.name === node.name && n.node !== node)
46
+ .head()
47
+ if (sameKind) {
48
+ const isAnotherDoc = sameKind.documentUri !== AstUtils.getDocument(node).uri
49
+ accept('error', `Duplicate element kind '${node.name}'`, {
50
+ node: node,
51
+ property: 'name',
52
+ ...isAnotherDoc && {
53
+ relatedInformation: [
54
+ {
55
+ location: {
56
+ range: sameKind.nameSegment!.range,
57
+ uri: sameKind.documentUri.toString()
58
+ },
59
+ message: `conflicting definition`
60
+ }
61
+ ]
62
+ }
63
+ })
64
+ }
65
+ }
66
+ }
67
+
68
+ export const tagChecks = (services: LikeC4Services): ValidationCheck<ast.Tag> => {
69
+ const index = services.shared.workspace.IndexManager
70
+ return (node, accept) => {
71
+ const tagname = '#' + node.name
72
+ const sameTag = index
73
+ .allElements(ast.Tag)
74
+ .filter(n => n.name === tagname && n.node !== node)
75
+ .head()
76
+ if (sameTag) {
77
+ const isAnotherDoc = sameTag.documentUri !== AstUtils.getDocument(node).uri
78
+ accept(
79
+ 'error',
80
+ `Duplicate tag '${node.name}'`,
81
+ {
82
+ node,
83
+ property: 'name',
84
+ ...isAnotherDoc && {
85
+ relatedInformation: [
86
+ {
87
+ location: {
88
+ range: sameTag.nameSegment!.range,
89
+ uri: sameTag.documentUri.toString()
90
+ },
91
+ message: `conflicting definition`
92
+ }
93
+ ]
94
+ }
95
+ }
96
+ )
97
+ }
98
+ }
99
+ }
100
+
101
+ export const relationshipChecks = (
102
+ services: LikeC4Services
103
+ ): ValidationCheck<ast.RelationshipKind> => {
104
+ const index = services.shared.workspace.IndexManager
105
+ return (node, accept) => {
106
+ const sameKinds = index
107
+ .allElements(ast.RelationshipKind)
108
+ .filter(n => n.name === node.name)
109
+ .limit(2)
110
+ .count()
111
+ if (sameKinds > 1) {
112
+ accept('error', `Duplicate RelationshipKind '${node.name}'`, {
113
+ node: node,
114
+ property: 'name'
115
+ })
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,21 @@
1
+ import type { ValidationCheck } from 'langium'
2
+ import { ast } from '../../ast'
3
+ import type { LikeC4Services } from '../../module'
4
+
5
+ export const customElementExprChecks = (
6
+ _services: LikeC4Services
7
+ ): ValidationCheck<ast.CustomElementExpr> => {
8
+ return (el, accept) => {
9
+ if (ast.isExcludePredicate(el.$container)) {
10
+ accept('error', 'Invalid inside "exclude"', {
11
+ node: el
12
+ })
13
+ }
14
+ if (!ast.isElementRef(el.target)) {
15
+ accept('error', 'Invalid target for customization', {
16
+ node: el,
17
+ property: 'target'
18
+ })
19
+ }
20
+ }
21
+ }
@@ -1,29 +1,34 @@
1
- import { nonexhaustive } from "@likec4/core";
2
- import { ast } from "../../ast.js";
3
- export const expandElementExprChecks = (_services) => {
1
+ import { nonexhaustive } from '@likec4/core'
2
+ import type { ValidationCheck } from 'langium'
3
+ import { ast } from '../../ast'
4
+ import type { LikeC4Services } from '../../module'
5
+
6
+ export const expandElementExprChecks = (
7
+ _services: LikeC4Services
8
+ ): ValidationCheck<ast.ExpandElementExpr> => {
4
9
  return (el, accept) => {
5
10
  switch (true) {
6
11
  case ast.isIncludePredicate(el.$container):
7
12
  case ast.isDynamicViewRulePredicate(el.$container):
8
13
  case ast.isViewRuleStyle(el.$container):
9
- return;
14
+ return
10
15
  case ast.isCustomElementExpr(el.$container):
11
- return accept("warning", `Custom rules apply only to parent`, {
16
+ return accept('warning', `Custom rules apply only to parent`, {
12
17
  node: el
13
- });
18
+ })
14
19
  case ast.isExcludePredicate(el.$container):
15
- return accept("warning", `Ignored, as can't be used in exclude`, {
20
+ return accept('warning', `Ignored, as can't be used in exclude`, {
16
21
  node: el
17
- });
22
+ })
18
23
  case ast.isInOutExpr(el.$container):
19
24
  case ast.isIncomingExpr(el.$container):
20
25
  case ast.isOutgoingExpr(el.$container):
21
26
  case ast.isRelationExpr(el.$container):
22
- return accept("warning", `Wrong usage of expanded element in relations predicate`, {
27
+ return accept('warning', `Wrong usage of expanded element in relations predicate`, {
23
28
  node: el
24
- });
29
+ })
25
30
  default:
26
- nonexhaustive(el.$container);
31
+ nonexhaustive(el.$container)
27
32
  }
28
- };
29
- };
33
+ }
34
+ }
@@ -0,0 +1,19 @@
1
+ import type { ValidationCheck } from 'langium'
2
+ import { isNullish } from 'remeda'
3
+ import { ast } from '../../ast'
4
+ import type { LikeC4Services } from '../../module'
5
+
6
+ export const incomingExpressionChecks = (
7
+ _services: LikeC4Services
8
+ ): ValidationCheck<ast.IncomingExpr> => {
9
+ return (el, accept) => {
10
+ if (ast.isWildcardExpr(el.to) && ast.isViewRulePredicate(el.$container)) {
11
+ const view = el.$container.$container.$container
12
+ if (isNullish(view.viewOf)) {
13
+ accept('warning', 'Predicate is ignored as it concerns all relationships', {
14
+ node: el
15
+ })
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,4 @@
1
+ export * from './custom-element-expr'
2
+ export * from './expanded-element'
3
+ export * from './incoming'
4
+ export * from './outgoing'
@@ -0,0 +1,19 @@
1
+ import type { ValidationCheck } from 'langium'
2
+ import { isNullish } from 'remeda'
3
+ import { ast } from '../../ast'
4
+ import type { LikeC4Services } from '../../module'
5
+
6
+ export const outgoingExpressionChecks = (
7
+ _services: LikeC4Services
8
+ ): ValidationCheck<ast.OutgoingExpr> => {
9
+ return (el, accept) => {
10
+ if (ast.isWildcardExpr(el.from)) {
11
+ const view = el.$container.$container.$container
12
+ if (view.$type === 'ElementView' && isNullish(view.viewOf)) {
13
+ accept('warning', 'Predicate is ignored as it concerns all relationships', {
14
+ node: el
15
+ })
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,26 @@
1
+ import type { ValidationCheck } from 'langium'
2
+ import { ast } from '../ast'
3
+ import type { LikeC4Services } from '../module'
4
+
5
+ export const viewChecks = (services: LikeC4Services): ValidationCheck<ast.LikeC4View> => {
6
+ const index = services.shared.workspace.IndexManager
7
+ return (el, accept) => {
8
+ // if (el.extends) {
9
+ // // TODO: circular dependency check
10
+ // }
11
+ if (!el.name) {
12
+ return
13
+ }
14
+ const anotherViews = index
15
+ .allElements(ast.LikeC4View)
16
+ .filter(n => n.name === el.name)
17
+ .limit(2)
18
+ .count()
19
+ if (anotherViews > 1) {
20
+ accept('error', `Duplicate view '${el.name}'`, {
21
+ node: el,
22
+ property: 'name'
23
+ })
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,30 @@
1
+ import { type ComputedView, type Fqn, isComputedElementView, type ViewID } from '@likec4/core'
2
+ import { find } from 'remeda'
3
+
4
+ export function assignNavigateTo<R extends Iterable<ComputedView>>(views: R): R {
5
+ const allElementViews = new Map<Fqn, ViewID[]>()
6
+
7
+ for (const v of views) {
8
+ if (isComputedElementView(v) && v.viewOf && !v.extends) {
9
+ const viewsOf = allElementViews.get(v.viewOf) ?? []
10
+ viewsOf.push(v.id)
11
+ allElementViews.set(v.viewOf, viewsOf)
12
+ }
13
+ }
14
+
15
+ // set default navigateTo
16
+ for (const { id, nodes } of views) {
17
+ for (const node of nodes) {
18
+ if (node.navigateTo) {
19
+ continue
20
+ }
21
+ // find first element view that is not the current one
22
+ const navigateTo = find(allElementViews.get(node.id) ?? [], v => v !== id)
23
+ if (navigateTo) {
24
+ node.navigateTo = navigateTo
25
+ }
26
+ }
27
+ }
28
+
29
+ return views
30
+ }
@@ -0,0 +1,3 @@
1
+ export * from './assignNavigateTo'
2
+ export * from './resolve-extended-views'
3
+ export * from './resolve-relative-paths'
@@ -0,0 +1,57 @@
1
+ import graphlib from '@dagrejs/graphlib'
2
+ import { isExtendsElementView, type View } from '@likec4/core'
3
+
4
+ // '@dagrejs/graphlib' is a CommonJS module
5
+ // Here is a workaround to import it
6
+ const { Graph, alg } = graphlib
7
+
8
+ /**
9
+ * Resolve rules of extended views
10
+ * (Removes invalid views)
11
+ */
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ export function resolveRulesExtendedViews<V extends Record<any, View>>(
14
+ unresolvedViews: V
15
+ ): V {
16
+ const g = new Graph({
17
+ directed: true,
18
+ multigraph: false,
19
+ compound: false
20
+ })
21
+ for (const view of Object.values(unresolvedViews)) {
22
+ g.setNode(view.id)
23
+ if (isExtendsElementView(view)) {
24
+ // view -> parent
25
+ g.setEdge(view.id, view.extends)
26
+ }
27
+ }
28
+
29
+ // Remove circular dependencies
30
+ const cycles = alg.findCycles(g)
31
+ if (cycles.length > 0) {
32
+ cycles.flat().forEach(id => g.removeNode(id))
33
+ }
34
+
35
+ const ordered = alg.postorder(g, g.sources())
36
+
37
+ return ordered.reduce((acc, id) => {
38
+ const view = unresolvedViews[id]
39
+ if (!view) {
40
+ return acc
41
+ }
42
+ if (isExtendsElementView(view)) {
43
+ const extendsFrom = acc[view.extends]
44
+ if (!extendsFrom) {
45
+ return acc
46
+ }
47
+ return Object.assign(acc, {
48
+ [view.id]: {
49
+ ...extendsFrom,
50
+ ...view,
51
+ rules: [...extendsFrom.rules, ...view.rules]
52
+ }
53
+ })
54
+ }
55
+ return Object.assign(acc, { [view.id]: view })
56
+ }, {} as V)
57
+ }
@@ -0,0 +1,84 @@
1
+ import type { View } from '@likec4/core'
2
+ import { invariant } from '@likec4/core'
3
+ import { hasAtLeast, unique, zip } from 'remeda'
4
+
5
+ function commonAncestorPath(views: View[], sep = '/') {
6
+ if (views.length <= 1) return ''
7
+ const uniqURIs = unique(views.flatMap(({ docUri }) => (docUri ? [docUri] : [])))
8
+ if (uniqURIs.length === 0) return ''
9
+ if (uniqURIs.length === 1) {
10
+ invariant(hasAtLeast(uniqURIs, 1))
11
+ return new URL(uniqURIs[0]).pathname
12
+ }
13
+ invariant(hasAtLeast(uniqURIs, 2), 'Expected at least 2 unique URIs')
14
+ const [baseUri, ...tail] = uniqURIs
15
+ const parts = new URL(baseUri).pathname.split(sep)
16
+ let endOfPrefix = parts.length
17
+ for (const uri of tail) {
18
+ if (uri === baseUri) {
19
+ continue
20
+ }
21
+ const compare = new URL(uri).pathname.split(sep)
22
+ for (let i = 0; i < endOfPrefix; i++) {
23
+ if (compare[i] !== parts[i]) {
24
+ endOfPrefix = i
25
+ }
26
+ }
27
+ if (endOfPrefix === 0) return ''
28
+ }
29
+ const prefix = parts.slice(0, endOfPrefix).join(sep)
30
+ return prefix.endsWith(sep) ? prefix : prefix + sep
31
+ }
32
+
33
+ export function resolveRelativePaths(views: View[]): View[] {
34
+ const commonPrefix = commonAncestorPath(views)
35
+ return (
36
+ views
37
+ // For each view, compute the relative path to the common prefix
38
+ // Store array of path segments
39
+ .map(view => {
40
+ if (!view.docUri) {
41
+ return {
42
+ ...view,
43
+ parts: []
44
+ }
45
+ }
46
+ const path = new URL(view.docUri).pathname
47
+ const parts = path.replace(commonPrefix, '').split('/')
48
+ parts.pop() // remove filename
49
+ return {
50
+ ...view,
51
+ parts
52
+ }
53
+ })
54
+ // Sort views by path segments
55
+ .sort((a, b) => {
56
+ if (a.parts.length === b.parts.length) {
57
+ if (a.parts.length === 0) {
58
+ return 0
59
+ }
60
+ if (a.parts.length === 1 && hasAtLeast(a.parts, 1) && hasAtLeast(b.parts, 1)) {
61
+ return a.parts[0].localeCompare(b.parts[0])
62
+ }
63
+ for (const [_a, _b] of zip(a.parts, b.parts)) {
64
+ const compare = _a.localeCompare(_b)
65
+ if (compare !== 0) {
66
+ return compare
67
+ }
68
+ }
69
+ return 0
70
+ }
71
+ return a.parts.length - b.parts.length
72
+ })
73
+ // Build relativePath from path segments
74
+ .map(({ parts, ...view }) => {
75
+ if (view.docUri) {
76
+ return {
77
+ ...view,
78
+ relativePath: parts.join('/')
79
+ }
80
+ }
81
+ return view
82
+ })
83
+ )
84
+ }
package/dist/Rpc.d.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { LikeC4Services } from './module';
2
- import { Disposable } from 'langium';
3
- export declare class Rpc implements Disposable {
4
- private services;
5
- private disposables;
6
- constructor(services: LikeC4Services);
7
- init(): void;
8
- dispose(): void;
9
- }
10
- //# sourceMappingURL=Rpc.d.ts.map