@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.
Files changed (60) hide show
  1. package/contrib/likec4.monarch.ts +5 -5
  2. package/contrib/likec4.tmLanguage.json +1 -1
  3. package/dist/Rpc.d.ts +7 -0
  4. package/dist/Rpc.js +130 -0
  5. package/dist/ast.d.ts +20 -0
  6. package/dist/ast.js +169 -141
  7. package/dist/elementRef.js +31 -44
  8. package/dist/generated/ast.d.ts +48 -7
  9. package/dist/generated/ast.js +344 -315
  10. package/dist/generated/grammar.js +2 -3177
  11. package/dist/generated/module.js +13 -18
  12. package/dist/index.js +2 -3
  13. package/dist/logger.js +39 -42
  14. package/dist/lsp/CodeLensProvider.js +28 -32
  15. package/dist/lsp/DocumentLinkProvider.js +26 -33
  16. package/dist/lsp/DocumentSymbolProvider.js +165 -167
  17. package/dist/lsp/HoverProvider.js +35 -48
  18. package/dist/lsp/SemanticTokenProvider.js +160 -201
  19. package/dist/lsp/index.js +5 -6
  20. package/dist/model/fqn-computation.js +39 -40
  21. package/dist/model/fqn-index.js +117 -141
  22. package/dist/model/index.js +5 -6
  23. package/dist/model/model-builder.d.ts +10 -5
  24. package/dist/model/model-builder.js +247 -176
  25. package/dist/model/model-locator.d.ts +1 -1
  26. package/dist/model/model-locator.js +102 -100
  27. package/dist/model/model-parser.d.ts +2 -6
  28. package/dist/model/model-parser.js +284 -286
  29. package/dist/module.d.ts +4 -1
  30. package/dist/module.js +69 -60
  31. package/dist/protocol.d.ts +16 -19
  32. package/dist/protocol.js +14 -22
  33. package/dist/references/index.js +2 -3
  34. package/dist/references/scope-computation.js +72 -69
  35. package/dist/references/scope-provider.js +126 -116
  36. package/dist/shared/WorkspaceManager.d.ts +2 -3
  37. package/dist/shared/WorkspaceManager.js +13 -16
  38. package/dist/shared/index.js +1 -2
  39. package/dist/test/index.js +1 -2
  40. package/dist/test/testServices.js +73 -61
  41. package/dist/utils.d.ts +1 -0
  42. package/dist/utils.js +4 -3
  43. package/dist/validation/element.d.ts +1 -0
  44. package/dist/validation/element.js +31 -38
  45. package/dist/validation/index.js +37 -46
  46. package/dist/validation/relation.js +46 -46
  47. package/dist/validation/specification.d.ts +1 -0
  48. package/dist/validation/specification.js +33 -30
  49. package/dist/validation/view.js +16 -22
  50. package/dist/view-utils/assignNavigateTo.d.ts +3 -0
  51. package/dist/view-utils/assignNavigateTo.js +20 -0
  52. package/dist/view-utils/index.d.ts +4 -0
  53. package/dist/view-utils/index.js +3 -0
  54. package/dist/view-utils/resolve-extended-views.d.ts +7 -0
  55. package/dist/view-utils/resolve-extended-views.js +41 -0
  56. package/dist/view-utils/resolve-relative-paths.d.ts +3 -0
  57. package/dist/view-utils/resolve-relative-paths.js +76 -0
  58. package/package.json +33 -18
  59. package/dist/registerProtocolHandlers.d.ts +0 -3
  60. 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,3 @@
1
+ import type { ElementView } from '@likec4/core';
2
+ export declare function resolveRelativePaths(views: ElementView[]): ElementView[];
3
+ //# sourceMappingURL=resolve-relative-paths.d.ts.map
@@ -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.37.1",
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
- "main": "./dist/index.js",
24
- "module": "./dist/index.js",
25
- "types": "./dist/index.d.ts",
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
- "turbo-build": "run -T turbo run build --filter='language-server'",
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
- "build": "tsc",
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.37.1",
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.24.0",
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": "^18.15.11",
58
- "@types/object-hash": "^3.0.2",
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
- "vitest": "^0.34.4"
73
+ "unbuild": "^2.0.0",
74
+ "vitest": "^0.34.6"
63
75
  },
64
- "packageManager": "yarn@3.6.3"
76
+ "packageManager": "yarn@3.6.4",
77
+ "volta": {
78
+ "extends": "../../package.json"
79
+ }
65
80
  }
@@ -1,3 +0,0 @@
1
- import type { LikeC4Services } from './module';
2
- export declare function registerProtocolHandlers(services: LikeC4Services): void;
3
- //# sourceMappingURL=registerProtocolHandlers.d.ts.map
@@ -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