@likec4/language-server 1.5.0 → 1.6.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.
- package/contrib/likec4.tmLanguage.json +1 -1
- package/package.json +17 -5
- package/src/Rpc.ts +2 -1
- package/src/ast.ts +10 -5
- package/src/generated/ast.ts +355 -238
- package/src/generated/grammar.ts +1 -1
- package/src/generated-lib/icons.ts +952 -0
- package/src/like-c4.langium +85 -54
- package/src/likec4lib.ts +7 -0
- package/src/lsp/DocumentSymbolProvider.ts +28 -1
- package/src/lsp/SemanticTokenProvider.ts +30 -9
- package/src/model/model-parser.ts +126 -74
- package/src/model-change/changeElementStyle.ts +4 -4
- package/src/model-graph/compute-view/__test__/fixture.ts +3 -3
- package/src/model-graph/compute-view/compute.ts +4 -4
- package/src/model-graph/dynamic-view/__test__/fixture.ts +2 -2
- package/src/references/scope-computation.ts +113 -60
- package/src/shared/NodeKindProvider.ts +1 -0
- package/src/shared/WorkspaceManager.ts +15 -6
- package/src/validation/dynamic-view-rule.ts +19 -24
- package/src/validation/index.ts +8 -7
- package/src/validation/property-checks.ts +23 -1
- package/src/validation/view-predicates/custom-element-expr.ts +9 -7
- package/src/validation/view-predicates/custom-relation-expr.ts +4 -3
- package/src/validation/view-predicates/expanded-element.ts +13 -24
- package/src/validation/view-predicates/incoming.ts +5 -5
- package/src/validation/view-predicates/outgoing.ts +5 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Fqn, invariant, isAncestor, type NonEmptyArray, nonNullable, type ViewChanges } from '@likec4/core'
|
|
2
2
|
import { GrammarUtils } from 'langium'
|
|
3
|
-
import { entries, filter, findLast, last } from 'remeda'
|
|
3
|
+
import { entries, filter, findLast, isTruthy, last } from 'remeda'
|
|
4
4
|
import { type Range, TextEdit } from 'vscode-languageserver-protocol'
|
|
5
5
|
import { ast, type ParsedAstView, type ParsedLikeC4LangiumDocument } from '../ast'
|
|
6
6
|
import type { FqnIndex } from '../model'
|
|
@@ -37,8 +37,8 @@ const isMatchingViewRule =
|
|
|
37
37
|
if (!ast.isViewRuleStyle(rule)) {
|
|
38
38
|
return false
|
|
39
39
|
}
|
|
40
|
-
const
|
|
41
|
-
if (!target ||
|
|
40
|
+
const target = rule.target.value
|
|
41
|
+
if (!target || isTruthy(rule.target.prev) || !ast.isElementRef(target)) {
|
|
42
42
|
return false
|
|
43
43
|
}
|
|
44
44
|
const ref = target.el.ref
|
|
@@ -127,7 +127,7 @@ export function changeElementStyle(services: LikeC4Services, {
|
|
|
127
127
|
invariant(ruleCstNode, 'RuleCstNode not found')
|
|
128
128
|
for (const [key, _value] of entries(style)) {
|
|
129
129
|
const value = key === 'opacity' ? _value.toString() + '%' : _value
|
|
130
|
-
const ruleProp = rule.
|
|
130
|
+
const ruleProp = rule.props.find(p => p.key === key)
|
|
131
131
|
// replace existing property
|
|
132
132
|
if (ruleProp && ruleProp.$cstNode) {
|
|
133
133
|
const { range: { start, end } } = nonNullable(
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
ThemeColor,
|
|
17
17
|
ViewID,
|
|
18
18
|
ViewRule,
|
|
19
|
-
|
|
19
|
+
ViewRulePredicate,
|
|
20
20
|
ViewRuleStyle
|
|
21
21
|
} from '@likec4/core'
|
|
22
22
|
import { indexBy, isString, map, prop } from 'remeda'
|
|
@@ -418,12 +418,12 @@ export function $expr(expr: Expression | C4Expression): C4Expression {
|
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
-
export function $include(expr: Expression | C4Expression):
|
|
421
|
+
export function $include(expr: Expression | C4Expression): ViewRulePredicate {
|
|
422
422
|
return {
|
|
423
423
|
include: [$expr(expr)]
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
|
-
export function $exclude(expr: Expression | C4Expression):
|
|
426
|
+
export function $exclude(expr: Expression | C4Expression): ViewRulePredicate {
|
|
427
427
|
return {
|
|
428
428
|
exclude: [$expr(expr)]
|
|
429
429
|
}
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
Element,
|
|
6
6
|
ElementView,
|
|
7
7
|
Relation,
|
|
8
|
-
|
|
8
|
+
ViewRulePredicate
|
|
9
9
|
} from '@likec4/core'
|
|
10
10
|
import {
|
|
11
11
|
ancestorsFqn,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
isAncestor,
|
|
17
17
|
isScopedElementView,
|
|
18
18
|
isViewRuleAutoLayout,
|
|
19
|
-
|
|
19
|
+
isViewRulePredicate,
|
|
20
20
|
nonexhaustive,
|
|
21
21
|
parentFqn
|
|
22
22
|
} from '@likec4/core'
|
|
@@ -92,7 +92,7 @@ export class ComputeCtx {
|
|
|
92
92
|
this.reset()
|
|
93
93
|
const { rules, ...view } = this.view
|
|
94
94
|
|
|
95
|
-
const viewPredicates = rules.filter(
|
|
95
|
+
const viewPredicates = rules.filter(isViewRulePredicate)
|
|
96
96
|
if (this.root && viewPredicates.length == 0) {
|
|
97
97
|
this.addElement(this.graph.element(this.root))
|
|
98
98
|
}
|
|
@@ -399,7 +399,7 @@ export class ComputeCtx {
|
|
|
399
399
|
}, [] as ComputeCtx.Edge[])
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
-
protected processPredicates(viewRules:
|
|
402
|
+
protected processPredicates(viewRules: ViewRulePredicate[]): this {
|
|
403
403
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
404
404
|
for (const rule of viewRules) {
|
|
405
405
|
const isInclude = 'include' in rule
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DynamicViewRule, DynamicViewStep, Fqn, ViewID,
|
|
1
|
+
import type { DynamicViewRule, DynamicViewStep, Fqn, ViewID, ViewRulePredicate } from '@likec4/core'
|
|
2
2
|
import { partition } from 'remeda'
|
|
3
3
|
import { type FakeElementIds, fakeModel } from '../../compute-view/__test__/fixture'
|
|
4
4
|
import { computeDynamicView } from '../index'
|
|
@@ -36,7 +36,7 @@ export function $step(expr: StepExpr, title?: string): DynamicViewStep {
|
|
|
36
36
|
throw new Error(`Invalid step expression: ${expr}`)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export function compute(stepsAndRules: (DynamicViewStep |
|
|
39
|
+
export function compute(stepsAndRules: (DynamicViewStep | ViewRulePredicate)[]) {
|
|
40
40
|
const [steps, rules] = partition(stepsAndRules, (s): s is DynamicViewStep => 'source' in s)
|
|
41
41
|
const result = computeDynamicView(
|
|
42
42
|
{
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
MultiMap,
|
|
7
7
|
type PrecomputedScopes
|
|
8
8
|
} from 'langium'
|
|
9
|
-
import { isTruthy } from 'remeda'
|
|
9
|
+
import { isNullish, isTruthy } from 'remeda'
|
|
10
10
|
import type { CancellationToken } from 'vscode-languageserver'
|
|
11
11
|
import { ast, type LikeC4LangiumDocument } from '../ast'
|
|
12
12
|
import { logError } from '../logger'
|
|
@@ -20,77 +20,130 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
20
20
|
): Promise<AstNodeDescription[]> {
|
|
21
21
|
const docExports: AstNodeDescription[] = []
|
|
22
22
|
try {
|
|
23
|
-
const { specifications, models, views } = document.parseResult.value
|
|
23
|
+
const { specifications, models, views, likec4lib } = document.parseResult.value
|
|
24
|
+
|
|
25
|
+
// Process library
|
|
26
|
+
this.exportLibrary(likec4lib, docExports, document)
|
|
24
27
|
|
|
25
28
|
// Process specification
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
case ast.isSpecificationRelationshipKind(spec): {
|
|
52
|
-
if (isTruthy(spec.kind.name)) {
|
|
53
|
-
docExports.push(
|
|
54
|
-
this.descriptions.createDescription(spec.kind, spec.kind.name, document),
|
|
55
|
-
this.descriptions.createDescription(spec.kind, '.' + spec.kind.name, document)
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
continue
|
|
59
|
-
}
|
|
60
|
-
// Thow error if not exhaustive
|
|
61
|
-
default:
|
|
62
|
-
nonexhaustive(spec)
|
|
63
|
-
}
|
|
64
|
-
} catch (e) {
|
|
65
|
-
logError(e)
|
|
29
|
+
this.exportSpecification(specifications, docExports, document)
|
|
30
|
+
|
|
31
|
+
// Process models
|
|
32
|
+
this.exportModel(models, docExports, document)
|
|
33
|
+
|
|
34
|
+
// Process views
|
|
35
|
+
this.exportViews(views, docExports, document)
|
|
36
|
+
} catch (e) {
|
|
37
|
+
logError(e)
|
|
38
|
+
}
|
|
39
|
+
return docExports
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private exportViews(
|
|
43
|
+
views: ast.ModelViews[] | undefined,
|
|
44
|
+
docExports: AstNodeDescription[],
|
|
45
|
+
document: LikeC4LangiumDocument
|
|
46
|
+
) {
|
|
47
|
+
if (isNullish(views) || views.length === 0) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
for (const viewAst of views.flatMap(v => v.views)) {
|
|
51
|
+
try {
|
|
52
|
+
if (isTruthy(viewAst.name)) {
|
|
53
|
+
docExports.push(this.descriptions.createDescription(viewAst, viewAst.name, document))
|
|
66
54
|
}
|
|
55
|
+
} catch (e) {
|
|
56
|
+
logError(e)
|
|
67
57
|
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
68
60
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
61
|
+
private exportModel(
|
|
62
|
+
models: ast.Model[] | undefined,
|
|
63
|
+
docExports: AstNodeDescription[],
|
|
64
|
+
document: LikeC4LangiumDocument
|
|
65
|
+
) {
|
|
66
|
+
if (isNullish(models) || models.length === 0) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
for (const elAst of models.flatMap(m => m.elements)) {
|
|
70
|
+
try {
|
|
71
|
+
if (ast.isElement(elAst) && isTruthy(elAst.name)) {
|
|
72
|
+
docExports.push(this.descriptions.createDescription(elAst, elAst.name, document))
|
|
77
73
|
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
logError(e)
|
|
78
76
|
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
private exportLibrary(
|
|
81
|
+
likec4lib: ast.LikeC4Lib[] | undefined,
|
|
82
|
+
docExports: AstNodeDescription[],
|
|
83
|
+
document: LikeC4LangiumDocument
|
|
84
|
+
) {
|
|
85
|
+
if (isNullish(likec4lib) || likec4lib.length === 0) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
for (const iconAst of likec4lib.flatMap(l => l.icons)) {
|
|
89
|
+
try {
|
|
90
|
+
docExports.push(this.descriptions.createDescription(iconAst, iconAst.name, document))
|
|
91
|
+
} catch (e) {
|
|
92
|
+
logError(e)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private exportSpecification(
|
|
98
|
+
specifications: ast.SpecificationRule[] | undefined,
|
|
99
|
+
docExports: AstNodeDescription[],
|
|
100
|
+
document: LikeC4LangiumDocument
|
|
101
|
+
) {
|
|
102
|
+
if (isNullish(specifications) || specifications.length === 0) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
for (
|
|
106
|
+
const spec of specifications.flatMap(s => [
|
|
107
|
+
...s.elements,
|
|
108
|
+
...s.relationships,
|
|
109
|
+
...s.tags
|
|
110
|
+
])
|
|
111
|
+
) {
|
|
112
|
+
try {
|
|
113
|
+
switch (true) {
|
|
114
|
+
case ast.isSpecificationElementKind(spec): {
|
|
115
|
+
if (isTruthy(spec.kind.name)) {
|
|
116
|
+
docExports.push(
|
|
117
|
+
this.descriptions.createDescription(spec.kind, spec.kind.name, document)
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
continue
|
|
85
121
|
}
|
|
86
|
-
|
|
87
|
-
|
|
122
|
+
case ast.isSpecificationTag(spec): {
|
|
123
|
+
if (isTruthy(spec.tag.name)) {
|
|
124
|
+
docExports.push(
|
|
125
|
+
this.descriptions.createDescription(spec.tag, '#' + spec.tag.name, document)
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
case ast.isSpecificationRelationshipKind(spec): {
|
|
131
|
+
if (isTruthy(spec.kind.name)) {
|
|
132
|
+
docExports.push(
|
|
133
|
+
this.descriptions.createDescription(spec.kind, spec.kind.name, document),
|
|
134
|
+
this.descriptions.createDescription(spec.kind, '.' + spec.kind.name, document)
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
// Thow error if not exhaustive
|
|
140
|
+
default:
|
|
141
|
+
nonexhaustive(spec)
|
|
88
142
|
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
logError(e)
|
|
89
145
|
}
|
|
90
|
-
} catch (e) {
|
|
91
|
-
logError(e)
|
|
92
146
|
}
|
|
93
|
-
return docExports
|
|
94
147
|
}
|
|
95
148
|
|
|
96
149
|
override computeLocalScopes(
|
|
@@ -25,6 +25,7 @@ export class NodeKindProvider implements LspNodeKindProvider {
|
|
|
25
25
|
return SymbolKind.Class
|
|
26
26
|
}
|
|
27
27
|
case (ast.isTag(node) || hasType(ast.Tag))
|
|
28
|
+
|| (ast.isLibIcon(node) || hasType(ast.LibIcon))
|
|
28
29
|
|| (ast.isSpecificationTag(node) || hasType(ast.SpecificationTag)): {
|
|
29
30
|
return SymbolKind.EnumMember
|
|
30
31
|
}
|
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
import { hasAtLeast, invariant } from '@likec4/core'
|
|
2
|
-
import type { LangiumDocument } from 'langium'
|
|
2
|
+
import type { LangiumDocument, LangiumDocumentFactory } from 'langium'
|
|
3
3
|
import { DefaultWorkspaceManager } from 'langium'
|
|
4
|
+
import type { LangiumSharedServices } from 'langium/lsp'
|
|
4
5
|
import type { WorkspaceFolder } from 'vscode-languageserver'
|
|
5
6
|
import { URI } from 'vscode-uri'
|
|
7
|
+
import * as BuiltIn from '../likec4lib'
|
|
6
8
|
|
|
7
9
|
export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
|
|
10
|
+
private documentFactory: LangiumDocumentFactory
|
|
11
|
+
|
|
12
|
+
constructor(services: LangiumSharedServices) {
|
|
13
|
+
super(services)
|
|
14
|
+
this.documentFactory = services.workspace.LangiumDocumentFactory
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
/**
|
|
9
18
|
* Load all additional documents that shall be visible in the context of the given workspace
|
|
10
19
|
* folders and add them to the collector. This can be used to include built-in libraries of
|
|
11
20
|
* your language, which can be either loaded from provided files or constructed in memory.
|
|
12
21
|
*/
|
|
13
|
-
protected override loadAdditionalDocuments(
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
protected override async loadAdditionalDocuments(
|
|
23
|
+
folders: WorkspaceFolder[],
|
|
24
|
+
collector: (document: LangiumDocument) => void
|
|
16
25
|
): Promise<void> {
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
await super.loadAdditionalDocuments(folders, collector)
|
|
27
|
+
collector(this.documentFactory.fromString(BuiltIn.Content, URI.parse(BuiltIn.Uri)))
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
public workspace() {
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
import { nonexhaustive } from '@likec4/core'
|
|
2
2
|
import type { ValidationCheck } from 'langium'
|
|
3
3
|
import { ast } from '../ast'
|
|
4
|
-
import { logError } from '../logger'
|
|
5
4
|
import type { LikeC4Services } from '../module'
|
|
6
5
|
|
|
7
|
-
export const dynamicViewRulePredicate = (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
default:
|
|
26
|
-
nonexhaustive(expr)
|
|
27
|
-
}
|
|
6
|
+
export const dynamicViewRulePredicate = (
|
|
7
|
+
_services: LikeC4Services
|
|
8
|
+
): ValidationCheck<ast.DynamicViewRulePredicateIterator> => {
|
|
9
|
+
return (expr, accept) => {
|
|
10
|
+
switch (true) {
|
|
11
|
+
case ast.isElementRef(expr.value):
|
|
12
|
+
case ast.isElementDescedantsExpression(expr.value):
|
|
13
|
+
case ast.isCustomElementExpression(expr.value):
|
|
14
|
+
case ast.isExpandElementExpression(expr.value):
|
|
15
|
+
return
|
|
16
|
+
case ast.isElementKindExpression(expr.value):
|
|
17
|
+
case ast.isElementTagExpression(expr.value):
|
|
18
|
+
case ast.isWildcardExpression(expr.value): {
|
|
19
|
+
accept('warning', `Predicate is ignored, as not supported in dynamic views`, {
|
|
20
|
+
node: expr
|
|
21
|
+
})
|
|
22
|
+
return
|
|
28
23
|
}
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
default:
|
|
25
|
+
nonexhaustive(expr.value)
|
|
31
26
|
}
|
|
32
27
|
}
|
|
33
28
|
}
|
package/src/validation/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { LikeC4Services } from '../module'
|
|
|
4
4
|
import { dynamicViewRulePredicate } from './dynamic-view-rule'
|
|
5
5
|
import { dynamicViewStep } from './dynamic-view-step'
|
|
6
6
|
import { elementChecks } from './element'
|
|
7
|
-
import { opacityPropertyRuleChecks } from './property-checks'
|
|
7
|
+
import { iconPropertyRuleChecks, opacityPropertyRuleChecks } from './property-checks'
|
|
8
8
|
import { relationChecks } from './relation'
|
|
9
9
|
import {
|
|
10
10
|
elementKindChecks,
|
|
@@ -28,6 +28,7 @@ export function registerValidationChecks(services: LikeC4Services) {
|
|
|
28
28
|
const registry = services.validation.ValidationRegistry
|
|
29
29
|
registry.register<ast.LikeC4AstType>({
|
|
30
30
|
OpacityProperty: opacityPropertyRuleChecks(services),
|
|
31
|
+
IconProperty: iconPropertyRuleChecks(services),
|
|
31
32
|
SpecificationRule: specificationRuleChecks(services),
|
|
32
33
|
Model: modelRuleChecks(services),
|
|
33
34
|
ModelViews: modelViewsChecks(services),
|
|
@@ -37,13 +38,13 @@ export function registerValidationChecks(services: LikeC4Services) {
|
|
|
37
38
|
ElementKind: elementKindChecks(services),
|
|
38
39
|
Relation: relationChecks(services),
|
|
39
40
|
Tag: tagChecks(services),
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
DynamicViewRulePredicateIterator: dynamicViewRulePredicate(services),
|
|
42
|
+
CustomElementExpression: customElementExprChecks(services),
|
|
43
|
+
CustomRelationExpression: customRelationExprChecks(services),
|
|
44
|
+
ExpandElementExpression: expandElementExprChecks(services),
|
|
44
45
|
RelationshipKind: relationshipChecks(services),
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
IncomingRelationExpression: incomingExpressionChecks(services),
|
|
47
|
+
OutgoingRelationExpression: outgoingExpressionChecks(services)
|
|
47
48
|
})
|
|
48
49
|
const connection = services.shared.lsp.Connection
|
|
49
50
|
if (connection) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ValidationCheck } from 'langium'
|
|
2
|
-
import
|
|
2
|
+
import { ast } from '../ast'
|
|
3
3
|
import type { LikeC4Services } from '../module'
|
|
4
4
|
|
|
5
5
|
export const opacityPropertyRuleChecks = (
|
|
@@ -15,3 +15,25 @@ export const opacityPropertyRuleChecks = (
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
export const iconPropertyRuleChecks = (
|
|
20
|
+
_: LikeC4Services
|
|
21
|
+
): ValidationCheck<ast.IconProperty> => {
|
|
22
|
+
return (node, accept) => {
|
|
23
|
+
const container = node.$container
|
|
24
|
+
const anotherIcon = container.props.some(p => ast.isIconProperty(p) && p !== node)
|
|
25
|
+
if (anotherIcon) {
|
|
26
|
+
accept('error', `Icon must be defined once`, {
|
|
27
|
+
node
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
if (
|
|
31
|
+
ast.isStyleProperties(container) && ast.isElementBody(container.$container)
|
|
32
|
+
&& container.$container.props.some(p => ast.isIconProperty(p))
|
|
33
|
+
) {
|
|
34
|
+
accept('warning', `Redundant as icon defined on element`, {
|
|
35
|
+
node
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import { nonexhaustive } from '@likec4/core'
|
|
2
2
|
import type { ValidationCheck } from 'langium'
|
|
3
|
+
import { AstUtils } from 'langium'
|
|
3
4
|
import { ast } from '../../ast'
|
|
4
5
|
import type { LikeC4Services } from '../../module'
|
|
5
6
|
|
|
6
7
|
export const customElementExprChecks = (
|
|
7
8
|
_services: LikeC4Services
|
|
8
|
-
): ValidationCheck<ast.
|
|
9
|
+
): ValidationCheck<ast.CustomElementExpression> => {
|
|
9
10
|
return (el, accept) => {
|
|
10
|
-
|
|
11
|
+
const container = AstUtils.getContainerOfType(el, ast.isViewRulePredicate)
|
|
12
|
+
if (ast.isExcludePredicate(container)) {
|
|
11
13
|
accept('error', 'Invalid usage inside "exclude"', {
|
|
12
14
|
node: el
|
|
13
15
|
})
|
|
14
16
|
}
|
|
15
17
|
switch (true) {
|
|
16
18
|
case ast.isElementRef(el.target):
|
|
19
|
+
case ast.isElementDescedantsExpression(el.target):
|
|
20
|
+
case ast.isExpandElementExpression(el.target):
|
|
17
21
|
return
|
|
18
|
-
case ast.
|
|
19
|
-
case ast.
|
|
20
|
-
case ast.
|
|
21
|
-
case ast.isWildcardExpr(el.target):
|
|
22
|
-
case ast.isDescedantsExpr(el.target):
|
|
22
|
+
case ast.isElementKindExpression(el.target):
|
|
23
|
+
case ast.isElementTagExpression(el.target):
|
|
24
|
+
case ast.isWildcardExpression(el.target):
|
|
23
25
|
accept('error', 'Invalid target (expect reference to specific element)', {
|
|
24
26
|
node: el,
|
|
25
27
|
property: 'target'
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { AstUtils, type ValidationCheck } from 'langium'
|
|
2
2
|
import { ast } from '../../ast'
|
|
3
3
|
import type { LikeC4Services } from '../../module'
|
|
4
4
|
|
|
5
5
|
export const customRelationExprChecks = (
|
|
6
6
|
_services: LikeC4Services
|
|
7
|
-
): ValidationCheck<ast.
|
|
7
|
+
): ValidationCheck<ast.CustomRelationExpression> => {
|
|
8
8
|
return (el, accept) => {
|
|
9
|
-
|
|
9
|
+
const container = AstUtils.getContainerOfType(el, ast.isViewRulePredicate)
|
|
10
|
+
if (ast.isExcludePredicate(container)) {
|
|
10
11
|
accept('error', 'Invalid usage inside "exclude"', {
|
|
11
12
|
node: el
|
|
12
13
|
})
|
|
@@ -1,34 +1,23 @@
|
|
|
1
1
|
import { nonexhaustive } from '@likec4/core'
|
|
2
|
-
import type
|
|
2
|
+
import { type AstNode, AstUtils, type ValidationCheck } from 'langium'
|
|
3
3
|
import { ast } from '../../ast'
|
|
4
4
|
import type { LikeC4Services } from '../../module'
|
|
5
5
|
|
|
6
6
|
export const expandElementExprChecks = (
|
|
7
7
|
_services: LikeC4Services
|
|
8
|
-
): ValidationCheck<ast.
|
|
8
|
+
): ValidationCheck<ast.ExpandElementExpression> => {
|
|
9
9
|
return (el, accept) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
node: el
|
|
22
|
-
})
|
|
23
|
-
case ast.isInOutExpr(el.$container):
|
|
24
|
-
case ast.isIncomingExpr(el.$container):
|
|
25
|
-
case ast.isOutgoingExpr(el.$container):
|
|
26
|
-
case ast.isRelationExpr(el.$container):
|
|
27
|
-
return accept('warning', `Wrong usage of expanded element in relations predicate`, {
|
|
28
|
-
node: el
|
|
29
|
-
})
|
|
30
|
-
default:
|
|
31
|
-
nonexhaustive(el.$container)
|
|
10
|
+
const isInside = <T extends AstNode>(typePredicate: (n: AstNode) => n is T): boolean =>
|
|
11
|
+
!!AstUtils.getContainerOfType(el, typePredicate)
|
|
12
|
+
if (isInside(ast.isRelationExpression)) {
|
|
13
|
+
accept('warning', `Redundant usage, expand predicate resolves parent element only when used in relations`, {
|
|
14
|
+
node: el
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
if (isInside(ast.isExcludePredicate)) {
|
|
18
|
+
accept('warning', `Expand predicate is ignored in exclude`, {
|
|
19
|
+
node: el
|
|
20
|
+
})
|
|
32
21
|
}
|
|
33
22
|
}
|
|
34
23
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { AstUtils, type ValidationCheck } from 'langium'
|
|
2
2
|
import { isNullish } from 'remeda'
|
|
3
3
|
import { ast } from '../../ast'
|
|
4
4
|
import type { LikeC4Services } from '../../module'
|
|
5
5
|
|
|
6
6
|
export const incomingExpressionChecks = (
|
|
7
7
|
_services: LikeC4Services
|
|
8
|
-
): ValidationCheck<ast.
|
|
8
|
+
): ValidationCheck<ast.IncomingRelationExpression> => {
|
|
9
9
|
return (el, accept) => {
|
|
10
|
-
if (ast.
|
|
11
|
-
const view = el
|
|
12
|
-
if (isNullish(view
|
|
10
|
+
if (ast.isWildcardExpression(el.to) && ast.isExpressions(el.$container)) {
|
|
11
|
+
const view = AstUtils.getContainerOfType(el, ast.isElementView)
|
|
12
|
+
if (isNullish(view?.viewOf)) {
|
|
13
13
|
accept('warning', 'Predicate is ignored as it concerns all relationships', {
|
|
14
14
|
node: el
|
|
15
15
|
})
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { AstUtils, type ValidationCheck } from 'langium'
|
|
2
2
|
import { isNullish } from 'remeda'
|
|
3
3
|
import { ast } from '../../ast'
|
|
4
4
|
import type { LikeC4Services } from '../../module'
|
|
5
5
|
|
|
6
6
|
export const outgoingExpressionChecks = (
|
|
7
7
|
_services: LikeC4Services
|
|
8
|
-
): ValidationCheck<ast.
|
|
8
|
+
): ValidationCheck<ast.OutgoingRelationExpression> => {
|
|
9
9
|
return (el, accept) => {
|
|
10
|
-
if (ast.
|
|
11
|
-
const view = el
|
|
12
|
-
if (
|
|
10
|
+
if (ast.isWildcardExpression(el.from) && ast.isExpressions(el.$container)) {
|
|
11
|
+
const view = AstUtils.getContainerOfType(el, ast.isElementView)
|
|
12
|
+
if (isNullish(view?.viewOf)) {
|
|
13
13
|
accept('warning', 'Predicate is ignored as it concerns all relationships', {
|
|
14
14
|
node: el
|
|
15
15
|
})
|