@likec4/language-server 1.13.0 → 1.15.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.
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.mts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.mjs +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/likec4lib.cjs +0 -1
- package/dist/likec4lib.mjs +0 -1
- package/dist/model-graph/index.cjs +1 -1
- package/dist/model-graph/index.mjs +1 -1
- package/dist/shared/{language-server.CbDa016p.cjs → language-server.80ITEDo5.cjs} +272 -64
- package/dist/shared/{language-server.C2ebP2pZ.cjs → language-server.BUtiWTKg.cjs} +383 -32
- package/dist/shared/{language-server.DFLaUdYu.mjs → language-server.DXC9g4_f.mjs} +274 -66
- package/dist/shared/{language-server.ryB8CivX.d.mts → language-server.DfMwkd2l.d.mts} +81 -15
- package/dist/shared/{language-server.Cnq_hgfm.d.cts → language-server.U2piOAVt.d.cts} +81 -15
- package/dist/shared/{language-server.eY70DuKx.d.ts → language-server.j-ShR6as.d.ts} +81 -15
- package/dist/shared/{language-server.CrE0nFSB.mjs → language-server.zY53FGJE.mjs} +385 -34
- package/package.json +12 -12
- package/src/ast.ts +11 -0
- package/src/generated/ast.ts +177 -17
- package/src/generated/grammar.ts +1 -1
- package/src/generated-lib/icons.ts +0 -1
- package/src/like-c4.langium +50 -4
- package/src/lsp/CompletionProvider.ts +70 -2
- package/src/model/model-builder.ts +25 -3
- package/src/model/model-parser.ts +150 -33
- package/src/model-graph/compute-view/__test__/fixture.ts +45 -4
- package/src/model-graph/compute-view/compute.ts +223 -40
- package/src/model-graph/compute-view/predicates.ts +12 -6
- package/src/model-graph/utils/applyCustomElementProperties.ts +18 -4
- package/src/model-graph/utils/applyCustomRelationProperties.ts +2 -1
- package/src/model-graph/utils/applyViewRuleStyles.ts +30 -25
- package/src/model-graph/utils/buildComputeNodes.ts +69 -17
- package/src/model-graph/utils/elementExpressionToPredicate.ts +3 -1
- package/src/model-graph/utils/sortNodes.ts +11 -7
- package/src/references/scope-computation.ts +24 -1
- package/src/validation/index.ts +4 -0
- package/src/validation/specification.ts +30 -0
- package/src/view-utils/index.ts +1 -0
- package/src/view-utils/resolve-global-rules.ts +72 -0
- package/dist/shared/language-server.B-9_mDoo.d.mts +0 -1238
- package/dist/shared/language-server.C3oS5yhF.d.cts +0 -1238
- package/dist/shared/language-server.r5AXAWzc.d.ts +0 -1238
|
@@ -4,7 +4,9 @@ import { isNullish } from 'remeda'
|
|
|
4
4
|
|
|
5
5
|
type Predicate<T> = (x: T) => boolean
|
|
6
6
|
|
|
7
|
-
export function elementExprToPredicate
|
|
7
|
+
export function elementExprToPredicate<T extends Pick<Element, 'id' | 'kind' | 'tags'>>(
|
|
8
|
+
target: Expr.ElementPredicateExpression
|
|
9
|
+
): Predicate<T> {
|
|
8
10
|
if (Expr.isElementWhere(target)) {
|
|
9
11
|
const predicate = elementExprToPredicate(target.where.expr)
|
|
10
12
|
const where = whereOperatorAsPredicate(target.where.condition)
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
invariant,
|
|
9
9
|
nonNullable
|
|
10
10
|
} from '@likec4/core'
|
|
11
|
-
import { difference, filter, map, pipe, sort } from 'remeda'
|
|
11
|
+
import { difference, filter, map, pipe, sort, tap } from 'remeda'
|
|
12
12
|
import { Graph, postorder } from '../../utils/graphlib'
|
|
13
13
|
|
|
14
14
|
// side effect
|
|
@@ -27,9 +27,16 @@ export function sortNodes({
|
|
|
27
27
|
nodes: ComputedNode[]
|
|
28
28
|
edges: ComputedEdge[]
|
|
29
29
|
}): ComputedNode[] {
|
|
30
|
-
if (
|
|
30
|
+
if (nodes.length < 2) {
|
|
31
31
|
return nodes
|
|
32
32
|
}
|
|
33
|
+
if (edges.length === 0) {
|
|
34
|
+
return pipe(
|
|
35
|
+
nodes,
|
|
36
|
+
sort(compareByFqnHierarchically),
|
|
37
|
+
tap(sortChildren)
|
|
38
|
+
)
|
|
39
|
+
}
|
|
33
40
|
|
|
34
41
|
const g = new Graph({
|
|
35
42
|
compound: false,
|
|
@@ -55,9 +62,6 @@ export function sortNodes({
|
|
|
55
62
|
for (const n of nodes) {
|
|
56
63
|
g.setNode(n.id, n.id)
|
|
57
64
|
if (n.children.length > 0) {
|
|
58
|
-
// n.children.forEach(c => {
|
|
59
|
-
// g.setEdge(n.id, c, undefined, `${n.id}:${c}`)
|
|
60
|
-
// })
|
|
61
65
|
n.inEdges.forEach(e => {
|
|
62
66
|
const edge = getEdge(e)
|
|
63
67
|
// if this edge from leaf to the child of this node
|
|
@@ -83,15 +87,15 @@ export function sortNodes({
|
|
|
83
87
|
if (sources.length === 0) {
|
|
84
88
|
sources = pipe(
|
|
85
89
|
nodes,
|
|
86
|
-
sort(compareByFqnHierarchically),
|
|
87
90
|
filter(n => n.inEdges.length === 0 || n.parent === null),
|
|
91
|
+
sort(compareByFqnHierarchically),
|
|
88
92
|
map(n => n.id)
|
|
89
93
|
)
|
|
90
94
|
}
|
|
91
95
|
const orderedIds = postorder(g, sources).reverse() as Fqn[]
|
|
92
96
|
const sorted = orderedIds.map(getNode)
|
|
93
97
|
if (sorted.length < nodes.length) {
|
|
94
|
-
const unsorted = difference(nodes, sorted)
|
|
98
|
+
const unsorted = difference(nodes, sorted).sort(compareByFqnHierarchically)
|
|
95
99
|
sorted.push(...unsorted)
|
|
96
100
|
}
|
|
97
101
|
|
|
@@ -20,7 +20,7 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
20
20
|
): Promise<AstNodeDescription[]> {
|
|
21
21
|
const docExports: AstNodeDescription[] = []
|
|
22
22
|
try {
|
|
23
|
-
const { specifications, models, views, likec4lib } = document.parseResult.value
|
|
23
|
+
const { specifications, models, views, globals, likec4lib } = document.parseResult.value
|
|
24
24
|
|
|
25
25
|
// Process library
|
|
26
26
|
this.exportLibrary(likec4lib, docExports, document)
|
|
@@ -33,6 +33,9 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
33
33
|
|
|
34
34
|
// Process views
|
|
35
35
|
this.exportViews(views, docExports, document)
|
|
36
|
+
|
|
37
|
+
// Process global
|
|
38
|
+
this.exportGlobals(globals, docExports, document)
|
|
36
39
|
} catch (e) {
|
|
37
40
|
logError(e)
|
|
38
41
|
}
|
|
@@ -58,6 +61,26 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
|
|
64
|
+
private exportGlobals(
|
|
65
|
+
globals: ast.Globals[] | undefined,
|
|
66
|
+
docExports: AstNodeDescription[],
|
|
67
|
+
document: LikeC4LangiumDocument
|
|
68
|
+
) {
|
|
69
|
+
if (isNullish(globals) || globals.length === 0) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
for (const globalStyleAst of globals.flatMap(g => g.styles)) {
|
|
73
|
+
try {
|
|
74
|
+
const id = globalStyleAst.id
|
|
75
|
+
if (isTruthy(id.name)) {
|
|
76
|
+
docExports.push(this.descriptions.createDescription(id, id.name, document))
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
logError(e)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
61
84
|
private exportModel(
|
|
62
85
|
models: ast.Model[] | undefined,
|
|
63
86
|
docExports: AstNodeDescription[],
|
package/src/validation/index.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { iconPropertyRuleChecks, notesPropertyRuleChecks, opacityPropertyRuleChe
|
|
|
8
8
|
import { relationBodyChecks, relationChecks } from './relation'
|
|
9
9
|
import {
|
|
10
10
|
elementKindChecks,
|
|
11
|
+
globalsChecks,
|
|
12
|
+
globalStyleIdChecks,
|
|
11
13
|
modelRuleChecks,
|
|
12
14
|
relationshipChecks,
|
|
13
15
|
specificationRuleChecks,
|
|
@@ -31,6 +33,8 @@ export function registerValidationChecks(services: LikeC4Services) {
|
|
|
31
33
|
IconProperty: iconPropertyRuleChecks(services),
|
|
32
34
|
SpecificationRule: specificationRuleChecks(services),
|
|
33
35
|
Model: modelRuleChecks(services),
|
|
36
|
+
Globals: globalsChecks(services),
|
|
37
|
+
GlobalStyleId: globalStyleIdChecks(services),
|
|
34
38
|
DynamicViewStep: dynamicViewStep(services),
|
|
35
39
|
LikeC4View: viewChecks(services),
|
|
36
40
|
Element: elementChecks(services),
|
|
@@ -27,6 +27,17 @@ export const modelRuleChecks = (_: LikeC4Services): ValidationCheck<ast.Model> =
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export const globalsChecks = (_: LikeC4Services): ValidationCheck<ast.Globals> => {
|
|
31
|
+
return (node, accept) => {
|
|
32
|
+
if (node.$containerIndex && node.$containerIndex > 0) {
|
|
33
|
+
accept('warning', `Prefer one global block per document`, {
|
|
34
|
+
node: node,
|
|
35
|
+
property: 'name'
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
export const elementKindChecks = (services: LikeC4Services): ValidationCheck<ast.ElementKind> => {
|
|
31
42
|
const index = services.shared.workspace.IndexManager
|
|
32
43
|
return tryOrLog((node, accept) => {
|
|
@@ -118,3 +129,22 @@ export const relationshipChecks = (
|
|
|
118
129
|
}
|
|
119
130
|
}
|
|
120
131
|
}
|
|
132
|
+
|
|
133
|
+
export const globalStyleIdChecks = (
|
|
134
|
+
services: LikeC4Services
|
|
135
|
+
): ValidationCheck<ast.GlobalStyleId> => {
|
|
136
|
+
const index = services.shared.workspace.IndexManager
|
|
137
|
+
return (node, accept) => {
|
|
138
|
+
const sameName = index
|
|
139
|
+
.allElements(ast.GlobalStyleId)
|
|
140
|
+
.filter(s => s.name === node.name)
|
|
141
|
+
.limit(2)
|
|
142
|
+
.count()
|
|
143
|
+
if (sameName > 1) {
|
|
144
|
+
accept('error', `Duplicate GlobalStyleId name '${node.name}'`, {
|
|
145
|
+
node: node,
|
|
146
|
+
property: 'name'
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
package/src/view-utils/index.ts
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DynamicView,
|
|
3
|
+
type ElementView,
|
|
4
|
+
type GlobalStyle,
|
|
5
|
+
type GlobalStyleID,
|
|
6
|
+
isDynamicView,
|
|
7
|
+
isElementView,
|
|
8
|
+
isViewRuleGlobalStyle,
|
|
9
|
+
type LikeC4View,
|
|
10
|
+
nonexhaustive
|
|
11
|
+
} from '@likec4/core'
|
|
12
|
+
import { logger } from '@likec4/log'
|
|
13
|
+
|
|
14
|
+
export function resolveGlobalRules(
|
|
15
|
+
view: LikeC4View,
|
|
16
|
+
globalStyles: Record<GlobalStyleID, GlobalStyle>
|
|
17
|
+
): LikeC4View {
|
|
18
|
+
if (isElementView(view)) {
|
|
19
|
+
return resolveGlobalStyleInElementView(view, globalStyles)
|
|
20
|
+
} else if (isDynamicView(view)) {
|
|
21
|
+
return resolveGlobalStyleInDynamicView(view, globalStyles)
|
|
22
|
+
}
|
|
23
|
+
nonexhaustive(view)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function resolveGlobalStyleInElementView(
|
|
27
|
+
view: ElementView,
|
|
28
|
+
globalStyles: Record<GlobalStyleID, GlobalStyle>
|
|
29
|
+
): LikeC4View {
|
|
30
|
+
const resolvedRules = view.rules
|
|
31
|
+
.flatMap(rule => {
|
|
32
|
+
if (isViewRuleGlobalStyle(rule)) {
|
|
33
|
+
const globalStyle = globalStyles[rule.styleId]
|
|
34
|
+
if (globalStyle === undefined) {
|
|
35
|
+
logger.warn(`Global style not found: ${rule.styleId}`)
|
|
36
|
+
return []
|
|
37
|
+
}
|
|
38
|
+
return globalStyle.styles
|
|
39
|
+
} else {
|
|
40
|
+
return rule
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
...view,
|
|
46
|
+
rules: resolvedRules
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function resolveGlobalStyleInDynamicView(
|
|
51
|
+
view: DynamicView,
|
|
52
|
+
globalStyles: Record<GlobalStyleID, GlobalStyle>
|
|
53
|
+
): LikeC4View {
|
|
54
|
+
const resolvedRules = view.rules
|
|
55
|
+
.flatMap(rule => {
|
|
56
|
+
if (isViewRuleGlobalStyle(rule)) {
|
|
57
|
+
const globalStyle = globalStyles[rule.styleId]
|
|
58
|
+
if (globalStyle === undefined) {
|
|
59
|
+
logger.warn(`Global style not found: ${rule.styleId}`)
|
|
60
|
+
return []
|
|
61
|
+
}
|
|
62
|
+
return globalStyle.styles
|
|
63
|
+
} else {
|
|
64
|
+
return rule
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
...view,
|
|
70
|
+
rules: resolvedRules
|
|
71
|
+
}
|
|
72
|
+
}
|