@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.
Files changed (48) hide show
  1. package/contrib/likec4.tmLanguage.json +1 -1
  2. package/dist/browser.cjs +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.mts +2 -2
  5. package/dist/browser.d.ts +2 -2
  6. package/dist/browser.mjs +2 -2
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.d.cts +2 -2
  9. package/dist/index.d.mts +2 -2
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.mjs +2 -2
  12. package/dist/likec4lib.cjs +0 -1
  13. package/dist/likec4lib.mjs +0 -1
  14. package/dist/model-graph/index.cjs +1 -1
  15. package/dist/model-graph/index.mjs +1 -1
  16. package/dist/shared/{language-server.CbDa016p.cjs → language-server.80ITEDo5.cjs} +272 -64
  17. package/dist/shared/{language-server.C2ebP2pZ.cjs → language-server.BUtiWTKg.cjs} +383 -32
  18. package/dist/shared/{language-server.DFLaUdYu.mjs → language-server.DXC9g4_f.mjs} +274 -66
  19. package/dist/shared/{language-server.ryB8CivX.d.mts → language-server.DfMwkd2l.d.mts} +81 -15
  20. package/dist/shared/{language-server.Cnq_hgfm.d.cts → language-server.U2piOAVt.d.cts} +81 -15
  21. package/dist/shared/{language-server.eY70DuKx.d.ts → language-server.j-ShR6as.d.ts} +81 -15
  22. package/dist/shared/{language-server.CrE0nFSB.mjs → language-server.zY53FGJE.mjs} +385 -34
  23. package/package.json +12 -12
  24. package/src/ast.ts +11 -0
  25. package/src/generated/ast.ts +177 -17
  26. package/src/generated/grammar.ts +1 -1
  27. package/src/generated-lib/icons.ts +0 -1
  28. package/src/like-c4.langium +50 -4
  29. package/src/lsp/CompletionProvider.ts +70 -2
  30. package/src/model/model-builder.ts +25 -3
  31. package/src/model/model-parser.ts +150 -33
  32. package/src/model-graph/compute-view/__test__/fixture.ts +45 -4
  33. package/src/model-graph/compute-view/compute.ts +223 -40
  34. package/src/model-graph/compute-view/predicates.ts +12 -6
  35. package/src/model-graph/utils/applyCustomElementProperties.ts +18 -4
  36. package/src/model-graph/utils/applyCustomRelationProperties.ts +2 -1
  37. package/src/model-graph/utils/applyViewRuleStyles.ts +30 -25
  38. package/src/model-graph/utils/buildComputeNodes.ts +69 -17
  39. package/src/model-graph/utils/elementExpressionToPredicate.ts +3 -1
  40. package/src/model-graph/utils/sortNodes.ts +11 -7
  41. package/src/references/scope-computation.ts +24 -1
  42. package/src/validation/index.ts +4 -0
  43. package/src/validation/specification.ts +30 -0
  44. package/src/view-utils/index.ts +1 -0
  45. package/src/view-utils/resolve-global-rules.ts +72 -0
  46. package/dist/shared/language-server.B-9_mDoo.d.mts +0 -1238
  47. package/dist/shared/language-server.C3oS5yhF.d.cts +0 -1238
  48. 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(target: Expr.ElementPredicateExpression): Predicate<Element> {
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 (edges.length === 0) {
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[],
@@ -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
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './assignNavigateTo'
2
2
  export * from './resolve-extended-views'
3
+ export * from './resolve-global-rules'
3
4
  export * from './resolve-relative-paths'
@@ -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
+ }