@likec4/language-server 0.37.1 → 0.41.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/contrib/likec4.monarch.ts +5 -5
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/Rpc.d.ts +7 -0
- package/dist/Rpc.js +130 -0
- package/dist/ast.d.ts +20 -0
- package/dist/ast.js +169 -141
- package/dist/elementRef.js +31 -44
- package/dist/generated/ast.d.ts +48 -7
- package/dist/generated/ast.js +344 -315
- package/dist/generated/grammar.js +2 -3177
- package/dist/generated/module.js +13 -18
- package/dist/index.js +2 -3
- package/dist/logger.js +39 -42
- package/dist/lsp/CodeLensProvider.js +28 -32
- package/dist/lsp/DocumentLinkProvider.js +26 -33
- package/dist/lsp/DocumentSymbolProvider.js +165 -167
- package/dist/lsp/HoverProvider.js +35 -48
- package/dist/lsp/SemanticTokenProvider.js +160 -201
- package/dist/lsp/index.js +5 -6
- package/dist/model/fqn-computation.js +39 -40
- package/dist/model/fqn-index.js +117 -141
- package/dist/model/index.js +5 -6
- package/dist/model/model-builder.d.ts +10 -5
- package/dist/model/model-builder.js +247 -176
- package/dist/model/model-locator.d.ts +1 -1
- package/dist/model/model-locator.js +102 -100
- package/dist/model/model-parser.d.ts +2 -6
- package/dist/model/model-parser.js +284 -286
- package/dist/module.d.ts +4 -1
- package/dist/module.js +69 -60
- package/dist/protocol.d.ts +16 -19
- package/dist/protocol.js +14 -22
- package/dist/references/index.js +2 -3
- package/dist/references/scope-computation.js +72 -69
- package/dist/references/scope-provider.js +126 -116
- package/dist/shared/WorkspaceManager.d.ts +2 -3
- package/dist/shared/WorkspaceManager.js +13 -16
- package/dist/shared/index.js +1 -2
- package/dist/test/index.js +1 -2
- package/dist/test/testServices.js +73 -61
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +4 -3
- package/dist/validation/element.d.ts +1 -0
- package/dist/validation/element.js +31 -38
- package/dist/validation/index.js +37 -46
- package/dist/validation/relation.js +46 -46
- package/dist/validation/specification.d.ts +1 -0
- package/dist/validation/specification.js +33 -30
- package/dist/validation/view.js +16 -22
- package/dist/view-utils/assignNavigateTo.d.ts +3 -0
- package/dist/view-utils/assignNavigateTo.js +20 -0
- package/dist/view-utils/index.d.ts +4 -0
- package/dist/view-utils/index.js +3 -0
- package/dist/view-utils/resolve-extended-views.d.ts +7 -0
- package/dist/view-utils/resolve-extended-views.js +41 -0
- package/dist/view-utils/resolve-relative-paths.d.ts +3 -0
- package/dist/view-utils/resolve-relative-paths.js +76 -0
- package/package.json +33 -18
- package/dist/registerProtocolHandlers.d.ts +0 -3
- package/dist/registerProtocolHandlers.js +0 -112
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import graphlib from "@dagrejs/graphlib";
|
|
2
|
+
import { isExtendsElementView } from "@likec4/core";
|
|
3
|
+
const { Graph, alg } = graphlib;
|
|
4
|
+
export function resolveRulesExtendedViews(unresolvedViews) {
|
|
5
|
+
const g = new Graph({
|
|
6
|
+
directed: true,
|
|
7
|
+
multigraph: false,
|
|
8
|
+
compound: false
|
|
9
|
+
});
|
|
10
|
+
for (const view of Object.values(unresolvedViews)) {
|
|
11
|
+
g.setNode(view.id);
|
|
12
|
+
if (isExtendsElementView(view)) {
|
|
13
|
+
g.setEdge(view.id, view.extends);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const cycles = alg.findCycles(g);
|
|
17
|
+
if (cycles.length > 0) {
|
|
18
|
+
cycles.flat().forEach((id) => g.removeNode(id));
|
|
19
|
+
}
|
|
20
|
+
const ordered = alg.postorder(g, g.sources());
|
|
21
|
+
return ordered.reduce((acc, id) => {
|
|
22
|
+
const view = unresolvedViews[id];
|
|
23
|
+
if (!view) {
|
|
24
|
+
return acc;
|
|
25
|
+
}
|
|
26
|
+
if (isExtendsElementView(view)) {
|
|
27
|
+
const extendsFrom = acc[view.extends];
|
|
28
|
+
if (!extendsFrom) {
|
|
29
|
+
return acc;
|
|
30
|
+
}
|
|
31
|
+
return Object.assign(acc, {
|
|
32
|
+
[view.id]: {
|
|
33
|
+
...extendsFrom,
|
|
34
|
+
...view,
|
|
35
|
+
rules: [...extendsFrom.rules, ...view.rules]
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return Object.assign(acc, { [view.id]: view });
|
|
40
|
+
}, {});
|
|
41
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { uniq, zip } from "rambdax";
|
|
2
|
+
import { hasAtLeast } from "remeda";
|
|
3
|
+
import { invariant } from "@likec4/core";
|
|
4
|
+
function commonAncestorPath(views, sep = "/") {
|
|
5
|
+
if (views.length <= 1)
|
|
6
|
+
return "";
|
|
7
|
+
const uniqURIs = uniq(views.flatMap(({ docUri }) => docUri ? [docUri] : []));
|
|
8
|
+
if (uniqURIs.length === 0)
|
|
9
|
+
return "";
|
|
10
|
+
if (uniqURIs.length === 1) {
|
|
11
|
+
invariant(hasAtLeast(uniqURIs, 1));
|
|
12
|
+
return new URL(uniqURIs[0]).pathname;
|
|
13
|
+
}
|
|
14
|
+
invariant(hasAtLeast(uniqURIs, 2), "Expected at least 2 unique URIs");
|
|
15
|
+
const [baseUri, ...tail] = uniqURIs;
|
|
16
|
+
const parts = new URL(baseUri).pathname.split(sep);
|
|
17
|
+
let endOfPrefix = parts.length;
|
|
18
|
+
for (const uri of tail) {
|
|
19
|
+
if (uri === baseUri) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const compare = new URL(uri).pathname.split(sep);
|
|
23
|
+
for (let i = 0; i < endOfPrefix; i++) {
|
|
24
|
+
if (compare[i] !== parts[i]) {
|
|
25
|
+
endOfPrefix = i;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (endOfPrefix === 0)
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
const prefix = parts.slice(0, endOfPrefix).join(sep);
|
|
32
|
+
return prefix.endsWith(sep) ? prefix : prefix + sep;
|
|
33
|
+
}
|
|
34
|
+
export function resolveRelativePaths(views) {
|
|
35
|
+
const commonPrefix = commonAncestorPath(views);
|
|
36
|
+
return views.map((view) => {
|
|
37
|
+
if (!view.docUri) {
|
|
38
|
+
return {
|
|
39
|
+
...view,
|
|
40
|
+
parts: []
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const path = new URL(view.docUri).pathname;
|
|
44
|
+
const parts = path.replace(commonPrefix, "").split("/");
|
|
45
|
+
parts.pop();
|
|
46
|
+
return {
|
|
47
|
+
...view,
|
|
48
|
+
parts
|
|
49
|
+
};
|
|
50
|
+
}).sort((a, b) => {
|
|
51
|
+
if (a.parts.length === b.parts.length) {
|
|
52
|
+
if (a.parts.length === 0) {
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
if (a.parts.length === 1 && hasAtLeast(a.parts, 1) && hasAtLeast(b.parts, 1)) {
|
|
56
|
+
return a.parts[0].localeCompare(b.parts[0]);
|
|
57
|
+
}
|
|
58
|
+
for (const [_a, _b] of zip(a.parts, b.parts)) {
|
|
59
|
+
const compare = _a.localeCompare(_b);
|
|
60
|
+
if (compare !== 0) {
|
|
61
|
+
return compare;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
return a.parts.length - b.parts.length;
|
|
67
|
+
}).map(({ parts, ...view }) => {
|
|
68
|
+
if (view.docUri) {
|
|
69
|
+
return {
|
|
70
|
+
...view,
|
|
71
|
+
relativePath: parts.join("/")
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return view;
|
|
75
|
+
});
|
|
76
|
+
}
|
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.41.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -20,46 +20,61 @@
|
|
|
20
20
|
"directory": "packages/language-server"
|
|
21
21
|
},
|
|
22
22
|
"type": "module",
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
26
29
|
"publishConfig": {
|
|
27
30
|
"registry": "https://registry.npmjs.org",
|
|
28
|
-
"access": "public"
|
|
31
|
+
"access": "public",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
29
38
|
},
|
|
30
39
|
"scripts": {
|
|
31
|
-
"
|
|
32
|
-
"turbo-compile": "run -T turbo run compile --filter='language-server'",
|
|
33
|
-
"compile": "tsc --noEmit",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
34
41
|
"watch:langium": "langium generate --watch",
|
|
35
42
|
"watch:ts": "tsc --watch",
|
|
36
43
|
"generate": "langium generate",
|
|
37
|
-
"
|
|
44
|
+
"prepack": "unbuild",
|
|
45
|
+
"build:turbo": "run -T turbo build --filter='language-server'",
|
|
46
|
+
"build": "unbuild",
|
|
38
47
|
"dev": "run-p 'watch:*'",
|
|
39
48
|
"lint": "run -T eslint src/ --fix",
|
|
40
49
|
"clean": "run -T rimraf dist contrib",
|
|
41
|
-
"test": "vitest run"
|
|
42
|
-
"test:watch": "vitest"
|
|
50
|
+
"test": "vitest run"
|
|
43
51
|
},
|
|
44
52
|
"dependencies": {
|
|
45
|
-
"@likec4/core": "0.
|
|
53
|
+
"@likec4/core": "0.41.0",
|
|
54
|
+
"@likec4/graph": "0.41.0",
|
|
46
55
|
"langium": "^2.0.2",
|
|
47
|
-
"nanoid": "^4.0.2",
|
|
48
56
|
"object-hash": "^3.0.0",
|
|
57
|
+
"p-debounce": "^4.0.0",
|
|
58
|
+
"p-throttle": "^5.1.0",
|
|
49
59
|
"rambdax": "^9.1.1",
|
|
50
|
-
"remeda": "^1.
|
|
60
|
+
"remeda": "^1.27.1",
|
|
51
61
|
"strip-indent": "^4.0.0",
|
|
52
62
|
"vscode-languageserver": "~8.1.0",
|
|
53
63
|
"vscode-languageserver-protocol": "~3.17.3",
|
|
54
64
|
"vscode-uri": "~3.0.7"
|
|
55
65
|
},
|
|
56
66
|
"devDependencies": {
|
|
57
|
-
"@types/node": "^
|
|
58
|
-
"@types/object-hash": "^3.0.
|
|
67
|
+
"@types/node": "^20.8.7",
|
|
68
|
+
"@types/object-hash": "^3.0.5",
|
|
69
|
+
"execa": "^8.0.1",
|
|
59
70
|
"langium-cli": "^2.0.1",
|
|
60
71
|
"npm-run-all": "^4.1.5",
|
|
61
72
|
"typescript": "^5.2.2",
|
|
62
|
-
"
|
|
73
|
+
"unbuild": "^2.0.0",
|
|
74
|
+
"vitest": "^0.34.6"
|
|
63
75
|
},
|
|
64
|
-
"packageManager": "yarn@3.6.
|
|
76
|
+
"packageManager": "yarn@3.6.4",
|
|
77
|
+
"volta": {
|
|
78
|
+
"extends": "../../package.json"
|
|
79
|
+
}
|
|
65
80
|
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { URI } from 'vscode-uri';
|
|
2
|
-
import { logger, logError } from './logger';
|
|
3
|
-
import { Rpc } from './protocol';
|
|
4
|
-
import { nonexhaustive } from '@likec4/core';
|
|
5
|
-
import { isLikeC4LangiumDocument } from './ast';
|
|
6
|
-
export function registerProtocolHandlers(services) {
|
|
7
|
-
const connection = services.shared.lsp.Connection;
|
|
8
|
-
if (!connection) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
const modelBuilder = services.likec4.ModelBuilder;
|
|
12
|
-
const modelLocator = services.likec4.ModelLocator;
|
|
13
|
-
const LangiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
14
|
-
connection.onRequest(Rpc.fetchModel, async (_cancelToken) => {
|
|
15
|
-
let model;
|
|
16
|
-
try {
|
|
17
|
-
model = modelBuilder.buildModel() ?? null;
|
|
18
|
-
}
|
|
19
|
-
catch (e) {
|
|
20
|
-
model = null;
|
|
21
|
-
logError(e);
|
|
22
|
-
}
|
|
23
|
-
return Promise.resolve({ model });
|
|
24
|
-
});
|
|
25
|
-
connection.onRequest(Rpc.fetchRawModel, async (_cancelToken) => {
|
|
26
|
-
let rawmodel;
|
|
27
|
-
try {
|
|
28
|
-
rawmodel = modelBuilder.buildRawModel() ?? null;
|
|
29
|
-
}
|
|
30
|
-
catch (e) {
|
|
31
|
-
rawmodel = null;
|
|
32
|
-
logError(e);
|
|
33
|
-
}
|
|
34
|
-
return Promise.resolve({ rawmodel });
|
|
35
|
-
});
|
|
36
|
-
connection.onRequest(Rpc.computeView, ({ viewId }) => {
|
|
37
|
-
return {
|
|
38
|
-
view: modelBuilder.computeView(viewId)
|
|
39
|
-
};
|
|
40
|
-
});
|
|
41
|
-
connection.onRequest(Rpc.rebuild, async (cancelToken) => {
|
|
42
|
-
const changed = LangiumDocuments.all
|
|
43
|
-
.map(d => {
|
|
44
|
-
// clean up any computed properties
|
|
45
|
-
if (isLikeC4LangiumDocument(d)) {
|
|
46
|
-
delete d.c4Specification;
|
|
47
|
-
delete d.c4Elements;
|
|
48
|
-
delete d.c4Relations;
|
|
49
|
-
delete d.c4Views;
|
|
50
|
-
delete d.c4fqns;
|
|
51
|
-
}
|
|
52
|
-
return d.uri;
|
|
53
|
-
})
|
|
54
|
-
.toArray();
|
|
55
|
-
logger.debug(`[ProtocolHandlers] rebuild all documents: [
|
|
56
|
-
${changed.map(d => d.toString()).join('\n ')}
|
|
57
|
-
]`);
|
|
58
|
-
await services.shared.workspace.DocumentBuilder.update(changed, [], cancelToken);
|
|
59
|
-
return {
|
|
60
|
-
docs: changed.map(d => d.toString())
|
|
61
|
-
};
|
|
62
|
-
});
|
|
63
|
-
connection.onRequest(Rpc.buildDocuments, async ({ docs }, cancelToken) => {
|
|
64
|
-
if (docs.length === 0) {
|
|
65
|
-
logger.debug(`[ProtocolHandlers] received empty request to rebuild`);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
logger.debug(`[ProtocolHandlers] received request to buildDocuments:
|
|
69
|
-
- ${docs.join('\n - ')}`);
|
|
70
|
-
const changed = [];
|
|
71
|
-
for (const d of docs) {
|
|
72
|
-
try {
|
|
73
|
-
const uri = URI.parse(d);
|
|
74
|
-
if (LangiumDocuments.hasDocument(uri)) {
|
|
75
|
-
changed.push(uri);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
logger.warn(`LangiumDocuments does not have document: ${d}`);
|
|
79
|
-
LangiumDocuments.getOrCreateDocument(uri);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
catch (e) {
|
|
83
|
-
logError(e);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (changed.length !== docs.length) {
|
|
87
|
-
const all = LangiumDocuments.all.map(d => d.uri.toString()).toArray();
|
|
88
|
-
logger.warn(`
|
|
89
|
-
We have in LangiumDocuments: [
|
|
90
|
-
${all.join('\n ')}
|
|
91
|
-
]
|
|
92
|
-
We rebuild: [
|
|
93
|
-
${changed.join('\n ')}
|
|
94
|
-
]
|
|
95
|
-
`.trim());
|
|
96
|
-
}
|
|
97
|
-
await services.shared.workspace.DocumentBuilder.update(changed, [], cancelToken);
|
|
98
|
-
});
|
|
99
|
-
connection.onRequest(Rpc.locate, params => {
|
|
100
|
-
if ('element' in params) {
|
|
101
|
-
return modelLocator.locateElement(params.element, params.property ?? 'name');
|
|
102
|
-
}
|
|
103
|
-
if ('relation' in params) {
|
|
104
|
-
return modelLocator.locateRelation(params.relation);
|
|
105
|
-
}
|
|
106
|
-
if ('view' in params) {
|
|
107
|
-
return modelLocator.locateView(params.view);
|
|
108
|
-
}
|
|
109
|
-
nonexhaustive(params);
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
//# sourceMappingURL=registerProtocolHandlers.js.map
|