@likec4/language-server 0.4.0 → 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.d.ts +1 -0
- package/dist/ast.js +10 -9
- package/dist/builtin.d.ts +4 -0
- package/dist/builtin.js +8 -0
- package/dist/generated/ast.d.ts +1 -1
- package/dist/generated/grammar.js +4 -0
- package/dist/lsp/DocumentSymbolProvider.d.ts +1 -0
- package/dist/lsp/DocumentSymbolProvider.js +20 -10
- package/dist/lsp/HoverProvider.js +1 -4
- package/dist/lsp/SemanticTokenProvider.js +10 -12
- package/dist/lsp/index.d.ts +0 -1
- package/dist/lsp/index.js +0 -1
- package/dist/model/fqn-index.js +7 -5
- package/dist/model/model-builder.js +7 -7
- package/dist/module.js +11 -7
- package/dist/references/scope-provider.d.ts +2 -2
- package/dist/references/scope-provider.js +4 -4
- package/dist/{lsp → shared}/CodeLensProvider.js +2 -2
- package/dist/shared/WorkspaceManager.d.ts +13 -0
- package/dist/shared/WorkspaceManager.js +19 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/test/testServices.js +4 -0
- package/dist/validation/element.d.ts +1 -0
- package/dist/validation/element.js +2 -2
- package/dist/validation/index.js +3 -1
- package/dist/validation/relation.d.ts +4 -0
- package/dist/validation/relation.js +53 -0
- package/dist/validation/specification.js +6 -4
- package/dist/validation/view.js +2 -1
- package/package.json +16 -6
- /package/dist/{lsp → shared}/CodeLensProvider.d.ts +0 -0
package/dist/ast.d.ts
CHANGED
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) &&
|
|
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
|
-
|
|
64
|
-
|
|
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;
|
package/dist/builtin.js
ADDED
package/dist/generated/ast.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ 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";
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* This program and the accompanying materials are made available under the
|
|
4
4
|
* terms of the MIT License, which is available in the project root.
|
|
5
5
|
******************************************************************************/
|
|
6
|
+
/// <reference types="react" />
|
|
6
7
|
import { type DocumentSymbolProvider, type MaybePromise } from 'langium';
|
|
7
8
|
import { type DocumentSymbol } from 'vscode-languageserver-protocol';
|
|
8
9
|
import { type LikeC4LangiumDocument, ast } from '../ast';
|
|
@@ -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;
|
|
@@ -112,14 +118,16 @@ export class LikeC4DocumentSymbolProvider {
|
|
|
112
118
|
const kind = astElement.kind.$refText;
|
|
113
119
|
// TODO: return the title as well
|
|
114
120
|
const detail = kind; // + (astElement.title ? ': ' + astElement.title : '').replaceAll('\n', ' ').trim()
|
|
115
|
-
return [
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
116
123
|
kind: SymbolKind.Constructor,
|
|
117
124
|
name: name,
|
|
118
125
|
range: cst.range,
|
|
119
126
|
selectionRange: nameNode.range,
|
|
120
127
|
detail,
|
|
121
128
|
children: astElement.body?.elements.flatMap(this.getElementsSymbol) ?? []
|
|
122
|
-
}
|
|
129
|
+
}
|
|
130
|
+
];
|
|
123
131
|
};
|
|
124
132
|
getModelViewsSymbols = (astViews) => {
|
|
125
133
|
const cst = astViews?.$cstNode;
|
|
@@ -128,12 +136,14 @@ export class LikeC4DocumentSymbolProvider {
|
|
|
128
136
|
const nameNode = findNodeForProperty(cst, 'name');
|
|
129
137
|
if (!nameNode)
|
|
130
138
|
return [];
|
|
131
|
-
return [
|
|
139
|
+
return [
|
|
140
|
+
{
|
|
132
141
|
kind: SymbolKind.Class,
|
|
133
142
|
name: astViews.name,
|
|
134
143
|
range: cst.range,
|
|
135
144
|
selectionRange: nameNode.range,
|
|
136
145
|
children: []
|
|
137
|
-
}
|
|
146
|
+
}
|
|
147
|
+
];
|
|
138
148
|
};
|
|
139
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) ||
|
|
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({
|
|
@@ -207,9 +207,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
207
207
|
node,
|
|
208
208
|
property: 'name',
|
|
209
209
|
type: SemanticTokenTypes.variable,
|
|
210
|
-
modifier: [
|
|
211
|
-
SemanticTokenModifiers.declaration,
|
|
212
|
-
]
|
|
210
|
+
modifier: [SemanticTokenModifiers.declaration]
|
|
213
211
|
});
|
|
214
212
|
acceptor({
|
|
215
213
|
node,
|
|
@@ -230,7 +228,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
230
228
|
acceptor({
|
|
231
229
|
node,
|
|
232
230
|
property: 'name',
|
|
233
|
-
type: SemanticTokenTypes.variable
|
|
231
|
+
type: SemanticTokenTypes.variable
|
|
234
232
|
});
|
|
235
233
|
}
|
|
236
234
|
if (node.viewOf) {
|
package/dist/lsp/index.d.ts
CHANGED
package/dist/lsp/index.js
CHANGED
package/dist/model/fqn-index.js
CHANGED
|
@@ -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
|
|
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 (
|
|
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
|
-
},
|
|
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
|
|
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
|
-
...
|
|
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,7 +181,7 @@ 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
|
-
let [title, description, technology
|
|
184
|
+
let [title, description, technology] = astNode.props;
|
|
185
185
|
const bodyProps = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
|
|
186
186
|
title = title ?? bodyProps.find(p => p.key === 'title')?.value;
|
|
187
187
|
description = description ?? bodyProps.find(p => p.key === 'description')?.value;
|
|
@@ -195,7 +195,7 @@ export class LikeC4ModelBuilder {
|
|
|
195
195
|
...(description && { description }),
|
|
196
196
|
...(tags.length > 0 ? { tags } : {}),
|
|
197
197
|
...(shape && shape !== DefaultElementShape ? { shape } : {}),
|
|
198
|
-
...(color && color !== DefaultThemeColor ? { color } : {})
|
|
198
|
+
...(color && color !== DefaultThemeColor ? { color } : {})
|
|
199
199
|
};
|
|
200
200
|
}
|
|
201
201
|
parseRelation(astNode) {
|
|
@@ -208,11 +208,11 @@ export class LikeC4ModelBuilder {
|
|
|
208
208
|
target
|
|
209
209
|
};
|
|
210
210
|
const id = objectHash(hashdata);
|
|
211
|
-
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 ?? '';
|
|
212
212
|
return {
|
|
213
213
|
id,
|
|
214
214
|
...hashdata,
|
|
215
|
-
title
|
|
215
|
+
title
|
|
216
216
|
};
|
|
217
217
|
}
|
|
218
218
|
parseElementExpression(astNode) {
|
|
@@ -253,7 +253,7 @@ export class LikeC4ModelBuilder {
|
|
|
253
253
|
if (ast.isRelationExpression(astNode)) {
|
|
254
254
|
return {
|
|
255
255
|
source: this.parseElementExpression(astNode.source),
|
|
256
|
-
target: this.parseElementExpression(astNode.target)
|
|
256
|
+
target: this.parseElementExpression(astNode.target)
|
|
257
257
|
};
|
|
258
258
|
}
|
|
259
259
|
failExpectedNever(astNode);
|
package/dist/module.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { createDefaultModule,
|
|
1
|
+
import { createDefaultModule, createDefaultSharedModule, inject } from 'langium';
|
|
2
2
|
import { LikeC4GeneratedModule, LikeC4GeneratedSharedModule } from './generated/module';
|
|
3
|
-
import {
|
|
3
|
+
import { LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
|
|
4
4
|
import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator } from './model';
|
|
5
|
-
import {
|
|
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:
|
|
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
|
-
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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
|
-
|
|
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(
|
|
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
|
+
}
|
|
@@ -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
|
};
|
package/dist/validation/index.js
CHANGED
|
@@ -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
|
-
|
|
19
|
+
Relation: relationChecks(services),
|
|
20
|
+
Tag: tagChecks(services)
|
|
19
21
|
});
|
|
20
22
|
}
|
|
@@ -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
|
|
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
|
|
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
|
};
|
package/dist/validation/view.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/language-server",
|
|
3
|
-
"version": "0.
|
|
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.
|
|
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,7 +78,7 @@
|
|
|
68
78
|
"rambdax": "^9.1.0",
|
|
69
79
|
"strip-indent": "^4.0.0",
|
|
70
80
|
"tiny-invariant": "^1.3.1",
|
|
71
|
-
"type-fest": "^3.
|
|
81
|
+
"type-fest": "^3.8.0",
|
|
72
82
|
"vscode-languageserver-protocol": "3.17.2",
|
|
73
83
|
"vscode-uri": "3.0.7"
|
|
74
84
|
},
|
|
@@ -77,7 +87,7 @@
|
|
|
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.
|
|
90
|
+
"typescript": "^5.0.4",
|
|
81
91
|
"vite": "^4.2.1",
|
|
82
92
|
"vitest": "^0.30.1"
|
|
83
93
|
}
|
|
File without changes
|