@likec4/language-server 0.2.2 → 0.5.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/dist/ast.js CHANGED
@@ -11,7 +11,7 @@ export function c4hash({ c4Specification, c4Elements, c4Relations, c4Views }) {
11
11
  c4Relations,
12
12
  c4Views
13
13
  }, {
14
- respectType: false,
14
+ respectType: false
15
15
  });
16
16
  }
17
17
  const idattr = Symbol.for('idattr');
@@ -39,9 +39,9 @@ export function cleanParsedModel(doc) {
39
39
  doc.c4Specification = {
40
40
  kinds: {}
41
41
  };
42
- const elements = doc.c4Elements = [];
43
- const relations = doc.c4Relations = [];
44
- const views = doc.c4Views = [];
42
+ const elements = (doc.c4Elements = []);
43
+ const relations = (doc.c4Relations = []);
44
+ const views = (doc.c4Views = []);
45
45
  return {
46
46
  elements,
47
47
  relations,
@@ -53,21 +53,22 @@ export function isLikeC4LangiumDocument(doc) {
53
53
  return doc.textDocument.languageId === LikeC4LanguageMetaData.languageId;
54
54
  }
55
55
  export function isParsedLikeC4LangiumDocument(doc) {
56
- return isLikeC4LangiumDocument(doc) && ['c4Specification', 'c4Elements', 'c4Relations', 'c4Views'].every(key => key in doc);
56
+ return (isLikeC4LangiumDocument(doc) &&
57
+ ['c4Specification', 'c4Elements', 'c4Relations', 'c4Views'].every(key => key in doc));
57
58
  }
58
59
  export const isValidDocument = (doc) => {
59
60
  if (!isLikeC4LangiumDocument(doc))
60
61
  return false;
61
62
  const { state, parseResult, diagnostics } = doc;
62
- return (state === DocumentState.Validated
63
- && parseResult.lexerErrors.length === 0
64
- && (!diagnostics || diagnostics.every(d => d.severity !== 1)));
63
+ return (state === DocumentState.Validated &&
64
+ parseResult.lexerErrors.length === 0 &&
65
+ (!diagnostics || diagnostics.every(d => d.severity !== 1)));
65
66
  };
66
67
  export function* streamModel(doc) {
67
68
  const elements = doc.parseResult.value.model?.elements ?? [];
68
69
  const traverseStack = [...elements];
69
70
  let el;
70
- while (el = traverseStack.shift()) {
71
+ while ((el = traverseStack.shift())) {
71
72
  if (ast.isExtendElement(el)) {
72
73
  traverseStack.push(...el.body.elements);
73
74
  continue;
@@ -0,0 +1,4 @@
1
+ export declare const specification: {
2
+ uri: string;
3
+ document: string;
4
+ };
@@ -0,0 +1,8 @@
1
+ export const specification = {
2
+ uri: 'builtin:///specification.likec4',
3
+ document: `
4
+ specification {
5
+ element element
6
+ }
7
+ `.trimStart()
8
+ };
@@ -16,14 +16,15 @@ export type ElementShape = 'browser' | 'cylinder' | 'person' | 'queue' | 'rectan
16
16
  export type Expression = ElementExpression | InOutExpression | IncomingExpression | OutgoingExpression | RelationExpression;
17
17
  export declare const Expression = "Expression";
18
18
  export declare function isExpression(item: unknown): item is Expression;
19
- export type Name = 'model' | ElementShape | ThemeColor | string;
19
+ export type Name = 'element' | 'model' | ElementShape | ThemeColor | string;
20
20
  export type ThemeColor = 'muted' | 'primary' | 'secondary';
21
21
  export type View = ElementView;
22
22
  export declare const View = "View";
23
23
  export declare function isView(item: unknown): item is View;
24
- export type ViewRule = ViewRuleExpression | ViewRuleStyle;
24
+ export type ViewRule = ViewRuleAutoLayout | ViewRuleExpression | ViewRuleStyle;
25
25
  export declare const ViewRule = "ViewRule";
26
26
  export declare function isViewRule(item: unknown): item is ViewRule;
27
+ export type ViewRuleLayoutDirection = 'BT' | 'LR' | 'RL' | 'TB';
27
28
  export interface ColorProperty extends AstNode {
28
29
  readonly $container: ElementStyleProperty | SpecificationElementKindStyle | ViewRuleStyle;
29
30
  readonly $type: 'ColorProperty';
@@ -38,7 +39,7 @@ export interface Element extends AstNode {
38
39
  body?: ElementBody;
39
40
  kind: Reference<ElementKind>;
40
41
  name: Name;
41
- title?: string;
42
+ props: Array<string>;
42
43
  }
43
44
  export declare const Element = "Element";
44
45
  export declare function isElement(item: unknown): item is Element;
@@ -262,6 +263,13 @@ export interface ViewProperty extends AstNode {
262
263
  }
263
264
  export declare const ViewProperty = "ViewProperty";
264
265
  export declare function isViewProperty(item: unknown): item is ViewProperty;
266
+ export interface ViewRuleAutoLayout extends AstNode {
267
+ readonly $container: ElementView;
268
+ readonly $type: 'ViewRuleAutoLayout';
269
+ direction: ViewRuleLayoutDirection;
270
+ }
271
+ export declare const ViewRuleAutoLayout = "ViewRuleAutoLayout";
272
+ export declare function isViewRuleAutoLayout(item: unknown): item is ViewRuleAutoLayout;
265
273
  export interface ViewRuleExpression extends AstNode {
266
274
  readonly $container: ElementView;
267
275
  readonly $type: 'ViewRuleExpression';
@@ -333,6 +341,7 @@ export interface LikeC4AstType {
333
341
  View: View;
334
342
  ViewProperty: ViewProperty;
335
343
  ViewRule: ViewRule;
344
+ ViewRuleAutoLayout: ViewRuleAutoLayout;
336
345
  ViewRuleExpression: ViewRuleExpression;
337
346
  ViewRuleStyle: ViewRuleStyle;
338
347
  WildcardExpression: WildcardExpression;
@@ -148,6 +148,10 @@ export const ViewProperty = 'ViewProperty';
148
148
  export function isViewProperty(item) {
149
149
  return reflection.isInstance(item, ViewProperty);
150
150
  }
151
+ export const ViewRuleAutoLayout = 'ViewRuleAutoLayout';
152
+ export function isViewRuleAutoLayout(item) {
153
+ return reflection.isInstance(item, ViewRuleAutoLayout);
154
+ }
151
155
  export const ViewRuleExpression = 'ViewRuleExpression';
152
156
  export function isViewRuleExpression(item) {
153
157
  return reflection.isInstance(item, ViewRuleExpression);
@@ -166,7 +170,7 @@ export function isRelationWithSource(item) {
166
170
  }
167
171
  export class LikeC4AstReflection extends AbstractAstReflection {
168
172
  getAllTypes() {
169
- return ['AStyleProperty', 'ColorProperty', 'Element', 'ElementBody', 'ElementExpression', 'ElementKind', 'ElementProperty', 'ElementRef', 'ElementRefExpression', 'ElementStringProperty', 'ElementStyleProperty', 'ElementView', 'Expression', 'ExtendElement', 'ExtendElementBody', 'InOutExpression', 'IncomingExpression', 'LikeC4Document', 'Model', 'ModelViews', 'OutgoingExpression', 'Relation', 'RelationBody', 'RelationExpression', 'RelationProperty', 'RelationWithSource', 'ShapeProperty', 'SpecificationElementKind', 'SpecificationElementKindStyle', 'SpecificationRule', 'SpecificationTag', 'StrictElementRef', 'Tag', 'Tags', 'View', 'ViewProperty', 'ViewRule', 'ViewRuleExpression', 'ViewRuleStyle', 'WildcardExpression'];
173
+ return ['AStyleProperty', 'ColorProperty', 'Element', 'ElementBody', 'ElementExpression', 'ElementKind', 'ElementProperty', 'ElementRef', 'ElementRefExpression', 'ElementStringProperty', 'ElementStyleProperty', 'ElementView', 'Expression', 'ExtendElement', 'ExtendElementBody', 'InOutExpression', 'IncomingExpression', 'LikeC4Document', 'Model', 'ModelViews', 'OutgoingExpression', 'Relation', 'RelationBody', 'RelationExpression', 'RelationProperty', 'RelationWithSource', 'ShapeProperty', 'SpecificationElementKind', 'SpecificationElementKindStyle', 'SpecificationRule', 'SpecificationTag', 'StrictElementRef', 'Tag', 'Tags', 'View', 'ViewProperty', 'ViewRule', 'ViewRuleAutoLayout', 'ViewRuleExpression', 'ViewRuleStyle', 'WildcardExpression'];
170
174
  }
171
175
  computeIsSubtype(subtype, supertype) {
172
176
  switch (subtype) {
@@ -195,6 +199,7 @@ export class LikeC4AstReflection extends AbstractAstReflection {
195
199
  case RelationWithSource: {
196
200
  return this.isSubtype(Relation, supertype);
197
201
  }
202
+ case ViewRuleAutoLayout:
198
203
  case ViewRuleExpression:
199
204
  case ViewRuleStyle: {
200
205
  return this.isSubtype(ViewRule, supertype);
@@ -224,6 +229,14 @@ export class LikeC4AstReflection extends AbstractAstReflection {
224
229
  }
225
230
  getTypeMetaData(type) {
226
231
  switch (type) {
232
+ case 'Element': {
233
+ return {
234
+ name: 'Element',
235
+ mandatory: [
236
+ { name: 'props', type: 'array' }
237
+ ]
238
+ };
239
+ }
227
240
  case 'ElementBody': {
228
241
  return {
229
242
  name: 'ElementBody',
@@ -535,6 +535,10 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
535
535
  },
536
536
  "arguments": []
537
537
  },
538
+ {
539
+ "$type": "Keyword",
540
+ "value": "element"
541
+ },
538
542
  {
539
543
  "$type": "Keyword",
540
544
  "value": "model"
@@ -892,23 +896,77 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
892
896
  },
893
897
  "deprecatedSyntax": false
894
898
  }
899
+ }
900
+ ]
901
+ }
902
+ ]
903
+ },
904
+ {
905
+ "$type": "Group",
906
+ "elements": [
907
+ {
908
+ "$type": "Assignment",
909
+ "feature": "props",
910
+ "operator": "+=",
911
+ "terminal": {
912
+ "$type": "RuleCall",
913
+ "rule": {
914
+ "$ref": "#/rules@22"
895
915
  },
916
+ "arguments": []
917
+ }
918
+ },
919
+ {
920
+ "$type": "Group",
921
+ "elements": [
896
922
  {
897
923
  "$type": "Assignment",
898
- "feature": "title",
899
- "operator": "=",
924
+ "feature": "props",
925
+ "operator": "+=",
900
926
  "terminal": {
901
927
  "$type": "RuleCall",
902
928
  "rule": {
903
929
  "$ref": "#/rules@22"
904
930
  },
905
931
  "arguments": []
906
- },
932
+ }
933
+ },
934
+ {
935
+ "$type": "Group",
936
+ "elements": [
937
+ {
938
+ "$type": "Assignment",
939
+ "feature": "props",
940
+ "operator": "+=",
941
+ "terminal": {
942
+ "$type": "RuleCall",
943
+ "rule": {
944
+ "$ref": "#/rules@22"
945
+ },
946
+ "arguments": []
947
+ }
948
+ },
949
+ {
950
+ "$type": "Assignment",
951
+ "feature": "props",
952
+ "operator": "+=",
953
+ "terminal": {
954
+ "$type": "RuleCall",
955
+ "rule": {
956
+ "$ref": "#/rules@22"
957
+ },
958
+ "arguments": []
959
+ },
960
+ "cardinality": "?"
961
+ }
962
+ ],
907
963
  "cardinality": "?"
908
964
  }
909
- ]
965
+ ],
966
+ "cardinality": "?"
910
967
  }
911
- ]
968
+ ],
969
+ "cardinality": "?"
912
970
  },
913
971
  {
914
972
  "$type": "Assignment",
@@ -1864,7 +1922,14 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
1864
1922
  {
1865
1923
  "$type": "RuleCall",
1866
1924
  "rule": {
1867
- "$ref": "#/rules@47"
1925
+ "$ref": "#/rules@49"
1926
+ },
1927
+ "arguments": []
1928
+ },
1929
+ {
1930
+ "$type": "RuleCall",
1931
+ "rule": {
1932
+ "$ref": "#/rules@48"
1868
1933
  },
1869
1934
  "arguments": []
1870
1935
  }
@@ -1908,7 +1973,7 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
1908
1973
  "terminal": {
1909
1974
  "$type": "RuleCall",
1910
1975
  "rule": {
1911
- "$ref": "#/rules@48"
1976
+ "$ref": "#/rules@50"
1912
1977
  },
1913
1978
  "arguments": []
1914
1979
  }
@@ -1930,7 +1995,7 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
1930
1995
  "terminal": {
1931
1996
  "$type": "RuleCall",
1932
1997
  "rule": {
1933
- "$ref": "#/rules@48"
1998
+ "$ref": "#/rules@50"
1934
1999
  },
1935
2000
  "arguments": []
1936
2001
  }
@@ -1947,6 +2012,69 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
1947
2012
  "parameters": [],
1948
2013
  "wildcard": false
1949
2014
  },
2015
+ {
2016
+ "$type": "ParserRule",
2017
+ "name": "ViewRuleLayoutDirection",
2018
+ "dataType": "string",
2019
+ "definition": {
2020
+ "$type": "Alternatives",
2021
+ "elements": [
2022
+ {
2023
+ "$type": "Keyword",
2024
+ "value": "TB"
2025
+ },
2026
+ {
2027
+ "$type": "Keyword",
2028
+ "value": "LR"
2029
+ },
2030
+ {
2031
+ "$type": "Keyword",
2032
+ "value": "BT"
2033
+ },
2034
+ {
2035
+ "$type": "Keyword",
2036
+ "value": "RL"
2037
+ }
2038
+ ]
2039
+ },
2040
+ "definesHiddenTokens": false,
2041
+ "entry": false,
2042
+ "fragment": false,
2043
+ "hiddenTokens": [],
2044
+ "parameters": [],
2045
+ "wildcard": false
2046
+ },
2047
+ {
2048
+ "$type": "ParserRule",
2049
+ "name": "ViewRuleAutoLayout",
2050
+ "definition": {
2051
+ "$type": "Group",
2052
+ "elements": [
2053
+ {
2054
+ "$type": "Keyword",
2055
+ "value": "autoLayout"
2056
+ },
2057
+ {
2058
+ "$type": "Assignment",
2059
+ "feature": "direction",
2060
+ "operator": "=",
2061
+ "terminal": {
2062
+ "$type": "RuleCall",
2063
+ "rule": {
2064
+ "$ref": "#/rules@47"
2065
+ },
2066
+ "arguments": []
2067
+ }
2068
+ }
2069
+ ]
2070
+ },
2071
+ "definesHiddenTokens": false,
2072
+ "entry": false,
2073
+ "fragment": false,
2074
+ "hiddenTokens": [],
2075
+ "parameters": [],
2076
+ "wildcard": false
2077
+ },
1950
2078
  {
1951
2079
  "$type": "ParserRule",
1952
2080
  "name": "ViewRuleStyle",
@@ -1964,7 +2092,7 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
1964
2092
  "terminal": {
1965
2093
  "$type": "RuleCall",
1966
2094
  "rule": {
1967
- "$ref": "#/rules@49"
2095
+ "$ref": "#/rules@51"
1968
2096
  },
1969
2097
  "arguments": []
1970
2098
  }
@@ -1986,7 +2114,7 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
1986
2114
  "terminal": {
1987
2115
  "$type": "RuleCall",
1988
2116
  "rule": {
1989
- "$ref": "#/rules@49"
2117
+ "$ref": "#/rules@51"
1990
2118
  },
1991
2119
  "arguments": []
1992
2120
  }
@@ -2069,7 +2197,7 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
2069
2197
  "terminal": {
2070
2198
  "$type": "RuleCall",
2071
2199
  "rule": {
2072
- "$ref": "#/rules@49"
2200
+ "$ref": "#/rules@51"
2073
2201
  },
2074
2202
  "arguments": []
2075
2203
  }
@@ -2101,7 +2229,7 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
2101
2229
  {
2102
2230
  "$type": "RuleCall",
2103
2231
  "rule": {
2104
- "$ref": "#/rules@49"
2232
+ "$ref": "#/rules@51"
2105
2233
  },
2106
2234
  "arguments": []
2107
2235
  },
@@ -2131,7 +2259,7 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
2131
2259
  "terminal": {
2132
2260
  "$type": "RuleCall",
2133
2261
  "rule": {
2134
- "$ref": "#/rules@49"
2262
+ "$ref": "#/rules@51"
2135
2263
  },
2136
2264
  "arguments": []
2137
2265
  }
@@ -2200,7 +2328,7 @@ export const LikeC4Grammar = () => loadedLikeC4Grammar ?? (loadedLikeC4Grammar =
2200
2328
  {
2201
2329
  "$type": "RuleCall",
2202
2330
  "rule": {
2203
- "$ref": "#/rules@50"
2331
+ "$ref": "#/rules@52"
2204
2332
  },
2205
2333
  "arguments": []
2206
2334
  }
@@ -56,13 +56,15 @@ export class LikeC4DocumentSymbolProvider {
56
56
  }
57
57
  if (specSymbols.length === 0)
58
58
  return [];
59
- return [{
59
+ return [
60
+ {
60
61
  kind: SymbolKind.Class,
61
62
  name: astSpec.name,
62
63
  range: cstModel.range,
63
64
  selectionRange: specKeywordNode.range,
64
65
  children: specSymbols
65
- }];
66
+ }
67
+ ];
66
68
  };
67
69
  getModelSymbols = (astModel) => {
68
70
  const cstModel = astModel?.$cstNode;
@@ -71,13 +73,15 @@ export class LikeC4DocumentSymbolProvider {
71
73
  const nameNode = findNodeForProperty(cstModel, 'name');
72
74
  if (!nameNode)
73
75
  return [];
74
- return [{
76
+ return [
77
+ {
75
78
  kind: SymbolKind.Class,
76
79
  name: astModel.name,
77
80
  range: cstModel.range,
78
81
  selectionRange: nameNode.range,
79
82
  children: astModel.elements.flatMap(this.getElementsSymbol)
80
- }];
83
+ }
84
+ ];
81
85
  };
82
86
  getElementsSymbol = (el) => {
83
87
  if (ast.isExtendElement(el)) {
@@ -93,13 +97,15 @@ export class LikeC4DocumentSymbolProvider {
93
97
  const nameNode = astElement.element.$cstNode;
94
98
  if (!cst || !nameNode)
95
99
  return [];
96
- return [{
100
+ return [
101
+ {
97
102
  kind: SymbolKind.Constructor,
98
103
  name: nameNode.text,
99
104
  range: cst.range,
100
105
  selectionRange: nameNode.range,
101
106
  children: astElement.body.elements.flatMap(this.getElementsSymbol)
102
- }];
107
+ }
108
+ ];
103
109
  };
104
110
  getElementSymbol = (astElement) => {
105
111
  const cst = astElement.$cstNode;
@@ -110,15 +116,18 @@ export class LikeC4DocumentSymbolProvider {
110
116
  return [];
111
117
  const name = astElement.name;
112
118
  const kind = astElement.kind.$refText;
113
- const detail = kind + (astElement.title ? ': ' + astElement.title : '').replaceAll('\n', ' ').trim();
114
- return [{
119
+ // TODO: return the title as well
120
+ const detail = kind; // + (astElement.title ? ': ' + astElement.title : '').replaceAll('\n', ' ').trim()
121
+ return [
122
+ {
115
123
  kind: SymbolKind.Constructor,
116
124
  name: name,
117
125
  range: cst.range,
118
126
  selectionRange: nameNode.range,
119
127
  detail,
120
128
  children: astElement.body?.elements.flatMap(this.getElementsSymbol) ?? []
121
- }];
129
+ }
130
+ ];
122
131
  };
123
132
  getModelViewsSymbols = (astViews) => {
124
133
  const cst = astViews?.$cstNode;
@@ -127,12 +136,14 @@ export class LikeC4DocumentSymbolProvider {
127
136
  const nameNode = findNodeForProperty(cst, 'name');
128
137
  if (!nameNode)
129
138
  return [];
130
- return [{
139
+ return [
140
+ {
131
141
  kind: SymbolKind.Class,
132
142
  name: astViews.name,
133
143
  range: cst.range,
134
144
  selectionRange: nameNode.range,
135
145
  children: []
136
- }];
146
+ }
147
+ ];
137
148
  };
138
149
  }
@@ -35,10 +35,7 @@ export class LikeC4HoverProvider extends AstNodeHoverProvider {
35
35
  if (!el) {
36
36
  return;
37
37
  }
38
- const lines = [
39
- el.id,
40
- `${el.kind}: **${el.title}**`
41
- ];
38
+ const lines = [el.id, `${el.kind}: **${el.title}**`];
42
39
  if (el.tags && el.tags.length > 0) {
43
40
  lines.push(' \n', el.tags.map(t => '#' + t).join(', '));
44
41
  }
@@ -7,13 +7,13 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
7
7
  const keyword = (keyword, _index) => acceptor({
8
8
  node,
9
9
  keyword,
10
- type: SemanticTokenTypes.keyword,
10
+ type: SemanticTokenTypes.keyword
11
11
  });
12
12
  if (ast.isElementRef(node) || ast.isStrictElementRef(node)) {
13
13
  acceptor({
14
14
  node,
15
15
  property: 'el',
16
- type: isElementRefHead(node) ? SemanticTokenTypes.variable : SemanticTokenTypes.property,
16
+ type: isElementRefHead(node) ? SemanticTokenTypes.variable : SemanticTokenTypes.property
17
17
  });
18
18
  // acceptor({
19
19
  // node,
@@ -34,7 +34,9 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
34
34
  });
35
35
  return;
36
36
  }
37
- if (ast.isRelationExpression(node) || ast.isIncomingExpression(node) || ast.isOutgoingExpression(node)) {
37
+ if (ast.isRelationExpression(node) ||
38
+ ast.isIncomingExpression(node) ||
39
+ ast.isOutgoingExpression(node)) {
38
40
  keyword('->');
39
41
  return;
40
42
  }
@@ -74,9 +76,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
74
76
  node,
75
77
  property: 'name',
76
78
  type: SemanticTokenTypes.type,
77
- modifier: [
78
- SemanticTokenModifiers.definition,
79
- ]
79
+ modifier: [SemanticTokenModifiers.definition]
80
80
  });
81
81
  return;
82
82
  }
@@ -126,7 +126,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
126
126
  acceptor({
127
127
  node,
128
128
  property: 'key',
129
- type: SemanticTokenTypes.keyword,
129
+ type: SemanticTokenTypes.keyword
130
130
  });
131
131
  acceptor({
132
132
  node,
@@ -139,7 +139,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
139
139
  acceptor({
140
140
  node,
141
141
  property: 'key',
142
- type: SemanticTokenTypes.keyword,
142
+ type: SemanticTokenTypes.keyword
143
143
  });
144
144
  if ('value' in node) {
145
145
  acceptor({
@@ -188,6 +188,10 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
188
188
  // keyword('steps')
189
189
  // return
190
190
  // }
191
+ if (ast.isViewRuleAutoLayout(node)) {
192
+ keyword('autoLayout');
193
+ return;
194
+ }
191
195
  if (ast.isViewRuleStyle(node)) {
192
196
  keyword('style');
193
197
  return;
@@ -203,9 +207,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
203
207
  node,
204
208
  property: 'name',
205
209
  type: SemanticTokenTypes.variable,
206
- modifier: [
207
- SemanticTokenModifiers.declaration,
208
- ]
210
+ modifier: [SemanticTokenModifiers.declaration]
209
211
  });
210
212
  acceptor({
211
213
  node,
@@ -226,7 +228,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
226
228
  acceptor({
227
229
  node,
228
230
  property: 'name',
229
- type: SemanticTokenTypes.variable,
231
+ type: SemanticTokenTypes.variable
230
232
  });
231
233
  }
232
234
  if (node.viewOf) {
@@ -1,4 +1,3 @@
1
- export * from './CodeLensProvider';
2
1
  export * from './DocumentSymbolProvider';
3
2
  export * from './HoverProvider';
4
3
  export * from './SemanticTokenProvider';
package/dist/lsp/index.js CHANGED
@@ -1,4 +1,3 @@
1
- export * from './CodeLensProvider';
2
1
  export * from './DocumentSymbolProvider';
3
2
  export * from './HoverProvider';
4
3
  export * from './SemanticTokenProvider';
@@ -42,7 +42,7 @@ export class FqnIndex {
42
42
  directChildrenOf(parent) {
43
43
  return this.#index
44
44
  .entriesGroupedByKey()
45
- .flatMap(([fqn, descrs]) => (descrs.length === 1 && parentFqn(fqn) === parent) ? descrs : []);
45
+ .flatMap(([fqn, descrs]) => (descrs.length === 1 && parentFqn(fqn) === parent ? descrs : []));
46
46
  }
47
47
  uniqueDescedants(parent) {
48
48
  return new StreamImpl(() => {
@@ -61,7 +61,7 @@ export class FqnIndex {
61
61
  }
62
62
  }
63
63
  });
64
- if ((children.length + descedants.length) === 0) {
64
+ if (children.length + descedants.length === 0) {
65
65
  return null;
66
66
  }
67
67
  const nested = new MultiMap(children.map(([_fqn, desc]) => [desc.name, desc]));
@@ -72,9 +72,9 @@ export class FqnIndex {
72
72
  }
73
73
  return nested
74
74
  .entriesGroupedByKey()
75
- .flatMap(([_name, descrs]) => descrs.length === 1 ? descrs : [])
75
+ .flatMap(([_name, descrs]) => (descrs.length === 1 ? descrs : []))
76
76
  .iterator();
77
- }, (iterator) => {
77
+ }, iterator => {
78
78
  if (iterator) {
79
79
  return iterator.next();
80
80
  }
@@ -128,7 +128,9 @@ export class FqnIndex {
128
128
  }
129
129
  cleanIndexedElements(docUri) {
130
130
  const docUriAsString = docUri.toString();
131
- const toDelete = this.#index.entries().filter(([, indexed]) => indexed.documentUri.toString() === docUriAsString);
131
+ const toDelete = this.#index
132
+ .entries()
133
+ .filter(([, indexed]) => indexed.documentUri.toString() === docUriAsString);
132
134
  for (const [fqn, indexed] of toDelete.toArray()) {
133
135
  this.#index.delete(fqn, indexed);
134
136
  }
@@ -67,7 +67,7 @@ export class LikeC4ModelBuilder {
67
67
  return {
68
68
  ...(kind.shape !== DefaultElementShape ? { shape: kind.shape } : {}),
69
69
  ...(kind.color !== DefaultThemeColor ? { color: kind.color } : {}),
70
- ...(omit(['astPath'], el))
70
+ ...omit(['astPath'], el)
71
71
  };
72
72
  }
73
73
  return null;
@@ -127,7 +127,7 @@ export class LikeC4ModelBuilder {
127
127
  const styleProps = toElementStyle(style?.props);
128
128
  specification.kinds[kind.name] = {
129
129
  color: styleProps.color ?? DefaultThemeColor,
130
- shape: styleProps.shape ?? DefaultElementShape,
130
+ shape: styleProps.shape ?? DefaultElementShape
131
131
  };
132
132
  }
133
133
  catch (e) {
@@ -181,10 +181,11 @@ export class LikeC4ModelBuilder {
181
181
  const styleProps = astNode.body?.props.find(ast.isElementStyleProperty)?.props;
182
182
  const { color, shape } = toElementStyle(styleProps);
183
183
  const astPath = this.getAstNodePath(astNode);
184
- const props = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
185
- const title = astNode.title ?? props.find(p => p.key === 'title')?.value;
186
- const description = props.find(p => p.key === 'description')?.value;
187
- const technology = props.find(p => p.key === 'technology')?.value;
184
+ let [title, description, technology] = astNode.props;
185
+ const bodyProps = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
186
+ title = title ?? bodyProps.find(p => p.key === 'title')?.value;
187
+ description = description ?? bodyProps.find(p => p.key === 'description')?.value;
188
+ technology = technology ?? bodyProps.find(p => p.key === 'technology')?.value;
188
189
  return {
189
190
  id,
190
191
  kind,
@@ -194,7 +195,7 @@ export class LikeC4ModelBuilder {
194
195
  ...(description && { description }),
195
196
  ...(tags.length > 0 ? { tags } : {}),
196
197
  ...(shape && shape !== DefaultElementShape ? { shape } : {}),
197
- ...(color && color !== DefaultThemeColor ? { color } : {}),
198
+ ...(color && color !== DefaultThemeColor ? { color } : {})
198
199
  };
199
200
  }
200
201
  parseRelation(astNode) {
@@ -207,11 +208,11 @@ export class LikeC4ModelBuilder {
207
208
  target
208
209
  };
209
210
  const id = objectHash(hashdata);
210
- const title = astNode.definition?.props.find(p => p.key === 'title')?.value ?? '';
211
+ const title = astNode.title ?? astNode.definition?.props.find(p => p.key === 'title')?.value ?? '';
211
212
  return {
212
213
  id,
213
214
  ...hashdata,
214
- title: astNode.title ?? title
215
+ title
215
216
  };
216
217
  }
217
218
  parseElementExpression(astNode) {
@@ -252,7 +253,7 @@ export class LikeC4ModelBuilder {
252
253
  if (ast.isRelationExpression(astNode)) {
253
254
  return {
254
255
  source: this.parseElementExpression(astNode.source),
255
- target: this.parseElementExpression(astNode.target),
256
+ target: this.parseElementExpression(astNode.target)
256
257
  };
257
258
  }
258
259
  failExpectedNever(astNode);
@@ -274,6 +275,11 @@ export class LikeC4ModelBuilder {
274
275
  }
275
276
  };
276
277
  }
278
+ if (ast.isViewRuleAutoLayout(astNode)) {
279
+ return {
280
+ autoLayout: astNode.direction
281
+ };
282
+ }
277
283
  failExpectedNever(astNode);
278
284
  }
279
285
  parseElementView(astNode) {
package/dist/module.js CHANGED
@@ -1,10 +1,11 @@
1
- import { createDefaultModule, inject, createDefaultSharedModule } from 'langium';
1
+ import { createDefaultModule, createDefaultSharedModule, inject } from 'langium';
2
2
  import { LikeC4GeneratedModule, LikeC4GeneratedSharedModule } from './generated/module';
3
- import { LikeC4ScopeComputation, LikeC4ScopeProvider } from './references';
3
+ import { LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
4
4
  import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator } from './model';
5
- import { registerValidationChecks } from './validation';
6
- import { LikeC4CodeLensProvider, LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
5
+ import { LikeC4ScopeComputation, LikeC4ScopeProvider } from './references';
7
6
  import { registerProtocolHandlers } from './registerProtocolHandlers';
7
+ import { LikeC4CodeLensProvider, LikeC4WorkspaceManager } from './shared';
8
+ import { registerValidationChecks } from './validation';
8
9
  function bind(Type) {
9
10
  return (services) => new Type(services);
10
11
  }
@@ -12,7 +13,7 @@ export const LikeC4Module = {
12
13
  likec4: {
13
14
  FqnIndex: bind(FqnIndex),
14
15
  ModelBuilder: bind(LikeC4ModelBuilder),
15
- ModelLocator: bind(LikeC4ModelLocator),
16
+ ModelLocator: bind(LikeC4ModelLocator)
16
17
  // Model: bind(LikeC4Model),
17
18
  // Model: bind(LikeC4Model),
18
19
  // SpecIndex: bind(LikeC4SpecIndex),
@@ -21,7 +22,7 @@ export const LikeC4Module = {
21
22
  lsp: {
22
23
  DocumentSymbolProvider: bind(LikeC4DocumentSymbolProvider),
23
24
  SemanticTokenProvider: bind(LikeC4SemanticTokenProvider),
24
- HoverProvider: bind(LikeC4HoverProvider),
25
+ HoverProvider: bind(LikeC4HoverProvider)
25
26
  },
26
27
  //
27
28
  // // Formatter: bind(LikeC4Formatter),
@@ -34,8 +35,11 @@ export const LikeC4Module = {
34
35
  };
35
36
  const LikeC4SharedModule = {
36
37
  ...LikeC4GeneratedSharedModule,
38
+ workspace: {
39
+ WorkspaceManager: services => new LikeC4WorkspaceManager(services)
40
+ },
37
41
  lsp: {
38
- CodeLensProvider: (services) => new LikeC4CodeLensProvider(services)
42
+ CodeLensProvider: services => new LikeC4CodeLensProvider(services)
39
43
  }
40
44
  };
41
45
  export function createLanguageServices(context) {
@@ -9,7 +9,7 @@ export declare class LikeC4ScopeProvider extends DefaultScopeProvider {
9
9
  getScope(context: ReferenceInfo): Scope;
10
10
  protected computeScope(node: AstNode, referenceType: string): Scope;
11
11
  /**
12
- * Create a global scope filtered for the given reference type.
13
- */
12
+ * Create a global scope filtered for the given reference type.
13
+ */
14
14
  protected getGlobalScope(referenceType: string): Scope;
15
15
  }
@@ -19,7 +19,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
19
19
  return this.fqnIndex.uniqueDescedants(fqn).iterator();
20
20
  }
21
21
  return null;
22
- }, (iterator) => {
22
+ }, iterator => {
23
23
  if (iterator) {
24
24
  return iterator.next();
25
25
  }
@@ -37,7 +37,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
37
37
  return this.fqnIndex.uniqueDescedants(fqn).iterator();
38
38
  }
39
39
  return null;
40
- }, (iterator) => {
40
+ }, iterator => {
41
41
  if (iterator) {
42
42
  return iterator.next();
43
43
  }
@@ -102,8 +102,8 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
102
102
  }, this.getGlobalScope(referenceType));
103
103
  }
104
104
  /**
105
- * Create a global scope filtered for the given reference type.
106
- */
105
+ * Create a global scope filtered for the given reference type.
106
+ */
107
107
  getGlobalScope(referenceType) {
108
108
  return new StreamScope(this.indexManager.allElements(referenceType));
109
109
  }
@@ -9,7 +9,7 @@ export class LikeC4CodeLensProvider {
9
9
  if (!isParsedLikeC4LangiumDocument(doc)) {
10
10
  return;
11
11
  }
12
- return doc.parseResult.value.views?.views.flatMap((ast) => {
12
+ return doc.parseResult.value.views?.views.flatMap(ast => {
13
13
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
14
  const viewId = ElementViewOps.readId(ast);
15
15
  const range = ast.$cstNode?.range;
@@ -27,7 +27,7 @@ export class LikeC4CodeLensProvider {
27
27
  command: {
28
28
  command: 'likec4.open-preview',
29
29
  arguments: [viewId],
30
- title: 'open preview',
30
+ title: 'open preview'
31
31
  }
32
32
  };
33
33
  });
@@ -0,0 +1,13 @@
1
+ import type { LangiumDocument, LangiumDocumentFactory, LangiumSharedServices } from 'langium';
2
+ import { DefaultWorkspaceManager } from 'langium';
3
+ import type { WorkspaceFolder } from 'vscode-languageserver-protocol';
4
+ export declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
5
+ protected readonly documentFactory: LangiumDocumentFactory;
6
+ constructor(services: LangiumSharedServices);
7
+ /**
8
+ * Load all additional documents that shall be visible in the context of the given workspace
9
+ * folders and add them to the collector. This can be used to include built-in libraries of
10
+ * your language, which can be either loaded from provided files or constructed in memory.
11
+ */
12
+ protected loadAdditionalDocuments(folders: WorkspaceFolder[], collector: (document: LangiumDocument) => void): Promise<void>;
13
+ }
@@ -0,0 +1,19 @@
1
+ import { DefaultWorkspaceManager } from 'langium';
2
+ import { URI } from 'vscode-uri';
3
+ import * as builtin from '../builtin';
4
+ export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
5
+ documentFactory;
6
+ constructor(services) {
7
+ super(services);
8
+ this.documentFactory = services.workspace.LangiumDocumentFactory;
9
+ }
10
+ /**
11
+ * Load all additional documents that shall be visible in the context of the given workspace
12
+ * folders and add them to the collector. This can be used to include built-in libraries of
13
+ * your language, which can be either loaded from provided files or constructed in memory.
14
+ */
15
+ loadAdditionalDocuments(folders, collector) {
16
+ collector(this.documentFactory.fromString(builtin.specification.document, URI.parse(builtin.specification.uri)));
17
+ return Promise.resolve();
18
+ }
19
+ }
@@ -0,0 +1,2 @@
1
+ export * from './CodeLensProvider';
2
+ export * from './WorkspaceManager';
@@ -0,0 +1,2 @@
1
+ export * from './CodeLensProvider';
2
+ export * from './WorkspaceManager';
@@ -7,8 +7,10 @@ export function createTestServices() {
7
7
  const langiumDocuments = services.shared.workspace.LangiumDocuments;
8
8
  const documentBuilder = services.shared.workspace.DocumentBuilder;
9
9
  const modelBuilder = services.likec4.ModelBuilder;
10
+ const initPromise = services.shared.workspace.WorkspaceManager.initializeWorkspace([]);
10
11
  let documentIndex = 1;
11
12
  const parse = async (input, uri) => {
13
+ await initPromise;
12
14
  uri = uri ?? `${documentIndex++}${metaData.fileExtensions[0]}`;
13
15
  const document = services.shared.workspace.LangiumDocumentFactory.fromString(input, URI.file(uri));
14
16
  langiumDocuments.addDocument(document);
@@ -16,6 +18,7 @@ export function createTestServices() {
16
18
  return document;
17
19
  };
18
20
  const validate = async (input) => {
21
+ await initPromise;
19
22
  const document = typeof input === 'string' ? await parse(input) : input;
20
23
  await documentBuilder.build([document], { validationChecks: 'all' });
21
24
  const diagnostics = document.diagnostics ?? [];
@@ -27,6 +30,7 @@ export function createTestServices() {
27
30
  };
28
31
  };
29
32
  const validateAll = async () => {
33
+ await initPromise;
30
34
  const docs = langiumDocuments.all.toArray();
31
35
  await documentBuilder.build(docs, { validationChecks: 'all' });
32
36
  const diagnostics = docs.flatMap(doc => doc.diagnostics ?? []);
@@ -5,7 +5,7 @@ export const elementChecks = (services) => {
5
5
  if (!fqn) {
6
6
  accept('error', 'Not indexed', {
7
7
  node: el,
8
- property: 'name',
8
+ property: 'name'
9
9
  });
10
10
  return;
11
11
  }
@@ -13,7 +13,7 @@ export const elementChecks = (services) => {
13
13
  if (withSameFqn.length > 1) {
14
14
  accept('error', `Duplicate element name ${el.name !== fqn ? el.name + ' (' + fqn + ')' : el.name}`, {
15
15
  node: el,
16
- property: 'name',
16
+ property: 'name'
17
17
  });
18
18
  }
19
19
  };
@@ -1,4 +1,5 @@
1
1
  import { elementChecks } from './element';
2
+ import { relationChecks } from './relation';
2
3
  import { elementKindChecks, tagChecks } from './specification';
3
4
  import { viewChecks } from './view';
4
5
  export function registerValidationChecks(services) {
@@ -15,6 +16,7 @@ export function registerValidationChecks(services) {
15
16
  ElementView: viewChecks(services),
16
17
  Element: elementChecks(services),
17
18
  ElementKind: elementKindChecks(services),
18
- Tag: tagChecks(services),
19
+ Relation: relationChecks(services),
20
+ Tag: tagChecks(services)
19
21
  });
20
22
  }
@@ -0,0 +1,4 @@
1
+ import type { ValidationCheck } from 'langium';
2
+ import type { ast } from '../ast';
3
+ import type { LikeC4Services } from '../module';
4
+ export declare const relationChecks: (services: LikeC4Services) => ValidationCheck<ast.Relation>;
@@ -0,0 +1,53 @@
1
+ import { resolveRelationPoints } from '../ast';
2
+ import { isSameHierarchy } from '@likec4/core/utils';
3
+ export const relationChecks = (services) => {
4
+ const fqnIndex = services.likec4.FqnIndex;
5
+ return (el, accept) => {
6
+ try {
7
+ const coupling = resolveRelationPoints(el);
8
+ const target = fqnIndex.get(coupling.target);
9
+ if (!target) {
10
+ return accept('error', 'Invalid target', {
11
+ node: el,
12
+ property: 'target'
13
+ });
14
+ }
15
+ const source = fqnIndex.get(coupling.source);
16
+ if (!source) {
17
+ return accept('error', 'Invalid source', {
18
+ node: el
19
+ });
20
+ }
21
+ if (isSameHierarchy(source, target)) {
22
+ return accept('error', 'Invalid relation (same hierarchy)', {
23
+ node: el
24
+ });
25
+ }
26
+ }
27
+ catch (e) {
28
+ if (e instanceof Error) {
29
+ return accept('error', e.message, {
30
+ node: el
31
+ });
32
+ }
33
+ accept('error', 'Invalid relation', {
34
+ node: el
35
+ });
36
+ }
37
+ // const fqn = fqnIndex.get(el)
38
+ // if (!fqn) {
39
+ // accept('error', 'Not indexed', {
40
+ // node: el,
41
+ // property: 'name',
42
+ // })
43
+ // return
44
+ // }
45
+ // const withSameFqn = fqnIndex.byFqn(fqn)
46
+ // if (withSameFqn.length > 1) {
47
+ // accept('error', `Duplicate element name ${el.name !== fqn ? el.name +' (' + fqn + ')' : el.name}`, {
48
+ // node: el,
49
+ // property: 'name',
50
+ // })
51
+ // }
52
+ };
53
+ };
@@ -2,14 +2,15 @@ import { ast } from '../ast';
2
2
  export const elementKindChecks = (services) => {
3
3
  const index = services.shared.workspace.IndexManager;
4
4
  return (node, accept) => {
5
- const sameKinds = index.allElements(ast.ElementKind)
5
+ const sameKinds = index
6
+ .allElements(ast.ElementKind)
6
7
  .filter(n => n.name === node.name)
7
8
  .limit(2)
8
9
  .count();
9
10
  if (sameKinds > 1) {
10
11
  accept('error', `Duplicate element kind '${node.name}'`, {
11
12
  node: node,
12
- property: 'name',
13
+ property: 'name'
13
14
  });
14
15
  }
15
16
  };
@@ -17,14 +18,15 @@ export const elementKindChecks = (services) => {
17
18
  export const tagChecks = (services) => {
18
19
  const index = services.shared.workspace.IndexManager;
19
20
  return (node, accept) => {
20
- const sameKinds = index.allElements(ast.Tag)
21
+ const sameKinds = index
22
+ .allElements(ast.Tag)
21
23
  .filter(n => n.name === node.name)
22
24
  .limit(2)
23
25
  .count();
24
26
  if (sameKinds > 1) {
25
27
  accept('error', `Duplicate tag '${node.name}'`, {
26
28
  node: node,
27
- property: 'name',
29
+ property: 'name'
28
30
  });
29
31
  }
30
32
  };
@@ -5,7 +5,8 @@ export const viewChecks = (services) => {
5
5
  if (!el.name) {
6
6
  return;
7
7
  }
8
- const anotherViews = index.allElements(ast.View)
8
+ const anotherViews = index
9
+ .allElements(ast.View)
9
10
  .filter(n => n.name === el.name)
10
11
  .limit(2)
11
12
  .count();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
- "version": "0.2.2",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "bugs": "https://github.com/likec4/likec4/issues",
6
6
  "homepage": "https://like-c4.dev",
@@ -24,6 +24,11 @@
24
24
  "types": "./dist/protocol.d.ts",
25
25
  "import": "./dist/protocol.js",
26
26
  "require": "./dist/protocol.cjs"
27
+ },
28
+ "./builtin": {
29
+ "types": "./dist/builtin.d.ts",
30
+ "import": "./dist/builtin.js",
31
+ "require": "./dist/builtin.cjs"
27
32
  }
28
33
  },
29
34
  "publishConfig": {
@@ -41,6 +46,11 @@
41
46
  "types": "./dist/protocol.d.ts",
42
47
  "import": "./dist/protocol.js",
43
48
  "require": "./dist/protocol.cjs"
49
+ },
50
+ "./builtin": {
51
+ "types": "./dist/builtin.d.ts",
52
+ "import": "./dist/builtin.js",
53
+ "require": "./dist/builtin.cjs"
44
54
  }
45
55
  }
46
56
  },
@@ -53,14 +63,14 @@
53
63
  "watch:ts": "tsc --watch",
54
64
  "generate": "langium generate",
55
65
  "build": "run-s 'build:langium' 'build:ts'",
56
- "dev": "run-p 'watch:*'",
66
+ "dev": "run generate && run-p 'watch:*'",
57
67
  "lint": "run -T eslint src/ --fix",
58
- "clean": "rimraf dist",
68
+ "clean": "run -T rimraf dist contrib",
59
69
  "test": "vitest run",
60
70
  "test:watch": "vitest"
61
71
  },
62
72
  "dependencies": {
63
- "@likec4/core": "0.2.2",
73
+ "@likec4/core": "0.5.0",
64
74
  "@mobily/ts-belt": "^3.13.1",
65
75
  "langium": "^1.1.0",
66
76
  "nanoid": "^4.0.2",
@@ -68,17 +78,17 @@
68
78
  "rambdax": "^9.1.0",
69
79
  "strip-indent": "^4.0.0",
70
80
  "tiny-invariant": "^1.3.1",
71
- "type-fest": "^3.7.2",
81
+ "type-fest": "^3.8.0",
72
82
  "vscode-languageserver-protocol": "3.17.2",
73
83
  "vscode-uri": "3.0.7"
74
84
  },
75
85
  "devDependencies": {
76
- "@types/node": "^18.15.6",
86
+ "@types/node": "^18.15.11",
77
87
  "@types/object-hash": "^3.0.2",
78
88
  "langium-cli": "^1.1.0",
79
89
  "npm-run-all": "^4.1.5",
80
- "typescript": "^5.0.3",
90
+ "typescript": "^5.0.4",
81
91
  "vite": "^4.2.1",
82
- "vitest": "^0.29.8"
92
+ "vitest": "^0.30.1"
83
93
  }
84
94
  }
File without changes