@likec4/language-server 1.23.1 → 1.24.1
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/LikeC4FileSystem.d.ts +1 -0
- package/dist/LikeC4FileSystem.js +7 -0
- package/dist/Rpc.js +13 -11
- package/dist/ast.d.ts +13 -29
- package/dist/ast.js +3 -70
- package/dist/bundled.mjs +2441 -2610
- package/dist/generated/ast.d.ts +36 -8
- package/dist/generated/ast.js +44 -2
- package/dist/generated/grammar.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/likec4lib.d.ts +2 -0
- package/dist/likec4lib.js +3 -0
- package/dist/lsp/CodeLensProvider.js +10 -6
- package/dist/lsp/CompletionProvider.js +20 -2
- package/dist/lsp/DocumentLinkProvider.d.ts +3 -3
- package/dist/lsp/DocumentLinkProvider.js +5 -5
- package/dist/lsp/DocumentSymbolProvider.d.ts +1 -1
- package/dist/lsp/DocumentSymbolProvider.js +5 -2
- package/dist/lsp/HoverProvider.js +20 -7
- package/dist/lsp/SemanticTokenProvider.js +18 -1
- package/dist/model/builder/MergedExtends.d.ts +12 -0
- package/dist/model/builder/MergedExtends.js +67 -0
- package/dist/model/builder/MergedSpecification.d.ts +29 -0
- package/dist/model/builder/MergedSpecification.js +203 -0
- package/dist/model/builder/buildModel.d.ts +3 -0
- package/dist/model/builder/buildModel.js +194 -0
- package/dist/model/deployments-index.d.ts +5 -56
- package/dist/model/deployments-index.js +61 -137
- package/dist/model/fqn-index.d.ts +50 -19
- package/dist/model/fqn-index.js +176 -69
- package/dist/model/index.d.ts +0 -1
- package/dist/model/index.js +0 -1
- package/dist/model/model-builder.d.ts +10 -9
- package/dist/model/model-builder.js +102 -547
- package/dist/model/model-locator.d.ts +2 -1
- package/dist/model/model-locator.js +7 -9
- package/dist/model/model-parser.d.ts +156 -150
- package/dist/model/model-parser.js +68 -38
- package/dist/model/parser/Base.d.ts +3 -3
- package/dist/model/parser/Base.js +15 -9
- package/dist/model/parser/DeploymentModelParser.d.ts +4 -3
- package/dist/model/parser/DeploymentModelParser.js +54 -3
- package/dist/model/parser/DeploymentViewParser.d.ts +3 -2
- package/dist/model/parser/FqnRefParser.d.ts +2 -2
- package/dist/model/parser/GlobalsParser.d.ts +3 -2
- package/dist/model/parser/ModelParser.d.ts +4 -4
- package/dist/model/parser/ModelParser.js +45 -4
- package/dist/model/parser/PredicatesParser.d.ts +2 -2
- package/dist/model/parser/SpecificationParser.d.ts +2 -2
- package/dist/model/parser/ViewsParser.d.ts +3 -2
- package/dist/module.d.ts +2 -3
- package/dist/module.js +2 -3
- package/dist/references/scope-computation.d.ts +1 -1
- package/dist/references/scope-computation.js +14 -11
- package/dist/references/scope-provider.d.ts +16 -4
- package/dist/references/scope-provider.js +64 -30
- package/dist/test/testServices.d.ts +2 -1
- package/dist/test/testServices.js +23 -20
- package/dist/utils/elementRef.d.ts +1 -1
- package/dist/utils/elementRef.js +3 -3
- package/dist/validation/deployment-checks.d.ts +1 -0
- package/dist/validation/deployment-checks.js +12 -0
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +8 -1
- package/dist/views/configurable-layouter.js +3 -3
- package/dist/views/likec4-views.d.ts +1 -0
- package/dist/views/likec4-views.js +11 -11
- package/package.json +12 -13
- package/dist/bundled.d.ts +0 -8
- package/dist/bundled.js +0 -25
- package/dist/model/fqn-computation.d.ts +0 -3
- package/dist/model/fqn-computation.js +0 -72
package/dist/model/fqn-index.js
CHANGED
|
@@ -1,103 +1,210 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { invariant, nonNullable } from "@likec4/core";
|
|
2
|
+
import { AsFqn } from "@likec4/core/types";
|
|
3
|
+
import { ancestorsFqn, compareNatural, DefaultWeakMap, MultiMap, sortNaturalByFqn } from "@likec4/core/utils";
|
|
4
|
+
import {
|
|
5
|
+
AstUtils,
|
|
6
|
+
DocumentState,
|
|
7
|
+
stream,
|
|
8
|
+
WorkspaceCache
|
|
9
|
+
} from "langium";
|
|
10
|
+
import { isDefined, isEmpty, isTruthy } from "remeda";
|
|
11
|
+
import {
|
|
12
|
+
ast,
|
|
13
|
+
ElementOps,
|
|
14
|
+
isLikeC4LangiumDocument
|
|
15
|
+
} from "../ast.js";
|
|
16
|
+
import { logWarnError } from "../logger.js";
|
|
5
17
|
import { ADisposable } from "../utils/index.js";
|
|
6
|
-
import {
|
|
18
|
+
import { readStrictFqn } from "../utils/elementRef.js";
|
|
7
19
|
export class FqnIndex extends ADisposable {
|
|
8
|
-
constructor(services) {
|
|
20
|
+
constructor(services, cachePrefix = "fqn-index") {
|
|
9
21
|
super();
|
|
10
22
|
this.services = services;
|
|
23
|
+
this.cachePrefix = cachePrefix;
|
|
11
24
|
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
25
|
+
this.documentCache = new DefaultWeakMap((doc) => this.createDocumentIndex(doc));
|
|
26
|
+
this.workspaceCache = new WorkspaceCache(services.shared, DocumentState.IndexedContent);
|
|
12
27
|
this.onDispose(
|
|
13
|
-
services.shared.workspace.DocumentBuilder.
|
|
28
|
+
services.shared.workspace.DocumentBuilder.onDocumentPhase(
|
|
14
29
|
DocumentState.IndexedContent,
|
|
15
|
-
async (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
delete doc.c4fqnIndex;
|
|
19
|
-
delete doc.c4Elements;
|
|
20
|
-
delete doc.c4Specification;
|
|
21
|
-
delete doc.c4Relations;
|
|
22
|
-
delete doc.c4Deployments;
|
|
23
|
-
delete doc.c4DeploymentRelations;
|
|
24
|
-
delete doc.c4Globals;
|
|
25
|
-
delete doc.c4Views;
|
|
26
|
-
try {
|
|
27
|
-
computeDocumentFqn(doc, services);
|
|
28
|
-
} catch (e) {
|
|
29
|
-
logWarnError(e);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
30
|
+
async (doc, _cancelToken) => {
|
|
31
|
+
if (isLikeC4LangiumDocument(doc)) {
|
|
32
|
+
this.documentCache.set(doc, this.createDocumentIndex(doc));
|
|
32
33
|
}
|
|
33
34
|
return await Promise.resolve();
|
|
34
35
|
}
|
|
35
36
|
)
|
|
36
37
|
);
|
|
37
|
-
logger.debug(`[FqnIndex] Created`);
|
|
38
38
|
}
|
|
39
39
|
langiumDocuments;
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
documentCache;
|
|
41
|
+
workspaceCache;
|
|
42
|
+
documents() {
|
|
43
|
+
return this.langiumDocuments.all.filter(
|
|
44
|
+
(d) => isLikeC4LangiumDocument(d) && d.state >= DocumentState.IndexedContent
|
|
45
|
+
);
|
|
42
46
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
+
get(document) {
|
|
48
|
+
if (document.state < DocumentState.IndexedContent) {
|
|
49
|
+
logWarnError(`Document ${document.uri.path} is not indexed`);
|
|
50
|
+
}
|
|
51
|
+
return this.documentCache.get(document);
|
|
47
52
|
}
|
|
48
53
|
getFqn(el) {
|
|
49
|
-
|
|
54
|
+
invariant(ast.isElement(el) || ast.isDeploymentElement(el));
|
|
55
|
+
let id = ElementOps.readId(el);
|
|
56
|
+
if (isTruthy(id)) {
|
|
57
|
+
return id;
|
|
58
|
+
}
|
|
59
|
+
const doc = AstUtils.getDocument(el);
|
|
60
|
+
invariant(isLikeC4LangiumDocument(doc));
|
|
61
|
+
logWarnError(`Document ${doc.uri.path} is not indexed, but getFqn was called`);
|
|
62
|
+
this.get(doc);
|
|
63
|
+
return nonNullable(ElementOps.readId(el), "Element fqn must be set, invalid state");
|
|
50
64
|
}
|
|
51
65
|
byFqn(fqn) {
|
|
52
|
-
return this.
|
|
53
|
-
return
|
|
54
|
-
|
|
66
|
+
return stream(this.workspaceCache.get(`${this.cachePrefix}:${fqn}`, () => {
|
|
67
|
+
return this.documents().toArray().flatMap((doc) => {
|
|
68
|
+
return this.get(doc).byFqn(fqn);
|
|
69
|
+
});
|
|
70
|
+
}));
|
|
55
71
|
}
|
|
56
72
|
directChildrenOf(parent) {
|
|
57
|
-
return stream(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
return stream(
|
|
74
|
+
this.workspaceCache.get(`${this.cachePrefix}:directChildrenOf:${parent}`, () => {
|
|
75
|
+
const allchildren = this.documents().reduce((map, doc) => {
|
|
76
|
+
this.get(doc).children(parent).forEach((desc) => {
|
|
77
|
+
map.set(desc.name, desc);
|
|
78
|
+
});
|
|
79
|
+
return map;
|
|
80
|
+
}, new MultiMap());
|
|
81
|
+
return uniqueByName(allchildren).sort((a, b) => compareNatural(a.name, b.name));
|
|
82
|
+
})
|
|
83
|
+
);
|
|
64
84
|
}
|
|
65
85
|
/**
|
|
66
86
|
* Returns descedant elements with unique names in the scope
|
|
67
87
|
*/
|
|
68
88
|
uniqueDescedants(parent) {
|
|
69
|
-
return
|
|
70
|
-
() => {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
89
|
+
return stream(
|
|
90
|
+
this.workspaceCache.get(`${this.cachePrefix}:uniqueDescedants:${parent}`, () => {
|
|
91
|
+
const { children, descendants } = this.documents().reduce((map, doc) => {
|
|
92
|
+
const docIndex = this.get(doc);
|
|
93
|
+
docIndex.children(parent).forEach((desc) => {
|
|
94
|
+
map.children.set(desc.name, desc);
|
|
95
|
+
});
|
|
96
|
+
docIndex.descendants(parent).forEach((desc) => {
|
|
97
|
+
map.descendants.set(desc.name, desc);
|
|
98
|
+
});
|
|
99
|
+
return map;
|
|
100
|
+
}, {
|
|
101
|
+
children: new MultiMap(),
|
|
102
|
+
descendants: new MultiMap()
|
|
84
103
|
});
|
|
85
|
-
|
|
86
|
-
|
|
104
|
+
const uniqueChildren = uniqueByName(children).sort((a, b) => compareNatural(a.name, b.name));
|
|
105
|
+
const uniqueDescendants = [...descendants.associations()].flatMap(([_name, descs]) => descs.length === 1 && !children.has(_name) ? descs : []);
|
|
106
|
+
return [
|
|
107
|
+
...uniqueChildren,
|
|
108
|
+
...sortNaturalByFqn(uniqueDescendants)
|
|
109
|
+
];
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
createDocumentIndex(document) {
|
|
114
|
+
const rootElements = document.parseResult.value.models.flatMap((m) => m.elements);
|
|
115
|
+
if (rootElements.length === 0) {
|
|
116
|
+
return DocumentFqnIndex.EMPTY;
|
|
117
|
+
}
|
|
118
|
+
const root = new Array();
|
|
119
|
+
const children = new MultiMap();
|
|
120
|
+
const descendants = new MultiMap();
|
|
121
|
+
const byfqn = new MultiMap();
|
|
122
|
+
const Descriptions = this.services.workspace.AstNodeDescriptionProvider;
|
|
123
|
+
const createAndSaveDescription = (node, name, fqn) => {
|
|
124
|
+
const desc = {
|
|
125
|
+
...Descriptions.createDescription(node, name, document),
|
|
126
|
+
id: fqn
|
|
127
|
+
};
|
|
128
|
+
ElementOps.writeId(node, fqn);
|
|
129
|
+
byfqn.set(fqn, desc);
|
|
130
|
+
return desc;
|
|
131
|
+
};
|
|
132
|
+
const traverseNode = (el, parentFqn) => {
|
|
133
|
+
let thisFqn;
|
|
134
|
+
if (ast.isElement(el)) {
|
|
135
|
+
thisFqn = AsFqn(el.name, parentFqn);
|
|
136
|
+
const desc = createAndSaveDescription(el, el.name, thisFqn);
|
|
137
|
+
if (!parentFqn) {
|
|
138
|
+
root.push(desc);
|
|
139
|
+
} else {
|
|
140
|
+
children.set(parentFqn, desc);
|
|
87
141
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
142
|
+
} else {
|
|
143
|
+
thisFqn = readStrictFqn(el.element);
|
|
144
|
+
}
|
|
145
|
+
let _nested = [];
|
|
146
|
+
if (isDefined(el.body) && !isEmpty(el.body.elements)) {
|
|
147
|
+
for (const child of el.body.elements) {
|
|
148
|
+
if (!ast.isRelation(child)) {
|
|
149
|
+
try {
|
|
150
|
+
_nested.push(...traverseNode(child, thisFqn));
|
|
151
|
+
} catch (e) {
|
|
152
|
+
logWarnError(e);
|
|
153
|
+
}
|
|
91
154
|
}
|
|
92
155
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
156
|
+
}
|
|
157
|
+
const directChildren = children.get(thisFqn) ?? [];
|
|
158
|
+
_nested = [
|
|
159
|
+
...directChildren,
|
|
160
|
+
..._nested
|
|
161
|
+
];
|
|
162
|
+
for (const child of _nested) {
|
|
163
|
+
descendants.set(thisFqn, child);
|
|
164
|
+
}
|
|
165
|
+
if (ast.isExtendElement(el)) {
|
|
166
|
+
for (const ancestor of ancestorsFqn(thisFqn)) {
|
|
167
|
+
for (const child of _nested) {
|
|
168
|
+
descendants.set(ancestor, child);
|
|
169
|
+
}
|
|
98
170
|
}
|
|
99
|
-
return DONE_RESULT;
|
|
100
171
|
}
|
|
101
|
-
|
|
172
|
+
return descendants.get(thisFqn) ?? [];
|
|
173
|
+
};
|
|
174
|
+
for (const node of rootElements) {
|
|
175
|
+
try {
|
|
176
|
+
if (ast.isRelation(node)) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
traverseNode(node, null);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
logWarnError(e);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return new DocumentFqnIndex(root, children, descendants, byfqn);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function uniqueByName(multimap) {
|
|
188
|
+
return [...multimap.associations()].flatMap(([_name, descs]) => descs.length === 1 ? descs : []);
|
|
189
|
+
}
|
|
190
|
+
export class DocumentFqnIndex {
|
|
191
|
+
constructor(_rootElements, _children, _descendants, _byfqn) {
|
|
192
|
+
this._rootElements = _rootElements;
|
|
193
|
+
this._children = _children;
|
|
194
|
+
this._descendants = _descendants;
|
|
195
|
+
this._byfqn = _byfqn;
|
|
196
|
+
}
|
|
197
|
+
static EMPTY = new DocumentFqnIndex([], new MultiMap(), new MultiMap(), new MultiMap());
|
|
198
|
+
rootElements() {
|
|
199
|
+
return this._rootElements;
|
|
200
|
+
}
|
|
201
|
+
byFqn(fqn) {
|
|
202
|
+
return this._byfqn.get(fqn) ?? [];
|
|
203
|
+
}
|
|
204
|
+
children(parent) {
|
|
205
|
+
return this._children.get(parent) ?? [];
|
|
206
|
+
}
|
|
207
|
+
descendants(nodeName) {
|
|
208
|
+
return this._descendants.get(nodeName) ?? [];
|
|
102
209
|
}
|
|
103
210
|
}
|
package/dist/model/index.d.ts
CHANGED
package/dist/model/index.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
import type * as c4 from '@likec4/core';
|
|
2
|
-
import { type ViewId } from '@likec4/core';
|
|
3
|
-
import type
|
|
4
|
-
import { Disposable } from 'langium';
|
|
2
|
+
import { type ViewId, LikeC4Model } from '@likec4/core';
|
|
3
|
+
import { type Cancellation, type URI, Disposable } from 'langium';
|
|
5
4
|
import type { LikeC4Services } from '../module';
|
|
6
5
|
import { ADisposable } from '../utils';
|
|
7
6
|
type ModelParsedListener = (docs: URI[]) => void;
|
|
8
7
|
export declare class LikeC4ModelBuilder extends ADisposable {
|
|
9
|
-
private
|
|
10
|
-
private langiumDocuments;
|
|
8
|
+
private parser;
|
|
11
9
|
private listeners;
|
|
10
|
+
private cache;
|
|
11
|
+
private DocumentBuilder;
|
|
12
|
+
private LangiumDocuments;
|
|
12
13
|
constructor(services: LikeC4Services);
|
|
13
14
|
/**
|
|
14
15
|
* WARNING:
|
|
15
16
|
* This method is internal and should to be called only when all documents are known to be parsed.
|
|
16
17
|
* Otherwise, the model may be incomplete.
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
private unsafeSyncParseModel;
|
|
20
|
+
parseModel(cancelToken?: Cancellation.CancellationToken): Promise<c4.ParsedLikeC4Model | null>;
|
|
20
21
|
private previousViews;
|
|
21
22
|
/**
|
|
22
23
|
* WARNING:
|
|
23
24
|
* This method is internal and should to be called only when all documents are known to be parsed.
|
|
24
25
|
* Otherwise, the model may be incomplete.
|
|
25
26
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
unsafeSyncBuildModel(): LikeC4Model.Computed;
|
|
28
|
+
buildLikeC4Model(cancelToken?: Cancellation.CancellationToken): Promise<LikeC4Model.Computed>;
|
|
28
29
|
computeView(viewId: ViewId, cancelToken?: Cancellation.CancellationToken): Promise<c4.ComputedView | null>;
|
|
29
30
|
onModelParsed(callback: ModelParsedListener): Disposable;
|
|
30
31
|
private documents;
|