@likec4/language-server 0.30.0 → 0.32.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/README.md +5 -0
- package/contrib/likec4.monarch.ts +1 -1
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/ast.d.ts +15 -4
- package/dist/ast.js +32 -11
- package/dist/generated/ast.d.ts +29 -31
- package/dist/generated/ast.js +14 -30
- package/dist/generated/grammar.js +342 -223
- package/dist/logger.d.ts +1 -0
- package/dist/lsp/DocumentSymbolProvider.js +3 -3
- package/dist/lsp/SemanticTokenProvider.js +42 -21
- package/dist/model/fqn-computation.js +3 -2
- package/dist/model/fqn-index.js +4 -4
- package/dist/model/model-builder.d.ts +2 -1
- package/dist/model/model-builder.js +33 -27
- package/package.json +7 -6
package/dist/logger.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export class LikeC4DocumentSymbolProvider {
|
|
|
80
80
|
return [];
|
|
81
81
|
return [
|
|
82
82
|
{
|
|
83
|
-
kind: SymbolKind.
|
|
83
|
+
kind: SymbolKind.Namespace,
|
|
84
84
|
name: astSpec.name,
|
|
85
85
|
range: cstModel.range,
|
|
86
86
|
selectionRange: specKeywordNode.range,
|
|
@@ -97,7 +97,7 @@ export class LikeC4DocumentSymbolProvider {
|
|
|
97
97
|
return [];
|
|
98
98
|
return [
|
|
99
99
|
{
|
|
100
|
-
kind: SymbolKind.
|
|
100
|
+
kind: SymbolKind.Namespace,
|
|
101
101
|
name: astModel.name,
|
|
102
102
|
range: cstModel.range,
|
|
103
103
|
selectionRange: nameNode.range,
|
|
@@ -166,7 +166,7 @@ export class LikeC4DocumentSymbolProvider {
|
|
|
166
166
|
return [];
|
|
167
167
|
return [
|
|
168
168
|
{
|
|
169
|
-
kind: SymbolKind.
|
|
169
|
+
kind: SymbolKind.Namespace,
|
|
170
170
|
name: astViews.name,
|
|
171
171
|
range: cst.range,
|
|
172
172
|
selectionRange: nameNode.range,
|
|
@@ -26,11 +26,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
26
26
|
});
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
if (
|
|
30
|
-
ast.isRelationExpression(node) ||
|
|
31
|
-
ast.isIncomingExpression(node) ||
|
|
32
|
-
ast.isInOutExpression(node) ||
|
|
33
|
-
ast.isOutgoingExpression(node)) {
|
|
29
|
+
if ('arr' in node) {
|
|
34
30
|
acceptor({
|
|
35
31
|
node,
|
|
36
32
|
property: 'arr',
|
|
@@ -38,6 +34,20 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
38
34
|
modifier: [SemanticTokenModifiers.defaultLibrary]
|
|
39
35
|
});
|
|
40
36
|
}
|
|
37
|
+
// if (
|
|
38
|
+
// ast.isRelation(node) ||
|
|
39
|
+
// ast.isRelationExpression(node) ||
|
|
40
|
+
// ast.isIncomingExpression(node) ||
|
|
41
|
+
// ast.isInOutExpression(node) ||
|
|
42
|
+
// ast.isOutgoingExpression(node)
|
|
43
|
+
// ) {
|
|
44
|
+
// acceptor({
|
|
45
|
+
// node,
|
|
46
|
+
// property: 'arr',
|
|
47
|
+
// type: SemanticTokenTypes.keyword,
|
|
48
|
+
// modifier: [SemanticTokenModifiers.defaultLibrary]
|
|
49
|
+
// })
|
|
50
|
+
// }
|
|
41
51
|
if (ast.isElementKindExpression(node) || ast.isElementTagExpression(node)) {
|
|
42
52
|
keyword('element');
|
|
43
53
|
if (ast.isElementKindExpression(node)) {
|
|
@@ -98,16 +108,11 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
98
108
|
});
|
|
99
109
|
return;
|
|
100
110
|
}
|
|
101
|
-
if (ast.
|
|
111
|
+
if (ast.isColorProperty(node) || ast.isShapeProperty(node)) {
|
|
102
112
|
acceptor({
|
|
103
113
|
node,
|
|
104
114
|
property: 'key',
|
|
105
115
|
type: SemanticTokenTypes.keyword
|
|
106
|
-
// type: SemanticTokenTypes.property,
|
|
107
|
-
// modifier: [
|
|
108
|
-
// SemanticTokenModifiers.readonly,
|
|
109
|
-
// SemanticTokenModifiers.declaration
|
|
110
|
-
// ]
|
|
111
116
|
});
|
|
112
117
|
acceptor({
|
|
113
118
|
node,
|
|
@@ -116,20 +121,11 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
116
121
|
});
|
|
117
122
|
return;
|
|
118
123
|
}
|
|
119
|
-
|
|
120
|
-
if (ast.isViewProperty(node) ||
|
|
121
|
-
ast.isElementStringProperty(node) ||
|
|
122
|
-
ast.isRelationStringProperty(node) ||
|
|
123
|
-
ast.isLinkProperty(node)) {
|
|
124
|
+
if (ast.isLinkProperty(node) || ast.isIconProperty(node)) {
|
|
124
125
|
acceptor({
|
|
125
126
|
node,
|
|
126
127
|
property: 'key',
|
|
127
128
|
type: SemanticTokenTypes.keyword
|
|
128
|
-
// type: SemanticTokenTypes.property,
|
|
129
|
-
// modifier: [
|
|
130
|
-
// SemanticTokenModifiers.readonly,
|
|
131
|
-
// SemanticTokenModifiers.declaration
|
|
132
|
-
// ]
|
|
133
129
|
});
|
|
134
130
|
acceptor({
|
|
135
131
|
node,
|
|
@@ -138,6 +134,31 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
138
134
|
});
|
|
139
135
|
return;
|
|
140
136
|
}
|
|
137
|
+
// ViewProperty | ElementStringProperty | RelationStringProperty | LinkProperty
|
|
138
|
+
// if (
|
|
139
|
+
// ast.isViewProperty(node) ||
|
|
140
|
+
// ast.isElementStringProperty(node) ||
|
|
141
|
+
// ast.isRelationStringProperty(node) ||
|
|
142
|
+
// ast.isLinkProperty(node) ||
|
|
143
|
+
// ast.isIconProperty(node)
|
|
144
|
+
// ) {
|
|
145
|
+
// acceptor({
|
|
146
|
+
// node,
|
|
147
|
+
// property: 'key',
|
|
148
|
+
// type: SemanticTokenTypes.keyword
|
|
149
|
+
// // type: SemanticTokenTypes.property,
|
|
150
|
+
// // modifier: [
|
|
151
|
+
// // SemanticTokenModifiers.readonly,
|
|
152
|
+
// // SemanticTokenModifiers.declaration
|
|
153
|
+
// // ]
|
|
154
|
+
// })
|
|
155
|
+
// acceptor({
|
|
156
|
+
// node,
|
|
157
|
+
// property: 'value',
|
|
158
|
+
// type: SemanticTokenTypes.string
|
|
159
|
+
// })
|
|
160
|
+
// return
|
|
161
|
+
// }
|
|
141
162
|
// if (ast.isModel(node)) {
|
|
142
163
|
// keyword('model')
|
|
143
164
|
// return
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { nonexhaustive } from '@likec4/core/errors';
|
|
2
|
+
import { AsFqn } from '@likec4/core/types';
|
|
2
3
|
import { MultiMap } from 'langium';
|
|
3
4
|
import { isEmpty, isNil } from 'remeda';
|
|
4
5
|
import { ElementOps, ast } from '../ast';
|
|
@@ -34,7 +35,7 @@ export function computeDocumentFqn(document, services) {
|
|
|
34
35
|
}
|
|
35
36
|
continue;
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
nonexhaustive(el);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
//# sourceMappingURL=fqn-computation.js.map
|
package/dist/model/fqn-index.js
CHANGED
|
@@ -51,18 +51,18 @@ export class FqnIndex {
|
|
|
51
51
|
}
|
|
52
52
|
directChildrenOf(parent) {
|
|
53
53
|
return new StreamImpl(() => {
|
|
54
|
-
const children =
|
|
54
|
+
const children = this.entries()
|
|
55
55
|
.filter(e => parentFqn(e.fqn) === parent)
|
|
56
56
|
.map((e) => {
|
|
57
57
|
const name = nameFromFqn(e.fqn);
|
|
58
58
|
const entry = { ...e, name };
|
|
59
59
|
return [name, entry];
|
|
60
60
|
})
|
|
61
|
-
.toArray()
|
|
62
|
-
if (children.
|
|
61
|
+
.toArray();
|
|
62
|
+
if (children.length === 0) {
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
|
-
return children
|
|
65
|
+
return new MultiMap(children)
|
|
66
66
|
.entriesGroupedByKey()
|
|
67
67
|
.flatMap(([_name, descrs]) => (descrs.length === 1 ? descrs : []))
|
|
68
68
|
.iterator();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type * as c4 from '@likec4/core/types';
|
|
2
|
-
import { ast
|
|
2
|
+
import { ast } from '../ast';
|
|
3
|
+
import type { LikeC4LangiumDocument } from '../ast';
|
|
3
4
|
import type { LikeC4Services } from '../module';
|
|
4
5
|
export declare class LikeC4ModelBuilder {
|
|
5
6
|
private services;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { ModelIndex, assignNavigateTo, computeView, invariant } from '@likec4/core';
|
|
2
|
-
import { DefaultElementShape, DefaultThemeColor } from '@likec4/core/types';
|
|
3
|
-
import { compareByFqnHierarchically, parentFqn } from '@likec4/core/utils';
|
|
1
|
+
import { ModelIndex, assignNavigateTo, compareByFqnHierarchically, computeView, invariant, isNonEmptyArray, nonexhaustive, parentFqn } from '@likec4/core';
|
|
4
2
|
import { DocumentState, getDocument } from 'langium';
|
|
5
3
|
import objectHash from 'object-hash';
|
|
6
4
|
import { clone } from 'rambdax';
|
|
7
5
|
import * as R from 'remeda';
|
|
8
6
|
import stripIndent from 'strip-indent';
|
|
9
|
-
import { ElementViewOps, ast, cleanParsedModel, isLikeC4LangiumDocument, isValidLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle } from '../ast';
|
|
7
|
+
import { ElementViewOps, ast, cleanParsedModel, isLikeC4LangiumDocument, isValidLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle, toElementStyleExcludeDefaults } from '../ast';
|
|
10
8
|
import { elementRef, strictElementRefFqn } from '../elementRef';
|
|
11
9
|
import { logger } from '../logger';
|
|
12
10
|
import { Rpc } from '../protocol';
|
|
@@ -67,15 +65,15 @@ export class LikeC4ModelBuilder {
|
|
|
67
65
|
kinds: {}
|
|
68
66
|
};
|
|
69
67
|
R.forEach(R.map(docs, R.prop('c4Specification')), spec => Object.assign(c4Specification.kinds, spec.kinds));
|
|
70
|
-
const toModelElement = ({ astPath, ...parsed }) => {
|
|
68
|
+
const toModelElement = ({ astPath, tags, links, ...parsed }) => {
|
|
71
69
|
const kind = c4Specification.kinds[parsed.kind];
|
|
72
70
|
if (kind) {
|
|
73
71
|
return {
|
|
74
|
-
|
|
75
|
-
color: kind.color,
|
|
72
|
+
...kind,
|
|
76
73
|
description: null,
|
|
77
74
|
technology: null,
|
|
78
|
-
tags:
|
|
75
|
+
tags: tags ?? null,
|
|
76
|
+
links: links ?? null,
|
|
79
77
|
...parsed
|
|
80
78
|
};
|
|
81
79
|
}
|
|
@@ -109,7 +107,7 @@ export class LikeC4ModelBuilder {
|
|
|
109
107
|
const modelIndex = ModelIndex.from({ elements, relations });
|
|
110
108
|
const toModelView = (view) => {
|
|
111
109
|
// eslint-disable-next-line prefer-const
|
|
112
|
-
let { astPath, rules, title, ...model } = view;
|
|
110
|
+
let { astPath, rules, title, description, tags, links, ...model } = view;
|
|
113
111
|
if (!title && view.viewOf) {
|
|
114
112
|
title = elements[view.viewOf]?.title;
|
|
115
113
|
}
|
|
@@ -118,7 +116,10 @@ export class LikeC4ModelBuilder {
|
|
|
118
116
|
}
|
|
119
117
|
return computeView({
|
|
120
118
|
...model,
|
|
121
|
-
|
|
119
|
+
title: title ?? null,
|
|
120
|
+
description: description ?? null,
|
|
121
|
+
tags: tags ?? null,
|
|
122
|
+
links: links ?? null,
|
|
122
123
|
rules: clone(rules)
|
|
123
124
|
}, modelIndex);
|
|
124
125
|
};
|
|
@@ -144,11 +145,7 @@ export class LikeC4ModelBuilder {
|
|
|
144
145
|
if (specs) {
|
|
145
146
|
for (const { kind, style } of specs) {
|
|
146
147
|
try {
|
|
147
|
-
|
|
148
|
-
specification.kinds[kind.name] = {
|
|
149
|
-
color: styleProps.color ?? DefaultThemeColor,
|
|
150
|
-
shape: styleProps.shape ?? DefaultElementShape
|
|
151
|
-
};
|
|
148
|
+
specification.kinds[kind.name] = toElementStyleExcludeDefaults(style?.props);
|
|
152
149
|
}
|
|
153
150
|
catch (e) {
|
|
154
151
|
logger.warn(e);
|
|
@@ -174,7 +171,7 @@ export class LikeC4ModelBuilder {
|
|
|
174
171
|
}
|
|
175
172
|
continue;
|
|
176
173
|
}
|
|
177
|
-
|
|
174
|
+
nonexhaustive(el);
|
|
178
175
|
}
|
|
179
176
|
const docviews = doc.parseResult.value.views?.views;
|
|
180
177
|
if (docviews) {
|
|
@@ -197,25 +194,26 @@ export class LikeC4ModelBuilder {
|
|
|
197
194
|
const id = this.resolveFqn(astNode);
|
|
198
195
|
invariant(astNode.kind.ref, 'Element kind is not resolved: ' + astNode.name);
|
|
199
196
|
const kind = astNode.kind.ref.name;
|
|
200
|
-
const tags =
|
|
201
|
-
const
|
|
202
|
-
const
|
|
197
|
+
const tags = this.convertTags(astNode.body);
|
|
198
|
+
const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
|
|
199
|
+
const styleProps = toElementStyleExcludeDefaults(stylePropsAst);
|
|
203
200
|
const astPath = this.getAstNodePath(astNode);
|
|
204
201
|
let [title, description, technology] = astNode.props;
|
|
205
202
|
const bodyProps = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
|
|
206
203
|
title = title ?? bodyProps.find(p => p.key === 'title')?.value;
|
|
207
204
|
description = description ?? bodyProps.find(p => p.key === 'description')?.value;
|
|
208
205
|
technology = technology ?? bodyProps.find(p => p.key === 'technology')?.value;
|
|
206
|
+
const links = astNode.body?.props.filter(ast.isLinkProperty).map(p => p.value);
|
|
209
207
|
return {
|
|
210
208
|
id,
|
|
211
209
|
kind,
|
|
212
210
|
astPath,
|
|
213
|
-
title: title
|
|
211
|
+
title: title ? stripIndent(title).trim() : astNode.name,
|
|
212
|
+
...(tags && { tags }),
|
|
213
|
+
...(links && isNonEmptyArray(links) && { links }),
|
|
214
214
|
...(technology && { technology }),
|
|
215
215
|
...(description && { description: stripIndent(description).trim() }),
|
|
216
|
-
...
|
|
217
|
-
...(shape && shape !== DefaultElementShape ? { shape } : {}),
|
|
218
|
-
...(color && color !== DefaultThemeColor ? { color } : {})
|
|
216
|
+
...styleProps
|
|
219
217
|
};
|
|
220
218
|
}
|
|
221
219
|
parseRelation(astNode) {
|
|
@@ -329,14 +327,18 @@ export class LikeC4ModelBuilder {
|
|
|
329
327
|
viewOf: viewOf ?? null
|
|
330
328
|
});
|
|
331
329
|
}
|
|
332
|
-
const title = astNode.
|
|
333
|
-
const description = astNode.
|
|
330
|
+
const title = astNode.props.find(p => p.key === 'title')?.value;
|
|
331
|
+
const description = astNode.props.find(p => p.key === 'description')?.value;
|
|
332
|
+
const tags = this.convertTags(astNode);
|
|
333
|
+
const links = astNode.props.filter(ast.isLinkProperty).map(p => p.value);
|
|
334
334
|
return {
|
|
335
335
|
id,
|
|
336
336
|
astPath,
|
|
337
337
|
...(viewOf && { viewOf }),
|
|
338
338
|
...(title && { title }),
|
|
339
339
|
...(description && { description }),
|
|
340
|
+
...(tags && { tags }),
|
|
341
|
+
...(links && isNonEmptyArray(links) && { links }),
|
|
340
342
|
rules: astNode.rules.map(n => this.parseViewRule(n))
|
|
341
343
|
};
|
|
342
344
|
}
|
|
@@ -351,8 +353,12 @@ export class LikeC4ModelBuilder {
|
|
|
351
353
|
getAstNodePath(node) {
|
|
352
354
|
return this.services.workspace.AstNodeLocator.getAstNodePath(node);
|
|
353
355
|
}
|
|
354
|
-
convertTags(
|
|
355
|
-
|
|
356
|
+
convertTags(withTags) {
|
|
357
|
+
if (!withTags) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const tags = withTags.tags?.value.flatMap(({ ref }) => (ref ? ref.name : []));
|
|
361
|
+
return tags && isNonEmptyArray(tags) ? tags : null;
|
|
356
362
|
}
|
|
357
363
|
scheduledCb = null;
|
|
358
364
|
notifyClient(cancelToken) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/language-server",
|
|
3
3
|
"description": "LikeC4 Language Server",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.32.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -55,16 +55,16 @@
|
|
|
55
55
|
"dev": "run-p 'watch:*'",
|
|
56
56
|
"lint": "run -T eslint src/ --fix",
|
|
57
57
|
"clean": "run -T rimraf dist contrib",
|
|
58
|
-
"test": "
|
|
59
|
-
"test:watch": "
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:watch": "vitest"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@likec4/core": "0.
|
|
62
|
+
"@likec4/core": "0.32.0",
|
|
63
63
|
"langium": "^1.2.1",
|
|
64
64
|
"nanoid": "^4.0.2",
|
|
65
65
|
"object-hash": "^3.0.0",
|
|
66
66
|
"rambdax": "^9.1.1",
|
|
67
|
-
"remeda": "^1.
|
|
67
|
+
"remeda": "^1.24.0",
|
|
68
68
|
"strip-indent": "^4.0.0",
|
|
69
69
|
"vscode-languageserver": "~8.1.0",
|
|
70
70
|
"vscode-languageserver-protocol": "~3.17.3"
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"@types/object-hash": "^3.0.2",
|
|
75
75
|
"langium-cli": "^1.2.1",
|
|
76
76
|
"npm-run-all": "^4.1.5",
|
|
77
|
-
"typescript": "^5.1.6"
|
|
77
|
+
"typescript": "^5.1.6",
|
|
78
|
+
"vitest": "^0.34.1"
|
|
78
79
|
}
|
|
79
80
|
}
|