@likec4/language-server 0.34.0 → 0.35.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 -1
- package/dist/ast.js +1 -1
- package/dist/generated/ast.d.ts +1 -1
- package/dist/generated/ast.js +1 -1
- package/dist/generated/grammar.d.ts +1 -1
- package/dist/generated/grammar.js +1 -1
- package/dist/generated/module.d.ts +1 -1
- package/dist/generated/module.js +1 -1
- package/dist/model/fqn-computation.js +1 -1
- package/dist/model/fqn-index.d.ts +1 -1
- package/dist/model/fqn-index.js +17 -2
- package/dist/model/model-locator.js +19 -20
- package/dist/model/model-parser.js +13 -1
- package/dist/references/scope-computation.js +11 -5
- package/dist/references/scope-provider.js +8 -1
- package/dist/registerProtocolHandlers.js +14 -1
- package/dist/validation/element.js +7 -0
- package/dist/validation/relation.d.ts +1 -1
- package/dist/validation/relation.js +29 -16
- package/package.json +8 -7
package/dist/ast.d.ts
CHANGED
package/dist/ast.js
CHANGED
|
@@ -119,7 +119,7 @@ export function resolveRelationPoints(node) {
|
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
if (!ast.isElementBody(node.$container)) {
|
|
122
|
-
throw new RelationRefError('Invalid relation parent');
|
|
122
|
+
throw new RelationRefError('Invalid relation parent, expected Element');
|
|
123
123
|
}
|
|
124
124
|
return {
|
|
125
125
|
source: node.$container.$container,
|
package/dist/generated/ast.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/******************************************************************************
|
|
2
|
-
* This file was generated by langium-cli 2.0.
|
|
2
|
+
* This file was generated by langium-cli 2.0.1.
|
|
3
3
|
* DO NOT EDIT MANUALLY!
|
|
4
4
|
******************************************************************************/
|
|
5
5
|
import type { AstNode, Reference, ReferenceInfo, TypeMetaData } from 'langium';
|
package/dist/generated/ast.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/******************************************************************************
|
|
2
|
-
* This file was generated by langium-cli 2.0.
|
|
2
|
+
* This file was generated by langium-cli 2.0.1.
|
|
3
3
|
* DO NOT EDIT MANUALLY!
|
|
4
4
|
******************************************************************************/
|
|
5
5
|
import { AbstractAstReflection } from 'langium';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/******************************************************************************
|
|
2
|
-
* This file was generated by langium-cli 2.0.
|
|
2
|
+
* This file was generated by langium-cli 2.0.1.
|
|
3
3
|
* DO NOT EDIT MANUALLY!
|
|
4
4
|
******************************************************************************/
|
|
5
5
|
import type { Grammar } from 'langium';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/******************************************************************************
|
|
2
|
-
* This file was generated by langium-cli 2.0.
|
|
2
|
+
* This file was generated by langium-cli 2.0.1.
|
|
3
3
|
* DO NOT EDIT MANUALLY!
|
|
4
4
|
******************************************************************************/
|
|
5
5
|
import { loadGrammarFromJson } from 'langium';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/******************************************************************************
|
|
2
|
-
* This file was generated by langium-cli 2.0.
|
|
2
|
+
* This file was generated by langium-cli 2.0.1.
|
|
3
3
|
* DO NOT EDIT MANUALLY!
|
|
4
4
|
******************************************************************************/
|
|
5
5
|
import type { LangiumGeneratedServices, LangiumGeneratedSharedServices, LangiumSharedServices, LangiumServices, Module, IParserConfig } from 'langium';
|
package/dist/generated/module.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/******************************************************************************
|
|
2
|
-
* This file was generated by langium-cli 2.0.
|
|
2
|
+
* This file was generated by langium-cli 2.0.1.
|
|
3
3
|
* DO NOT EDIT MANUALLY!
|
|
4
4
|
******************************************************************************/
|
|
5
5
|
import { LikeC4AstReflection } from './ast.js';
|
|
@@ -20,7 +20,7 @@ export declare class FqnIndex {
|
|
|
20
20
|
private documents;
|
|
21
21
|
private entries;
|
|
22
22
|
getFqn(el: ast.Element): Fqn | null;
|
|
23
|
-
byFqn(fqn: Fqn): Stream<
|
|
23
|
+
byFqn(fqn: Fqn): Stream<FqnIndexEntry>;
|
|
24
24
|
directChildrenOf(parent: Fqn): Stream<FqnIndexEntry>;
|
|
25
25
|
/**
|
|
26
26
|
* Returns descedant elements with unique names in the scope
|
package/dist/model/fqn-index.js
CHANGED
|
@@ -52,7 +52,16 @@ export class FqnIndex {
|
|
|
52
52
|
return this.langiumDocuments.all.filter(isFqnIndexedDocument);
|
|
53
53
|
}
|
|
54
54
|
entries() {
|
|
55
|
-
return this.documents().flatMap(doc => doc.c4fqns
|
|
55
|
+
return this.documents().flatMap(doc => doc.c4fqns
|
|
56
|
+
.entries()
|
|
57
|
+
.map(([fqn, entry]) => {
|
|
58
|
+
const el = entry.el.deref();
|
|
59
|
+
if (el) {
|
|
60
|
+
return { ...entry, fqn, el, doc };
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
})
|
|
64
|
+
.nonNullable());
|
|
56
65
|
}
|
|
57
66
|
getFqn(el) {
|
|
58
67
|
return el.fqn ?? ElementOps.readId(el) ?? null;
|
|
@@ -70,7 +79,13 @@ export class FqnIndex {
|
|
|
70
79
|
}
|
|
71
80
|
byFqn(fqn) {
|
|
72
81
|
return this.documents().flatMap(doc => {
|
|
73
|
-
return doc.c4fqns.get(fqn)
|
|
82
|
+
return doc.c4fqns.get(fqn).flatMap(entry => {
|
|
83
|
+
const el = entry.el.deref();
|
|
84
|
+
if (el) {
|
|
85
|
+
return [{ fqn, el, doc, path: entry.path, name: entry.name }];
|
|
86
|
+
}
|
|
87
|
+
return [];
|
|
88
|
+
});
|
|
74
89
|
});
|
|
75
90
|
}
|
|
76
91
|
directChildrenOf(parent) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { InvalidModelError } from '@likec4/core';
|
|
1
2
|
import { findNodeForProperty, getDocument } from 'langium';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { nonNullable } from '@likec4/core';
|
|
3
|
+
import { ast, isParsedLikeC4LangiumDocument } from '../ast';
|
|
4
|
+
import {} from './fqn-index';
|
|
5
5
|
export class LikeC4ModelLocator {
|
|
6
6
|
services;
|
|
7
7
|
fqnIndex;
|
|
@@ -15,7 +15,7 @@ export class LikeC4ModelLocator {
|
|
|
15
15
|
return this.langiumDocuments.all.filter(isParsedLikeC4LangiumDocument);
|
|
16
16
|
}
|
|
17
17
|
getParsedElement(astNode) {
|
|
18
|
-
const fqn =
|
|
18
|
+
const fqn = this.fqnIndex.getFqn(astNode);
|
|
19
19
|
if (!fqn)
|
|
20
20
|
return null;
|
|
21
21
|
const doc = getDocument(astNode);
|
|
@@ -25,22 +25,18 @@ export class LikeC4ModelLocator {
|
|
|
25
25
|
return doc.c4Elements.find(e => e.id === fqn) ?? null;
|
|
26
26
|
}
|
|
27
27
|
locateElement(fqn, property = 'name') {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
const { el: node } = nonNullable(entries[0]);
|
|
34
|
-
const propertyNode = findNodeForProperty(node.$cstNode, property) ?? node.$cstNode;
|
|
35
|
-
if (!propertyNode) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
return {
|
|
39
|
-
uri: doc.uri.toString(),
|
|
40
|
-
range: propertyNode.range
|
|
41
|
-
};
|
|
28
|
+
const entry = this.fqnIndex.byFqn(fqn).head();
|
|
29
|
+
if (!entry) {
|
|
30
|
+
return null;
|
|
42
31
|
}
|
|
43
|
-
|
|
32
|
+
const propertyNode = findNodeForProperty(entry.el.$cstNode, property) ?? entry.el.$cstNode;
|
|
33
|
+
if (!propertyNode) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
uri: entry.doc.uri.toString(),
|
|
38
|
+
range: propertyNode.range
|
|
39
|
+
};
|
|
44
40
|
}
|
|
45
41
|
locateRelation(relationId) {
|
|
46
42
|
for (const doc of this.documents()) {
|
|
@@ -64,6 +60,9 @@ export class LikeC4ModelLocator {
|
|
|
64
60
|
};
|
|
65
61
|
}
|
|
66
62
|
}
|
|
63
|
+
if (node.arr == null) {
|
|
64
|
+
throw new InvalidModelError('Relation.arr is not defined, but should be');
|
|
65
|
+
}
|
|
67
66
|
const targetNode = findNodeForProperty(node.$cstNode, 'arr');
|
|
68
67
|
if (!targetNode) {
|
|
69
68
|
return null;
|
|
@@ -71,7 +70,7 @@ export class LikeC4ModelLocator {
|
|
|
71
70
|
return {
|
|
72
71
|
uri: doc.uri.toString(),
|
|
73
72
|
range: {
|
|
74
|
-
start: targetNode.range.
|
|
73
|
+
start: targetNode.range.start,
|
|
75
74
|
end: targetNode.range.end
|
|
76
75
|
}
|
|
77
76
|
};
|
|
@@ -49,6 +49,10 @@ export class LikeC4ModelParser {
|
|
|
49
49
|
const specs = doc.parseResult.value.specification?.specs.filter(ast.isSpecificationElementKind);
|
|
50
50
|
if (specs) {
|
|
51
51
|
for (const { kind, style } of specs) {
|
|
52
|
+
if (kind.name in specification.kinds) {
|
|
53
|
+
logger.warn(`Duplicate specification for kind ${kind.name}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
52
56
|
try {
|
|
53
57
|
specification.kinds[kind.name] = toElementStyleExcludeDefaults(style?.props);
|
|
54
58
|
}
|
|
@@ -246,7 +250,15 @@ export class LikeC4ModelParser {
|
|
|
246
250
|
...(description && { description }),
|
|
247
251
|
...(tags && { tags }),
|
|
248
252
|
...(links && isNonEmptyArray(links) && { links }),
|
|
249
|
-
rules: astNode.rules.
|
|
253
|
+
rules: astNode.rules.flatMap(n => {
|
|
254
|
+
try {
|
|
255
|
+
return this.parseViewRule(n);
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
logWarnError(e);
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
})
|
|
250
262
|
};
|
|
251
263
|
}
|
|
252
264
|
resolveFqn(node) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DefaultScopeComputation, MultiMap } from 'langium';
|
|
2
2
|
import { ast } from '../ast';
|
|
3
3
|
import { isEmpty } from 'remeda';
|
|
4
|
+
import { nonexhaustive } from '@likec4/core';
|
|
4
5
|
export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
5
6
|
services;
|
|
6
7
|
constructor(services) {
|
|
@@ -12,15 +13,20 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
12
13
|
const docExports = [];
|
|
13
14
|
if (specification && specification.specs.length > 0) {
|
|
14
15
|
for (const spec of specification.specs) {
|
|
15
|
-
if (ast.isSpecificationElementKind(spec)
|
|
16
|
-
|
|
16
|
+
if (ast.isSpecificationElementKind(spec)) {
|
|
17
|
+
if (spec.kind && !isEmpty(spec.kind.name)) {
|
|
18
|
+
docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
|
|
19
|
+
}
|
|
17
20
|
continue;
|
|
18
21
|
}
|
|
19
|
-
if (ast.isSpecificationTag(spec)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
if (ast.isSpecificationTag(spec)) {
|
|
23
|
+
if (spec.tag && !isEmpty(spec.tag.name)) {
|
|
24
|
+
docExports.push(this.descriptions.createDescription(spec.tag, spec.tag.name, document));
|
|
25
|
+
docExports.push(this.descriptions.createDescription(spec.tag, '#' + spec.tag.name, document));
|
|
26
|
+
}
|
|
22
27
|
continue;
|
|
23
28
|
}
|
|
29
|
+
nonexhaustive(spec);
|
|
24
30
|
}
|
|
25
31
|
}
|
|
26
32
|
// Only root model elements are exported
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { DONE_RESULT, DefaultScopeProvider, EMPTY_STREAM, StreamImpl, StreamScope, getDocument, stream } from 'langium';
|
|
1
|
+
import { DONE_RESULT, DefaultScopeProvider, EMPTY_STREAM, StreamImpl, StreamScope, getDocument, stream, findNodeForProperty, toDocumentSegment } from 'langium';
|
|
2
2
|
import { ast } from '../ast';
|
|
3
3
|
import { elementRef, isElementRefHead, parentStrictElementRef } from '../elementRef';
|
|
4
4
|
import { logError } from '../logger';
|
|
5
5
|
function toAstNodeDescription(entry) {
|
|
6
|
+
const $cstNode = findNodeForProperty(entry.el.$cstNode, 'name');
|
|
6
7
|
return {
|
|
7
8
|
documentUri: entry.doc.uri,
|
|
8
9
|
name: entry.name,
|
|
10
|
+
...(entry.el.$cstNode && {
|
|
11
|
+
selectionSegment: toDocumentSegment(entry.el.$cstNode)
|
|
12
|
+
}),
|
|
13
|
+
...($cstNode && {
|
|
14
|
+
nameSegment: toDocumentSegment($cstNode)
|
|
15
|
+
}),
|
|
9
16
|
path: entry.path,
|
|
10
17
|
type: ast.Element
|
|
11
18
|
};
|
|
@@ -2,6 +2,7 @@ import { URI } from 'vscode-uri';
|
|
|
2
2
|
import { logger, logError } from './logger';
|
|
3
3
|
import { Rpc } from './protocol';
|
|
4
4
|
import { nonexhaustive } from '@likec4/core';
|
|
5
|
+
import { isLikeC4LangiumDocument } from './ast';
|
|
5
6
|
export function registerProtocolHandlers(services) {
|
|
6
7
|
const connection = services.shared.lsp.Connection;
|
|
7
8
|
if (!connection) {
|
|
@@ -22,7 +23,19 @@ export function registerProtocolHandlers(services) {
|
|
|
22
23
|
return Promise.resolve({ model });
|
|
23
24
|
});
|
|
24
25
|
connection.onRequest(Rpc.rebuild, async (cancelToken) => {
|
|
25
|
-
const changed = LangiumDocuments.all
|
|
26
|
+
const changed = LangiumDocuments.all
|
|
27
|
+
.map(d => {
|
|
28
|
+
// clean up any computed properties
|
|
29
|
+
if (isLikeC4LangiumDocument(d)) {
|
|
30
|
+
delete d.c4Specification;
|
|
31
|
+
delete d.c4Elements;
|
|
32
|
+
delete d.c4Relations;
|
|
33
|
+
delete d.c4Views;
|
|
34
|
+
delete d.c4fqns;
|
|
35
|
+
}
|
|
36
|
+
return d.uri;
|
|
37
|
+
})
|
|
38
|
+
.toArray();
|
|
26
39
|
logger.debug(`[ProtocolHandlers] rebuild all documents: [
|
|
27
40
|
${changed.map(d => d.toString()).join('\n ')}
|
|
28
41
|
]`);
|
|
@@ -29,6 +29,13 @@ export const elementChecks = (services) => {
|
|
|
29
29
|
]
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
+
// for (let i = 3; i < el.props.length; i++) {
|
|
33
|
+
// accept('error', `Too many properties, max 3 allowed`, {
|
|
34
|
+
// node: el,
|
|
35
|
+
// property: 'props',
|
|
36
|
+
// index: i
|
|
37
|
+
// })
|
|
38
|
+
// }
|
|
32
39
|
};
|
|
33
40
|
};
|
|
34
41
|
//# sourceMappingURL=element.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ValidationCheck } from 'langium';
|
|
2
|
-
import
|
|
2
|
+
import { ast } from '../ast';
|
|
3
3
|
import type { LikeC4Services } from '../module';
|
|
4
4
|
export declare const relationChecks: (services: LikeC4Services) => ValidationCheck<ast.Relation>;
|
|
5
5
|
//# sourceMappingURL=relation.d.ts.map
|
|
@@ -1,30 +1,43 @@
|
|
|
1
1
|
import { isSameHierarchy } from '@likec4/core';
|
|
2
|
-
import {
|
|
2
|
+
import { ast } from '../ast';
|
|
3
|
+
import { elementRef } from '../elementRef';
|
|
3
4
|
import { logError } from '../logger';
|
|
4
5
|
export const relationChecks = (services) => {
|
|
5
6
|
const fqnIndex = services.likec4.FqnIndex;
|
|
6
7
|
return (el, accept) => {
|
|
7
8
|
try {
|
|
8
|
-
const
|
|
9
|
-
const target = fqnIndex.getFqn(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const targetEl = elementRef(el.target);
|
|
10
|
+
const target = targetEl && fqnIndex.getFqn(targetEl);
|
|
11
|
+
if (!target) {
|
|
12
|
+
accept('error', 'Target not found (not parsed/indexed yet)', {
|
|
13
|
+
node: el,
|
|
14
|
+
property: 'target'
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
let sourceEl;
|
|
18
|
+
if ('source' in el) {
|
|
19
|
+
sourceEl = elementRef(el.source);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
if (!ast.isElementBody(el.$container)) {
|
|
23
|
+
accept('error', 'Invalid relation, expected to have source defined or be inside the element', {
|
|
14
24
|
node: el,
|
|
15
|
-
|
|
25
|
+
keyword: '->'
|
|
16
26
|
});
|
|
17
27
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
node: el,
|
|
21
|
-
property: 'source'
|
|
22
|
-
});
|
|
28
|
+
else {
|
|
29
|
+
sourceEl = el.$container.$container;
|
|
23
30
|
}
|
|
24
|
-
return;
|
|
25
31
|
}
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
const source = sourceEl && fqnIndex.getFqn(sourceEl);
|
|
33
|
+
if (sourceEl && !source) {
|
|
34
|
+
accept('error', 'Source not found (not parsed/indexed yet)', {
|
|
35
|
+
node: el,
|
|
36
|
+
property: 'source'
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (source && target && isSameHierarchy(source, target)) {
|
|
40
|
+
return accept('error', 'Invalid parent-child relationship', {
|
|
28
41
|
node: el
|
|
29
42
|
});
|
|
30
43
|
}
|
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.35.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"test:watch": "vitest"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@likec4/core": "0.
|
|
46
|
-
"langium": "^2.0.
|
|
45
|
+
"@likec4/core": "0.35.0",
|
|
46
|
+
"langium": "^2.0.2",
|
|
47
47
|
"nanoid": "^4.0.2",
|
|
48
48
|
"object-hash": "^3.0.0",
|
|
49
49
|
"rambdax": "^9.1.1",
|
|
@@ -56,9 +56,10 @@
|
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^18.15.11",
|
|
58
58
|
"@types/object-hash": "^3.0.2",
|
|
59
|
-
"langium-cli": "^2.0.
|
|
59
|
+
"langium-cli": "^2.0.1",
|
|
60
60
|
"npm-run-all": "^4.1.5",
|
|
61
|
-
"typescript": "^5.
|
|
62
|
-
"vitest": "^0.34.
|
|
63
|
-
}
|
|
61
|
+
"typescript": "^5.2.2",
|
|
62
|
+
"vitest": "^0.34.3"
|
|
63
|
+
},
|
|
64
|
+
"packageManager": "yarn@3.6.3"
|
|
64
65
|
}
|