@likec4/language-server 0.29.0 → 0.30.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/model/fqn-computation.js +3 -4
- package/dist/model/fqn-index.d.ts +10 -9
- package/dist/model/fqn-index.js +7 -5
- package/dist/model/model-builder.js +18 -13
- package/dist/references/scope-provider.js +11 -14
- package/dist/validation/element.js +1 -7
- package/dist/validation/relation.js +2 -2
- package/package.json +2 -2
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { failExpectedNever } from '@likec4/core';
|
|
2
|
-
import { Fqn } from '@likec4/core/types';
|
|
1
|
+
import { failExpectedNever, AsFqn } from '@likec4/core';
|
|
3
2
|
import { MultiMap } from 'langium';
|
|
4
3
|
import { isEmpty, isNil } from 'remeda';
|
|
5
4
|
import { ElementOps, ast } from '../ast';
|
|
6
5
|
import { strictElementRefFqn } from '../elementRef';
|
|
7
6
|
export function computeDocumentFqn(document, services) {
|
|
8
|
-
const c4fqns = document.c4fqns = new MultiMap();
|
|
7
|
+
const c4fqns = (document.c4fqns = new MultiMap());
|
|
9
8
|
const { model } = document.parseResult.value;
|
|
10
9
|
if (!model?.elements) {
|
|
11
10
|
return;
|
|
@@ -26,7 +25,7 @@ export function computeDocumentFqn(document, services) {
|
|
|
26
25
|
continue;
|
|
27
26
|
}
|
|
28
27
|
if (ast.isElement(el)) {
|
|
29
|
-
const fqn =
|
|
28
|
+
const fqn = AsFqn(el.name, parent);
|
|
30
29
|
const path = locator.getAstNodePath(el);
|
|
31
30
|
c4fqns.add(fqn, path);
|
|
32
31
|
ElementOps.writeId(el, fqn);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { Fqn } from '@likec4/core
|
|
2
|
-
import type { LangiumDocument, LangiumDocuments } from 'langium';
|
|
3
|
-
import { StreamImpl } from 'langium';
|
|
1
|
+
import type { Fqn } from '@likec4/core';
|
|
2
|
+
import type { LangiumDocument, LangiumDocuments, Stream } from 'langium';
|
|
4
3
|
import type { ast } from '../ast';
|
|
5
4
|
import { type LikeC4LangiumDocument } from '../ast';
|
|
6
5
|
import type { LikeC4Services } from '../module';
|
|
@@ -15,18 +14,20 @@ export interface FqnIndexEntry {
|
|
|
15
14
|
path: string;
|
|
16
15
|
}
|
|
17
16
|
export declare class FqnIndex {
|
|
18
|
-
private services;
|
|
19
17
|
protected langiumDocuments: LangiumDocuments;
|
|
20
18
|
constructor(services: LikeC4Services);
|
|
21
19
|
private documents;
|
|
22
20
|
private entries;
|
|
23
|
-
|
|
24
|
-
byFqn(fqn: Fqn):
|
|
21
|
+
getFqn(el: ast.Element): Fqn | null;
|
|
22
|
+
byFqn(fqn: Fqn): Stream<{
|
|
25
23
|
path: string;
|
|
26
|
-
doc:
|
|
24
|
+
doc: LikeC4LangiumDocument;
|
|
27
25
|
}>;
|
|
28
|
-
directChildrenOf(parent: Fqn):
|
|
29
|
-
|
|
26
|
+
directChildrenOf(parent: Fqn): Stream<FqnIndexEntry>;
|
|
27
|
+
/**
|
|
28
|
+
* Returns descedant elements with unique names in the scope
|
|
29
|
+
*/
|
|
30
|
+
uniqueDescedants(parent: Fqn): Stream<FqnIndexEntry>;
|
|
30
31
|
}
|
|
31
32
|
export {};
|
|
32
33
|
//# sourceMappingURL=fqn-index.d.ts.map
|
package/dist/model/fqn-index.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { nameFromFqn, parentFqn } from '@likec4/core
|
|
1
|
+
import { nameFromFqn, parentFqn } from '@likec4/core';
|
|
2
2
|
import { DONE_RESULT, DocumentState, MultiMap, StreamImpl } from 'langium';
|
|
3
3
|
import { isNil } from 'remeda';
|
|
4
4
|
import { ElementOps, isLikeC4LangiumDocument } from '../ast';
|
|
5
5
|
import { logger } from '../logger';
|
|
6
6
|
import { computeDocumentFqn } from './fqn-computation';
|
|
7
7
|
export function isFqnIndexedDocument(doc) {
|
|
8
|
-
return isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns);
|
|
8
|
+
return (isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns));
|
|
9
9
|
}
|
|
10
10
|
export class FqnIndex {
|
|
11
|
-
services;
|
|
12
11
|
langiumDocuments;
|
|
13
12
|
constructor(services) {
|
|
14
|
-
this.services = services;
|
|
15
13
|
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
16
14
|
services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, (docs, _cancelToken) => {
|
|
17
15
|
for (const doc of docs) {
|
|
@@ -32,7 +30,7 @@ export class FqnIndex {
|
|
|
32
30
|
entries() {
|
|
33
31
|
return this.documents().flatMap(doc => doc.c4fqns.entries().map(([fqn, path]) => ({ fqn, path, doc })));
|
|
34
32
|
}
|
|
35
|
-
|
|
33
|
+
getFqn(el) {
|
|
36
34
|
return ElementOps.readId(el) ?? null;
|
|
37
35
|
// if (fqn) {
|
|
38
36
|
// const doc = getDocument(el)
|
|
@@ -75,6 +73,9 @@ export class FqnIndex {
|
|
|
75
73
|
return DONE_RESULT;
|
|
76
74
|
});
|
|
77
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Returns descedant elements with unique names in the scope
|
|
78
|
+
*/
|
|
78
79
|
uniqueDescedants(parent) {
|
|
79
80
|
return new StreamImpl(() => {
|
|
80
81
|
const prefix = `${parent}.`;
|
|
@@ -86,6 +87,7 @@ export class FqnIndex {
|
|
|
86
87
|
.forEach(e => {
|
|
87
88
|
const name = nameFromFqn(e.fqn);
|
|
88
89
|
const entry = { ...e, name };
|
|
90
|
+
// To keep direct children always
|
|
89
91
|
if (parentFqn(e.fqn) === parent) {
|
|
90
92
|
childrenNames.add(name);
|
|
91
93
|
nested.add(name, entry);
|
|
@@ -67,27 +67,32 @@ export class LikeC4ModelBuilder {
|
|
|
67
67
|
kinds: {}
|
|
68
68
|
};
|
|
69
69
|
R.forEach(R.map(docs, R.prop('c4Specification')), spec => Object.assign(c4Specification.kinds, spec.kinds));
|
|
70
|
-
const toModelElement = (
|
|
71
|
-
const kind = c4Specification.kinds[
|
|
70
|
+
const toModelElement = ({ astPath, ...parsed }) => {
|
|
71
|
+
const kind = c4Specification.kinds[parsed.kind];
|
|
72
72
|
if (kind) {
|
|
73
|
-
const { astPath, ...model } = el;
|
|
74
73
|
return {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
shape: kind.shape,
|
|
75
|
+
color: kind.color,
|
|
76
|
+
description: null,
|
|
77
|
+
technology: null,
|
|
78
|
+
tags: [],
|
|
79
|
+
...parsed
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
return null;
|
|
81
83
|
};
|
|
82
84
|
const elements = R.pipe(R.flatMap(docs, d => d.c4Elements), R.map(toModelElement), R.compact, R.sort(compareByFqnHierarchically), R.reduce((acc, el) => {
|
|
83
85
|
const parent = parentFqn(el.id);
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
if (parent && R.isNil(acc[parent])) {
|
|
87
|
+
logger.warn(`No parent found for ${el.id}`);
|
|
88
|
+
return acc;
|
|
89
|
+
}
|
|
90
|
+
if (el.id in acc) {
|
|
91
|
+
// should not happen, as validated
|
|
92
|
+
logger.warn(`Duplicate element id: ${el.id}`);
|
|
93
|
+
return acc;
|
|
90
94
|
}
|
|
95
|
+
acc[el.id] = el;
|
|
91
96
|
return acc;
|
|
92
97
|
}, {}));
|
|
93
98
|
const toModelRelation = ({ astPath, source, target, ...model }) => {
|
|
@@ -339,7 +344,7 @@ export class LikeC4ModelBuilder {
|
|
|
339
344
|
if (ast.isExtendElement(node)) {
|
|
340
345
|
return strictElementRefFqn(node.element);
|
|
341
346
|
}
|
|
342
|
-
const fqn = this.fqnIndex.
|
|
347
|
+
const fqn = this.fqnIndex.getFqn(node);
|
|
343
348
|
invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
|
|
344
349
|
return fqn;
|
|
345
350
|
}
|
|
@@ -17,18 +17,15 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
17
17
|
this.fqnIndex = services.likec4.FqnIndex;
|
|
18
18
|
}
|
|
19
19
|
directChildrenOf(parent) {
|
|
20
|
-
return this.fqnIndex
|
|
21
|
-
.directChildrenOf(parent)
|
|
22
|
-
.map(toAstNodeDescription);
|
|
20
|
+
return this.fqnIndex.directChildrenOf(parent).map(toAstNodeDescription);
|
|
23
21
|
}
|
|
22
|
+
// we need lazy resolving here
|
|
24
23
|
uniqueDescedants(of) {
|
|
25
24
|
return new StreamImpl(() => {
|
|
26
25
|
const element = of();
|
|
27
|
-
const fqn = element && this.fqnIndex.
|
|
26
|
+
const fqn = element && this.fqnIndex.getFqn(element);
|
|
28
27
|
if (fqn) {
|
|
29
|
-
return this.fqnIndex.uniqueDescedants(fqn)
|
|
30
|
-
.map(toAstNodeDescription)
|
|
31
|
-
.iterator();
|
|
28
|
+
return this.fqnIndex.uniqueDescedants(fqn).map(toAstNodeDescription).iterator();
|
|
32
29
|
}
|
|
33
30
|
return null;
|
|
34
31
|
}, iterator => {
|
|
@@ -57,21 +54,21 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
57
54
|
getScope(context) {
|
|
58
55
|
const referenceType = this.reflection.getReferenceType(context);
|
|
59
56
|
try {
|
|
60
|
-
const
|
|
57
|
+
const container = context.container;
|
|
61
58
|
// const path = this.services.workspace.AstNodeLocator.getAstNodePath(node)
|
|
62
59
|
if (referenceType === ast.Element) {
|
|
63
|
-
if (ast.isStrictElementRef(
|
|
64
|
-
if (isElementRefHead(
|
|
60
|
+
if (ast.isStrictElementRef(container)) {
|
|
61
|
+
if (isElementRefHead(container)) {
|
|
65
62
|
return this.getGlobalScope(referenceType);
|
|
66
63
|
}
|
|
67
|
-
const parent = parentStrictElementRef(
|
|
64
|
+
const parent = parentStrictElementRef(container);
|
|
68
65
|
return new StreamScope(this.directChildrenOf(parent));
|
|
69
66
|
}
|
|
70
|
-
if (ast.isElementRef(
|
|
71
|
-
return new StreamScope(this.scopeElementRef(
|
|
67
|
+
if (ast.isElementRef(container) && !isElementRefHead(container)) {
|
|
68
|
+
return new StreamScope(this.scopeElementRef(container));
|
|
72
69
|
}
|
|
73
70
|
}
|
|
74
|
-
return this.computeScope(
|
|
71
|
+
return this.computeScope(container, referenceType);
|
|
75
72
|
}
|
|
76
73
|
catch (e) {
|
|
77
74
|
// console.error(e)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const elementChecks = (services) => {
|
|
2
2
|
const fqnIndex = services.likec4.FqnIndex;
|
|
3
3
|
return (el, accept) => {
|
|
4
|
-
const fqn = fqnIndex.
|
|
4
|
+
const fqn = fqnIndex.getFqn(el);
|
|
5
5
|
if (!fqn) {
|
|
6
6
|
accept('error', 'Not indexed element', {
|
|
7
7
|
node: el,
|
|
@@ -11,12 +11,6 @@ export const elementChecks = (services) => {
|
|
|
11
11
|
}
|
|
12
12
|
const withSameFqn = fqnIndex.byFqn(fqn).limit(2).count();
|
|
13
13
|
if (withSameFqn > 1) {
|
|
14
|
-
// console.error(withSameFqn.map(e => ({
|
|
15
|
-
// fqn,
|
|
16
|
-
// name: el.name,
|
|
17
|
-
// path: e.path,
|
|
18
|
-
// doc: e.doc.uri.toString()
|
|
19
|
-
// })))
|
|
20
14
|
accept('error', `Duplicate element name ${el.name !== fqn ? el.name + ' (' + fqn + ')' : el.name}`, {
|
|
21
15
|
node: el,
|
|
22
16
|
property: 'name'
|
|
@@ -6,8 +6,8 @@ export const relationChecks = (services) => {
|
|
|
6
6
|
return (el, accept) => {
|
|
7
7
|
try {
|
|
8
8
|
const coupling = resolveRelationPoints(el);
|
|
9
|
-
const target = fqnIndex.
|
|
10
|
-
const source = fqnIndex.
|
|
9
|
+
const target = fqnIndex.getFqn(coupling.target);
|
|
10
|
+
const source = fqnIndex.getFqn(coupling.source);
|
|
11
11
|
if (!target || !source) {
|
|
12
12
|
if (!target) {
|
|
13
13
|
accept('error', 'Target not found', {
|
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.30.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"test:watch": "run -T vitest"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@likec4/core": "0.
|
|
62
|
+
"@likec4/core": "0.30.0",
|
|
63
63
|
"langium": "^1.2.1",
|
|
64
64
|
"nanoid": "^4.0.2",
|
|
65
65
|
"object-hash": "^3.0.0",
|