@likec4/language-server 1.42.1 → 1.43.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.
@@ -1,7 +1,12 @@
1
1
  import type { AstNode } from 'langium';
2
2
  import { type SemanticTokenAcceptor, AbstractSemanticTokenProvider } from 'langium/lsp';
3
+ import type { LikeC4Services } from '../module';
3
4
  export declare class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
5
+ private rules;
6
+ constructor(services: LikeC4Services);
7
+ protected initRules(): void;
4
8
  protected highlightElement(node: AstNode, acceptor: SemanticTokenAcceptor): void | undefined | 'prune';
5
9
  private highlightNameAndKind;
6
10
  private highlightView;
11
+ private mark;
7
12
  }
@@ -1,350 +1,276 @@
1
- import { AbstractSemanticTokenProvider } from "langium/lsp";
1
+ import { invariant, isAnyOf, nonNullable } from "@likec4/core/utils";
2
+ import {
3
+ AbstractSemanticTokenProvider
4
+ } from "langium/lsp";
2
5
  import { isTruthy } from "remeda";
3
6
  import { SemanticTokenModifiers, SemanticTokenTypes } from "vscode-languageserver-types";
4
7
  import { ast } from "../ast.mjs";
8
+ import { logger } from "../logger.mjs";
9
+ const SemanticTypes = { ...SemanticTokenTypes };
10
+ const SemanticModifiers = { ...SemanticTokenModifiers };
11
+ function createSemanticTypeMethods(applyHighlight) {
12
+ const modifier = [];
13
+ const self = new Proxy({}, {
14
+ get(_, prop) {
15
+ if (prop === "modifier") {
16
+ return (mod) => {
17
+ modifier.push(mod);
18
+ return self;
19
+ };
20
+ }
21
+ if (prop in SemanticModifiers) {
22
+ modifier.push(SemanticModifiers[prop]);
23
+ return self;
24
+ }
25
+ if (prop in SemanticTypes) {
26
+ return () => applyHighlight(SemanticTypes[prop], modifier);
27
+ }
28
+ throw new Error(`Unknown semantic token type or modifier: ${prop}`);
29
+ }
30
+ });
31
+ return self;
32
+ }
33
+ const PRUNE = "Stop Highlighting";
34
+ const stopHighlight = () => {
35
+ throw PRUNE;
36
+ };
5
37
  export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
6
- // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
38
+ rules = [];
39
+ constructor(services) {
40
+ super(services);
41
+ this.initRules();
42
+ }
43
+ initRules() {
44
+ this.rules = [];
45
+ const when = (predicate, highlightFn) => {
46
+ const rule = { predicate, highlightFn };
47
+ this.rules.push(rule);
48
+ };
49
+ when(ast.isRelationshipKind, (mark) => {
50
+ mark.property("name").function();
51
+ });
52
+ when(isAnyOf(ast.isRelation, ast.isOutgoingRelationExpr, ast.isDeploymentRelation), (mark) => {
53
+ mark.property("kind").function();
54
+ });
55
+ when(ast.isLibIcon, (mark) => {
56
+ mark.property("name").definition.function();
57
+ stopHighlight();
58
+ });
59
+ when(isAnyOf(ast.isNavigateToProperty, ast.isRelationNavigateToProperty), (mark) => {
60
+ mark.property("value").readonly.definition.interface();
61
+ stopHighlight();
62
+ });
63
+ when(isAnyOf(ast.isFqnRefExpr, ast.isWildcardExpression), (mark) => {
64
+ mark.cst().readonly.definition.variable();
65
+ stopHighlight();
66
+ });
67
+ when(ast.isTagRef, (mark) => {
68
+ mark.cst().type();
69
+ stopHighlight();
70
+ });
71
+ when(ast.isRelationKindDotRef, (mark) => {
72
+ mark.cst().function();
73
+ stopHighlight();
74
+ });
75
+ when(ast.isWhereRelationKind, (mark) => {
76
+ if (isTruthy(mark.node.value)) {
77
+ mark.property("value").function();
78
+ }
79
+ });
80
+ when(isAnyOf(ast.isWhereElement, ast.isWhereRelation), (mark) => {
81
+ if (isTruthy(mark.node.value)) {
82
+ mark.property("value").readonly.definition.type();
83
+ }
84
+ });
85
+ when(isAnyOf(ast.isWhereRelationParticipantKind, ast.isWhereRelationParticipantTag), (mark) => {
86
+ mark.property("participant").keyword();
87
+ });
88
+ when(ast.isElementKindExpression, (mark) => {
89
+ if (isTruthy(mark.node.kind)) {
90
+ mark.property("kind").definition.type();
91
+ }
92
+ });
93
+ when(isAnyOf(ast.isGlobalStyleGroup, ast.isGlobalStyle), (mark) => {
94
+ mark.property("id").readonly.definition.variable();
95
+ });
96
+ when(ast.isViewRuleGlobalStyle, (mark) => {
97
+ mark.property("style").readonly.definition.variable();
98
+ });
99
+ when(isAnyOf(ast.isGlobalPredicateGroup, ast.isGlobalDynamicPredicateGroup), (mark) => {
100
+ mark.property("name").readonly.definition.variable();
101
+ });
102
+ when(ast.isViewRuleGlobalPredicateRef, (mark) => {
103
+ mark.property("predicate").readonly.definition.variable();
104
+ });
105
+ when(ast.isElementTagExpression, (mark) => {
106
+ if (isTruthy(mark.node.tag)) {
107
+ mark.property("tag").definition.type();
108
+ }
109
+ });
110
+ when(isAnyOf(ast.isFqnRef, ast.isStrictFqnRef), (mark) => {
111
+ mark.property("value").readonly.definition.variable();
112
+ if (!mark.node.parent) {
113
+ stopHighlight();
114
+ }
115
+ });
116
+ when(ast.isStrictFqnElementRef, (mark) => {
117
+ mark.property("el").readonly.definition.variable();
118
+ if (!mark.node.parent) {
119
+ stopHighlight();
120
+ }
121
+ });
122
+ when(ast.isSpecificationColor, (mark) => {
123
+ mark.keyword("color").keyword();
124
+ mark.property("name").readonly.declaration.type();
125
+ });
126
+ when(ast.isSpecificationTag, (mark) => {
127
+ if (isTruthy(mark.node.color)) {
128
+ mark.keyword("color").property();
129
+ }
130
+ });
131
+ when(
132
+ isAnyOf(
133
+ ast.isSpecificationElementKind,
134
+ ast.isSpecificationRelationshipKind,
135
+ ast.isSpecificationDeploymentNodeKind
136
+ ),
137
+ (mark) => {
138
+ mark.property("kind").readonly.declaration.type();
139
+ }
140
+ );
141
+ when(ast.isTag, (mark) => {
142
+ mark.property("name").definition.type();
143
+ });
144
+ when(ast.isOpacityProperty, (mark) => {
145
+ mark.property("value").number();
146
+ });
147
+ when(ast.isIconProperty, (mark) => {
148
+ if (mark.node.libicon || mark.node.value === "none") {
149
+ mark.property(mark.node.libicon ? "libicon" : "value").defaultLibrary.enum();
150
+ return;
151
+ }
152
+ mark.property("value").string();
153
+ });
154
+ when(ast.isLinkProperty, (mark) => {
155
+ if (isTruthy(mark.node.value)) {
156
+ mark.property("value").string();
157
+ }
158
+ });
159
+ when(ast.isColorProperty, (mark) => {
160
+ if (isTruthy(mark.node.customColor)) {
161
+ mark.property("customColor").enum();
162
+ }
163
+ if (isTruthy(mark.node.themeColor)) {
164
+ mark.property("themeColor").enum();
165
+ }
166
+ });
167
+ when(
168
+ isAnyOf(
169
+ ast.isShapeProperty,
170
+ ast.isArrowProperty,
171
+ ast.isLineProperty,
172
+ ast.isBorderProperty,
173
+ ast.isSizeProperty,
174
+ ast.isDynamicViewDisplayVariantProperty
175
+ ),
176
+ (mark) => {
177
+ if (isTruthy(mark.node.value)) {
178
+ mark.property("value").enum();
179
+ }
180
+ }
181
+ );
182
+ }
7
183
  highlightElement(node, acceptor) {
8
- if (ast.isElement(node) || ast.isDeploymentNode(node) || ast.isDeployedInstance(node)) {
9
- return this.highlightNameAndKind(node, acceptor);
10
- }
11
- if (ast.isLikeC4View(node)) {
12
- return this.highlightView(node, acceptor);
13
- }
14
- if (ast.isRelationshipKind(node)) {
15
- return acceptor({
16
- node,
17
- property: "name",
18
- type: SemanticTokenTypes.function
19
- });
20
- }
21
- if (ast.isLibIcon(node)) {
22
- acceptor({
23
- node,
24
- property: "name",
25
- type: SemanticTokenTypes.type,
26
- modifier: [SemanticTokenModifiers.definition]
27
- });
28
- return "prune";
29
- }
30
- if ((ast.isRelation(node) || ast.isOutgoingRelationExpr(node) || ast.isDeploymentRelation(node)) && "kind" in node) {
31
- acceptor({
32
- node,
33
- property: "kind",
34
- type: SemanticTokenTypes.function
35
- });
36
- }
37
- if (ast.isNavigateToProperty(node) || ast.isRelationNavigateToProperty(node)) {
38
- acceptor({
39
- node,
40
- property: "key",
41
- type: SemanticTokenTypes.property
42
- });
43
- acceptor({
44
- node,
45
- property: "value",
46
- type: SemanticTokenTypes.interface,
47
- modifier: [
48
- SemanticTokenModifiers.definition,
49
- SemanticTokenModifiers.readonly
50
- ]
51
- });
52
- return "prune";
53
- }
54
- if (ast.isFqnRefExpr(node) || ast.isWildcardExpression(node)) {
55
- acceptor({
56
- cst: node.$cstNode,
57
- type: SemanticTokenTypes.variable,
58
- modifier: [
59
- SemanticTokenModifiers.definition,
60
- SemanticTokenModifiers.readonly
61
- ]
62
- });
63
- return "prune";
64
- }
65
- if (ast.isRelationKindDotRef(node)) {
66
- acceptor({
67
- cst: node.$cstNode,
68
- type: SemanticTokenTypes.function
69
- });
70
- return "prune";
71
- }
72
- if (ast.isWhereRelationKind(node) && isTruthy(node.value)) {
73
- acceptor({
74
- node,
75
- property: "value",
76
- type: SemanticTokenTypes.function
77
- });
78
- return;
79
- }
80
- if ((ast.isWhereElement(node) || ast.isWhereRelation(node)) && isTruthy(node.value)) {
81
- acceptor({
82
- node,
83
- property: "value",
84
- type: SemanticTokenTypes.type,
85
- modifier: [
86
- SemanticTokenModifiers.definition,
87
- SemanticTokenModifiers.readonly
88
- ]
89
- });
90
- return;
91
- }
92
- if (ast.isWhereRelationParticipantKind(node) || ast.isWhereRelationParticipantTag(node)) {
93
- acceptor({
94
- node,
95
- property: "participant",
96
- type: SemanticTokenTypes.keyword
97
- });
98
- }
99
- if (ast.isElementKindExpression(node) && isTruthy(node.kind)) {
100
- acceptor({
101
- node,
102
- property: "kind",
103
- type: SemanticTokenTypes.type,
104
- modifier: [SemanticTokenModifiers.definition]
105
- });
106
- return;
107
- }
108
- if (ast.isGlobalStyleGroup(node) || ast.isGlobalStyle(node)) {
109
- acceptor({
110
- node,
111
- property: "id",
112
- type: SemanticTokenTypes.variable,
113
- modifier: [
114
- SemanticTokenModifiers.definition,
115
- SemanticTokenModifiers.readonly
116
- ]
117
- });
118
- return;
119
- }
120
- if (ast.isViewRuleGlobalStyle(node)) {
121
- acceptor({
122
- node,
123
- property: "style",
124
- type: SemanticTokenTypes.variable,
125
- modifier: [
126
- SemanticTokenModifiers.definition,
127
- SemanticTokenModifiers.readonly
128
- ]
129
- });
130
- return;
131
- }
132
- if (ast.isGlobalPredicateGroup(node) || ast.isGlobalDynamicPredicateGroup(node)) {
133
- acceptor({
134
- node,
135
- property: "name",
136
- type: SemanticTokenTypes.variable,
137
- modifier: [
138
- SemanticTokenModifiers.definition,
139
- SemanticTokenModifiers.readonly
140
- ]
141
- });
142
- return;
143
- }
144
- if (ast.isViewRuleGlobalPredicateRef(node)) {
145
- acceptor({
146
- node,
147
- property: "predicate",
148
- type: SemanticTokenTypes.variable,
149
- modifier: [
150
- SemanticTokenModifiers.definition,
151
- SemanticTokenModifiers.readonly
152
- ]
153
- });
154
- return;
155
- }
156
- if (ast.isElementTagExpression(node) && isTruthy(node.tag)) {
157
- acceptor({
158
- node,
159
- property: "tag",
160
- type: SemanticTokenTypes.type,
161
- modifier: [SemanticTokenModifiers.definition]
162
- });
163
- return;
164
- }
165
- if (ast.isFqnRef(node) || ast.isStrictFqnRef(node)) {
166
- acceptor({
167
- node,
168
- property: "value",
169
- type: SemanticTokenTypes.variable,
170
- modifier: [
171
- SemanticTokenModifiers.definition,
172
- SemanticTokenModifiers.readonly
173
- ]
174
- });
175
- return !node.parent ? "prune" : void 0;
176
- }
177
- if (ast.isStrictFqnElementRef(node)) {
178
- acceptor({
179
- node,
180
- property: "el",
181
- type: SemanticTokenTypes.variable,
182
- modifier: [
183
- SemanticTokenModifiers.definition,
184
- SemanticTokenModifiers.readonly
185
- ]
186
- });
187
- return !node.parent ? "prune" : void 0;
188
- }
189
- if (ast.isSpecificationColor(node)) {
190
- acceptor({
191
- node,
192
- keyword: "color",
193
- type: SemanticTokenTypes.keyword
194
- });
195
- acceptor({
196
- node,
197
- property: "name",
198
- type: SemanticTokenTypes.type,
199
- modifier: [
200
- SemanticTokenModifiers.declaration,
201
- SemanticTokenModifiers.readonly
202
- ]
203
- });
204
- return;
205
- }
206
- if (ast.isSpecificationTag(node) && isTruthy(node.color)) {
207
- acceptor({
208
- node,
209
- keyword: "color",
210
- type: SemanticTokenTypes.property
211
- });
212
- return;
213
- }
214
- if (ast.isSpecificationElementKind(node) || ast.isSpecificationRelationshipKind(node) || ast.isSpecificationDeploymentNodeKind(node)) {
215
- acceptor({
216
- node,
217
- property: "kind",
218
- type: SemanticTokenTypes.type,
219
- modifier: [
220
- SemanticTokenModifiers.declaration,
221
- SemanticTokenModifiers.readonly
222
- ]
223
- });
224
- }
225
- if (ast.isTagRef(node)) {
226
- acceptor({
227
- cst: node.$cstNode,
228
- type: SemanticTokenTypes.type
229
- });
230
- return "prune";
231
- }
232
- if (ast.isTag(node)) {
233
- return acceptor({
234
- node,
235
- property: "name",
236
- type: SemanticTokenTypes.type,
237
- modifier: [SemanticTokenModifiers.definition]
238
- });
239
- }
240
- if (ast.isRelationStyleProperty(node) || ast.isElementStyleProperty(node)) {
241
- acceptor({
242
- node,
243
- property: "key",
244
- type: SemanticTokenTypes.property
245
- });
246
- }
247
- if (ast.isOpacityProperty(node)) {
248
- acceptor({
249
- node,
250
- property: "key",
251
- type: SemanticTokenTypes.property
252
- });
253
- acceptor({
254
- node,
255
- property: "value",
256
- type: SemanticTokenTypes.number
257
- });
258
- return "prune";
259
- }
260
- if (ast.isLinkProperty(node) || ast.isIconProperty(node) || ast.isStringProperty(node)) {
261
- acceptor({
262
- node,
263
- property: "key",
264
- type: SemanticTokenTypes.property
265
- });
266
- if (ast.isIconProperty(node) && (node.libicon || node.value === "none")) {
267
- acceptor({
268
- node,
269
- property: node.libicon ? "libicon" : "value",
270
- type: SemanticTokenTypes.enum,
271
- modifier: [SemanticTokenModifiers.defaultLibrary]
272
- });
273
- return "prune";
184
+ try {
185
+ if (isAnyOf(ast.isElement, ast.isDeploymentNode, ast.isDeployedInstance)(node)) {
186
+ return this.highlightNameAndKind(node);
274
187
  }
275
- if ("value" in node && node.value && !ast.isStringProperty(node)) {
276
- acceptor({
277
- node,
278
- property: "value",
279
- type: SemanticTokenTypes.string
280
- });
188
+ if (ast.isLikeC4View(node)) {
189
+ return this.highlightView(node);
281
190
  }
282
- return "prune";
283
- }
284
- if (ast.isColorProperty(node) || ast.isShapeProperty(node) || ast.isArrowProperty(node) || ast.isLineProperty(node) || ast.isBorderProperty(node) || ast.isSizeProperty(node) || ast.isDynamicViewDisplayVariantProperty(node)) {
285
- acceptor({
286
- node,
287
- property: "key",
288
- type: SemanticTokenTypes.property
289
- });
290
- if ("value" in node) {
191
+ if (ast.isAnyProperty(node) && !isAnyOf(
192
+ ast.isMetadataProperty,
193
+ ast.isElementStyleProperty,
194
+ ast.isRelationStyleProperty
195
+ )(node)) {
196
+ invariant(node.key);
291
197
  acceptor({
292
198
  node,
293
- property: "value",
294
- type: SemanticTokenTypes.enum
199
+ property: "key",
200
+ type: SemanticTokenTypes.property
295
201
  });
296
202
  }
297
- return "prune";
203
+ for (const { predicate, highlightFn } of this.rules) {
204
+ if (predicate(node)) {
205
+ highlightFn(this.mark(node));
206
+ break;
207
+ }
208
+ }
209
+ } catch (error) {
210
+ if (error === PRUNE) {
211
+ return "prune";
212
+ }
213
+ logger.warn(`Error highlighting node of type ${node._type}`, { error });
298
214
  }
299
215
  }
300
- highlightNameAndKind(node, acceptor) {
301
- acceptor({
302
- node,
303
- property: "name",
304
- type: SemanticTokenTypes.variable,
305
- modifier: [
306
- SemanticTokenModifiers.declaration,
307
- SemanticTokenModifiers.readonly
308
- ]
309
- });
216
+ highlightNameAndKind(node) {
217
+ const mark = this.mark(node);
218
+ mark.property("name").declaration.readonly.variable();
310
219
  if (!ast.isDeployedInstance(node)) {
311
- acceptor({
312
- node,
313
- property: "kind",
314
- type: SemanticTokenTypes.keyword,
315
- modifier: []
316
- });
220
+ this.mark(node).property("kind").keyword();
317
221
  }
318
222
  if (ast.isElement(node)) {
319
223
  if (node.props.length > 0) {
320
- acceptor({
321
- node,
322
- property: "props",
323
- type: SemanticTokenTypes.string
324
- });
224
+ this.mark(node).property("props").string();
325
225
  }
326
226
  return;
327
227
  }
328
228
  if (node.title) {
329
- acceptor({
330
- node,
331
- property: "title",
332
- type: SemanticTokenTypes.string
333
- });
229
+ this.mark(node).property("title").string();
334
230
  }
335
231
  }
336
- highlightView(node, acceptor) {
232
+ highlightView(node) {
337
233
  if (node.name) {
338
- acceptor({
339
- node,
340
- property: "name",
341
- type: SemanticTokenTypes.interface,
342
- modifier: [
343
- SemanticTokenModifiers.declaration,
344
- SemanticTokenModifiers.readonly,
345
- "local"
346
- ]
347
- });
234
+ const mark = this.mark(node);
235
+ mark.property("name").modifier("local").declaration.readonly.interface();
348
236
  }
349
237
  }
238
+ mark(node) {
239
+ const cst = (cstNode) => {
240
+ return createSemanticTypeMethods(
241
+ (type, modifier) => this.highlightToken({
242
+ range: nonNullable(cstNode ?? node.$cstNode, "AST node has no CST node").range,
243
+ type,
244
+ modifier
245
+ })
246
+ );
247
+ };
248
+ const keyword = (keyword2) => {
249
+ return createSemanticTypeMethods(
250
+ (type, modifier) => this.highlightKeyword({
251
+ node,
252
+ keyword: keyword2,
253
+ type,
254
+ modifier
255
+ })
256
+ );
257
+ };
258
+ const property = (property2, index) => {
259
+ return createSemanticTypeMethods(
260
+ (type, modifier) => this.highlightProperty({
261
+ node,
262
+ property: property2,
263
+ type,
264
+ modifier,
265
+ ...index !== void 0 ? { index } : {}
266
+ })
267
+ );
268
+ };
269
+ return {
270
+ node,
271
+ cst,
272
+ keyword,
273
+ property
274
+ };
275
+ }
350
276
  }
@@ -87,7 +87,7 @@ Example response (deployed instance):
87
87
  technology: z.string().nullable(),
88
88
  tags: z.array(z.string()),
89
89
  project: z.string(),
90
- metadata: z.record(z.string()),
90
+ metadata: z.record(z.union([z.string(), z.array(z.string())])),
91
91
  shape: z.string(),
92
92
  color: z.string(),
93
93
  children: z.array(z.string()).describe("Children of this deployment node (Array of Deployment ids)"),
@@ -102,7 +102,7 @@ Example response:
102
102
  technology: z.string().nullable(),
103
103
  tags: z.array(z.string()),
104
104
  project: z.string(),
105
- metadata: z.record(z.string()),
105
+ metadata: z.record(z.union([z.string(), z.array(z.string())])),
106
106
  shape: z.string(),
107
107
  color: z.string(),
108
108
  children: z.array(z.string()).describe("Children of this element (Array of FQNs)"),
@@ -14,7 +14,7 @@ const searchResultSchema = z.array(
14
14
  technology: z.string().nullable(),
15
15
  shape: z.string(),
16
16
  includedInViews: includedInViewsSchema,
17
- metadata: z.record(z.string()),
17
+ metadata: z.record(z.union([z.string(), z.array(z.string())])),
18
18
  tags: z.array(z.string())
19
19
  }),
20
20
  z.object({
@@ -27,7 +27,7 @@ const searchResultSchema = z.array(
27
27
  technology: z.string().nullable(),
28
28
  shape: z.string(),
29
29
  includedInViews: includedInViewsSchema,
30
- metadata: z.record(z.string()),
30
+ metadata: z.record(z.union([z.string(), z.array(z.string())])),
31
31
  tags: z.array(z.string())
32
32
  })
33
33
  ])
@@ -2,11 +2,12 @@ import type * as c4 from '@likec4/core';
2
2
  import type { ParsedAstExtend } from '../../ast';
3
3
  export declare class MergedExtends {
4
4
  private mergedData;
5
+ private mergeMetadata;
5
6
  merge(parsedExtends: ParsedAstExtend[]): void;
6
7
  applyExtended<E extends {
7
8
  id: string;
8
9
  tags?: readonly string[] | null;
9
10
  links?: readonly c4.Link[] | null;
10
- metadata?: Record<string, string>;
11
+ metadata?: Record<string, string | string[]>;
11
12
  }>(el: E): E;
12
13
  }