@likec4/language-server 1.18.0 → 1.19.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.
Files changed (250) hide show
  1. package/dist/LikeC4FileSystem.d.ts +13 -0
  2. package/dist/LikeC4FileSystem.js +27 -0
  3. package/dist/Rpc.d.ts +9 -0
  4. package/dist/Rpc.js +132 -0
  5. package/dist/ast.d.ts +200 -0
  6. package/dist/ast.js +276 -0
  7. package/dist/browser.d.ts +7 -20
  8. package/dist/browser.js +13 -0
  9. package/dist/documentation/documentation-provider.d.ts +8 -0
  10. package/dist/documentation/documentation-provider.js +46 -0
  11. package/dist/documentation/index.d.ts +1 -0
  12. package/dist/documentation/index.js +1 -0
  13. package/dist/formatting/LikeC4Formatter.d.ts +27 -0
  14. package/dist/formatting/LikeC4Formatter.js +261 -0
  15. package/dist/formatting/utils.d.ts +6 -0
  16. package/dist/formatting/utils.js +15 -0
  17. package/dist/generated/ast.d.ts +1242 -0
  18. package/dist/generated/ast.js +1945 -0
  19. package/dist/generated/grammar.d.ts +6 -0
  20. package/dist/generated/grammar.js +3 -0
  21. package/dist/generated/module.d.ts +9 -0
  22. package/dist/generated/module.js +23 -0
  23. package/dist/generated-lib/icons.d.ts +1 -0
  24. package/dist/{likec4lib.mjs → generated-lib/icons.js} +1 -6
  25. package/dist/index.d.ts +9 -31
  26. package/dist/index.js +14 -0
  27. package/dist/like-c4.langium +845 -0
  28. package/dist/likec4lib.d.ts +4 -6
  29. package/dist/likec4lib.js +4 -0
  30. package/dist/logger.d.ts +7 -0
  31. package/dist/logger.js +73 -0
  32. package/dist/lsp/CodeLensProvider.d.ts +9 -0
  33. package/dist/lsp/CodeLensProvider.js +40 -0
  34. package/dist/lsp/CompletionProvider.d.ts +6 -0
  35. package/dist/lsp/CompletionProvider.js +135 -0
  36. package/dist/lsp/DocumentHighlightProvider.d.ts +9 -0
  37. package/dist/lsp/DocumentHighlightProvider.js +10 -0
  38. package/dist/lsp/DocumentLinkProvider.d.ts +11 -0
  39. package/dist/lsp/DocumentLinkProvider.js +49 -0
  40. package/dist/lsp/DocumentSymbolProvider.d.ts +32 -0
  41. package/dist/lsp/DocumentSymbolProvider.js +274 -0
  42. package/dist/lsp/HoverProvider.d.ts +10 -0
  43. package/dist/lsp/HoverProvider.js +69 -0
  44. package/dist/lsp/RenameProvider.d.ts +5 -0
  45. package/dist/lsp/RenameProvider.js +6 -0
  46. package/dist/lsp/SemanticTokenProvider.d.ts +7 -0
  47. package/dist/lsp/SemanticTokenProvider.js +297 -0
  48. package/dist/lsp/index.d.ts +7 -0
  49. package/dist/lsp/index.js +7 -0
  50. package/dist/model/deployments-index.d.ts +60 -0
  51. package/dist/model/deployments-index.js +181 -0
  52. package/dist/model/fqn-computation.d.ts +3 -0
  53. package/dist/model/fqn-computation.js +72 -0
  54. package/dist/model/fqn-index.d.ts +25 -0
  55. package/dist/model/fqn-index.js +96 -0
  56. package/dist/model/index.d.ts +6 -0
  57. package/dist/model/index.js +6 -0
  58. package/dist/model/model-builder.d.ts +32 -0
  59. package/dist/model/model-builder.js +598 -0
  60. package/dist/model/model-locator.d.ts +23 -0
  61. package/dist/model/model-locator.js +126 -0
  62. package/dist/model/model-parser-where.d.ts +3 -0
  63. package/dist/model/model-parser-where.js +70 -0
  64. package/dist/model/model-parser.d.ts +292 -0
  65. package/dist/model/model-parser.js +72 -0
  66. package/dist/model/parser/Base.d.ts +28 -0
  67. package/dist/model/parser/Base.js +87 -0
  68. package/dist/model/parser/DeploymentModelParser.d.ts +33 -0
  69. package/dist/model/parser/DeploymentModelParser.js +162 -0
  70. package/dist/model/parser/DeploymentViewParser.d.ts +38 -0
  71. package/dist/model/parser/DeploymentViewParser.js +98 -0
  72. package/dist/model/parser/FqnRefParser.d.ts +29 -0
  73. package/dist/model/parser/FqnRefParser.js +108 -0
  74. package/dist/model/parser/GlobalsParser.d.ts +66 -0
  75. package/dist/model/parser/GlobalsParser.js +80 -0
  76. package/dist/model/parser/ModelParser.d.ts +27 -0
  77. package/dist/model/parser/ModelParser.js +122 -0
  78. package/dist/model/parser/PredicatesParser.d.ts +34 -0
  79. package/dist/model/parser/PredicatesParser.js +272 -0
  80. package/dist/model/parser/SpecificationParser.d.ts +27 -0
  81. package/dist/model/parser/SpecificationParser.js +120 -0
  82. package/dist/model/parser/ViewsParser.d.ts +64 -0
  83. package/dist/model/parser/ViewsParser.js +377 -0
  84. package/dist/model-change/ModelChanges.d.ts +15 -0
  85. package/dist/model-change/ModelChanges.js +89 -0
  86. package/dist/model-change/changeElementStyle.d.ts +16 -0
  87. package/dist/model-change/changeElementStyle.js +136 -0
  88. package/dist/model-change/changeViewLayout.d.ts +12 -0
  89. package/dist/model-change/changeViewLayout.js +32 -0
  90. package/dist/model-change/saveManualLayout.d.ts +11 -0
  91. package/dist/model-change/saveManualLayout.js +27 -0
  92. package/dist/module.d.ts +70 -0
  93. package/dist/module.js +162 -0
  94. package/dist/protocol.d.ts +38 -23
  95. package/dist/protocol.js +15 -0
  96. package/dist/references/index.d.ts +3 -0
  97. package/dist/references/index.js +3 -0
  98. package/dist/references/name-provider.d.ts +9 -0
  99. package/dist/references/name-provider.js +33 -0
  100. package/dist/references/scope-computation.d.ts +20 -0
  101. package/dist/references/scope-computation.js +281 -0
  102. package/dist/references/scope-provider.d.ts +16 -0
  103. package/dist/references/scope-provider.js +165 -0
  104. package/dist/shared/NodeKindProvider.d.ts +15 -0
  105. package/dist/shared/NodeKindProvider.js +108 -0
  106. package/dist/shared/WorkspaceManager.d.ts +18 -0
  107. package/dist/shared/WorkspaceManager.js +36 -0
  108. package/dist/shared/WorkspaceSymbolProvider.d.ts +3 -0
  109. package/dist/shared/WorkspaceSymbolProvider.js +3 -0
  110. package/dist/shared/index.d.ts +3 -0
  111. package/dist/shared/index.js +3 -0
  112. package/dist/test/index.d.ts +1 -0
  113. package/dist/test/index.js +1 -0
  114. package/dist/test/setup.d.ts +1 -0
  115. package/dist/test/setup.js +7 -0
  116. package/dist/test/testServices.d.ts +22 -0
  117. package/dist/test/testServices.js +119 -0
  118. package/dist/utils/elementRef.d.ts +11 -0
  119. package/dist/utils/elementRef.js +15 -0
  120. package/dist/utils/fqnRef.d.ts +8 -0
  121. package/dist/utils/fqnRef.js +46 -0
  122. package/dist/utils/index.d.ts +1 -0
  123. package/dist/utils/index.js +1 -0
  124. package/dist/utils/printDocs.d.ts +2 -0
  125. package/dist/utils/printDocs.js +1 -0
  126. package/dist/utils/stringHash.d.ts +1 -0
  127. package/dist/utils/stringHash.js +5 -0
  128. package/dist/validation/_shared.d.ts +3 -0
  129. package/dist/validation/_shared.js +22 -0
  130. package/dist/validation/deployment-checks.d.ts +6 -0
  131. package/dist/validation/deployment-checks.js +114 -0
  132. package/dist/validation/dynamic-view-rule.d.ts +4 -0
  133. package/dist/validation/dynamic-view-rule.js +17 -0
  134. package/dist/validation/dynamic-view-step.d.ts +4 -0
  135. package/dist/validation/dynamic-view-step.js +29 -0
  136. package/dist/validation/element.d.ts +4 -0
  137. package/dist/validation/element.js +49 -0
  138. package/dist/validation/index.d.ts +15 -0
  139. package/dist/validation/index.js +152 -0
  140. package/dist/validation/property-checks.d.ts +6 -0
  141. package/dist/validation/property-checks.js +39 -0
  142. package/dist/validation/relation.d.ts +5 -0
  143. package/dist/validation/relation.js +56 -0
  144. package/dist/validation/specification.d.ts +11 -0
  145. package/dist/validation/specification.js +136 -0
  146. package/dist/validation/view-predicates/element-with.d.ts +4 -0
  147. package/dist/validation/view-predicates/element-with.js +31 -0
  148. package/dist/validation/view-predicates/expanded-element.d.ts +4 -0
  149. package/dist/validation/view-predicates/expanded-element.js +12 -0
  150. package/dist/validation/view-predicates/expression-v2.d.ts +5 -0
  151. package/dist/validation/view-predicates/expression-v2.js +83 -0
  152. package/dist/validation/view-predicates/incoming.d.ts +4 -0
  153. package/dist/validation/view-predicates/incoming.js +16 -0
  154. package/dist/validation/view-predicates/index.d.ts +6 -0
  155. package/dist/validation/view-predicates/index.js +6 -0
  156. package/dist/validation/view-predicates/outgoing.d.ts +4 -0
  157. package/dist/validation/view-predicates/outgoing.js +16 -0
  158. package/dist/validation/view-predicates/relation-with.d.ts +4 -0
  159. package/dist/validation/view-predicates/relation-with.js +13 -0
  160. package/dist/validation/view.d.ts +4 -0
  161. package/dist/validation/view.js +23 -0
  162. package/dist/view-utils/assignNavigateTo.d.ts +2 -0
  163. package/dist/view-utils/assignNavigateTo.js +25 -0
  164. package/dist/view-utils/index.d.ts +2 -0
  165. package/dist/view-utils/index.js +2 -0
  166. package/dist/view-utils/manual-layout.d.ts +7 -0
  167. package/dist/view-utils/manual-layout.js +99 -0
  168. package/dist/view-utils/resolve-relative-paths.d.ts +2 -0
  169. package/dist/view-utils/resolve-relative-paths.js +78 -0
  170. package/dist/views/configurable-layouter.d.ts +7 -0
  171. package/dist/views/configurable-layouter.js +55 -0
  172. package/dist/views/index.d.ts +1 -0
  173. package/dist/views/index.js +1 -0
  174. package/dist/views/likec4-views.d.ts +26 -0
  175. package/dist/views/likec4-views.js +113 -0
  176. package/package.json +40 -54
  177. package/src/LikeC4FileSystem.ts +22 -21
  178. package/src/Rpc.ts +13 -7
  179. package/src/ast.ts +44 -133
  180. package/src/browser.ts +11 -11
  181. package/src/documentation/documentation-provider.ts +52 -0
  182. package/src/documentation/index.ts +1 -0
  183. package/src/generated/ast.ts +177 -177
  184. package/src/generated/grammar.ts +1 -1
  185. package/src/index.ts +13 -9
  186. package/src/like-c4.langium +37 -34
  187. package/src/logger.ts +34 -55
  188. package/src/lsp/CompletionProvider.ts +4 -4
  189. package/src/lsp/DocumentSymbolProvider.ts +110 -28
  190. package/src/lsp/HoverProvider.ts +5 -3
  191. package/src/lsp/SemanticTokenProvider.ts +2 -2
  192. package/src/model/deployments-index.ts +18 -14
  193. package/src/model/fqn-computation.ts +8 -8
  194. package/src/model/model-builder.ts +62 -60
  195. package/src/model/model-parser.ts +62 -1574
  196. package/src/model/parser/Base.ts +107 -0
  197. package/src/model/parser/DeploymentModelParser.ts +192 -0
  198. package/src/model/parser/DeploymentViewParser.ts +116 -0
  199. package/src/model/parser/FqnRefParser.ts +118 -0
  200. package/src/model/parser/GlobalsParser.ts +96 -0
  201. package/src/model/parser/ModelParser.ts +141 -0
  202. package/src/model/parser/PredicatesParser.ts +291 -0
  203. package/src/model/parser/SpecificationParser.ts +133 -0
  204. package/src/model/parser/ViewsParser.ts +428 -0
  205. package/src/module.ts +71 -25
  206. package/src/protocol.ts +29 -4
  207. package/src/references/scope-computation.ts +35 -35
  208. package/src/references/scope-provider.ts +13 -7
  209. package/src/utils/{deploymentRef.ts → fqnRef.ts} +27 -2
  210. package/src/validation/_shared.ts +0 -1
  211. package/src/validation/deployment-checks.ts +49 -62
  212. package/src/validation/dynamic-view-rule.ts +5 -4
  213. package/src/validation/dynamic-view-step.ts +23 -26
  214. package/src/validation/index.ts +100 -9
  215. package/src/validation/property-checks.ts +11 -10
  216. package/src/validation/specification.ts +38 -38
  217. package/src/validation/view-predicates/element-with.ts +6 -5
  218. package/src/validation/view-predicates/expanded-element.ts +6 -5
  219. package/src/validation/view-predicates/expression-v2.ts +101 -0
  220. package/src/validation/view-predicates/incoming.ts +6 -5
  221. package/src/validation/view-predicates/index.ts +1 -1
  222. package/src/validation/view-predicates/outgoing.ts +6 -5
  223. package/src/validation/view-predicates/relation-with.ts +6 -5
  224. package/src/validation/view.ts +5 -5
  225. package/src/view-utils/assignNavigateTo.ts +1 -1
  226. package/src/view-utils/manual-layout.ts +25 -0
  227. package/src/views/configurable-layouter.ts +65 -0
  228. package/src/views/index.ts +1 -0
  229. package/src/views/likec4-views.ts +139 -0
  230. package/dist/browser.cjs +0 -25
  231. package/dist/browser.d.cts +0 -23
  232. package/dist/browser.d.mts +0 -23
  233. package/dist/browser.mjs +0 -20
  234. package/dist/index.cjs +0 -53
  235. package/dist/index.d.cts +0 -34
  236. package/dist/index.d.mts +0 -34
  237. package/dist/index.mjs +0 -46
  238. package/dist/likec4lib.cjs +0 -1546
  239. package/dist/likec4lib.d.cts +0 -6
  240. package/dist/likec4lib.d.mts +0 -6
  241. package/dist/protocol.cjs +0 -25
  242. package/dist/protocol.d.cts +0 -48
  243. package/dist/protocol.d.mts +0 -48
  244. package/dist/protocol.mjs +0 -17
  245. package/dist/shared/language-server.CO_nmHiL.cjs +0 -7689
  246. package/dist/shared/language-server.Da6ey08o.d.cts +0 -1619
  247. package/dist/shared/language-server.De7S3e5Z.d.ts +0 -1619
  248. package/dist/shared/language-server.Dj4iDjtB.d.mts +0 -1619
  249. package/dist/shared/language-server.oO_9JoAG.mjs +0 -7666
  250. package/src/validation/view-predicates/deployments.ts +0 -56
@@ -0,0 +1,2 @@
1
+ import { ComputedView } from '@likec4/core';
2
+ export declare function assignNavigateTo<R extends Iterable<ComputedView>>(views: R): R;
@@ -0,0 +1,25 @@
1
+ import { ComputedNode, ComputedView } from "@likec4/core";
2
+ import { find, isNullish } from "remeda";
3
+ export function assignNavigateTo(views) {
4
+ const allElementViews = /* @__PURE__ */ new Map();
5
+ for (const v of views) {
6
+ if (ComputedView.isElement(v) && v.viewOf && isNullish(v.extends)) {
7
+ const viewsOf = allElementViews.get(v.viewOf) ?? [];
8
+ viewsOf.push(v.id);
9
+ allElementViews.set(v.viewOf, viewsOf);
10
+ }
11
+ }
12
+ for (const { id, nodes } of views) {
13
+ for (const node of nodes) {
14
+ const modelRef = ComputedNode.modelRef(node);
15
+ if (node.navigateTo || !modelRef) {
16
+ continue;
17
+ }
18
+ const navigateTo = find(allElementViews.get(modelRef) ?? [], (v) => v !== id);
19
+ if (navigateTo) {
20
+ node.navigateTo = navigateTo;
21
+ }
22
+ }
23
+ }
24
+ return views;
25
+ }
@@ -0,0 +1,2 @@
1
+ export * from './assignNavigateTo';
2
+ export * from './resolve-relative-paths';
@@ -0,0 +1,2 @@
1
+ export * from "./assignNavigateTo.js";
2
+ export * from "./resolve-relative-paths.js";
@@ -0,0 +1,7 @@
1
+ import type * as c4 from '@likec4/core';
2
+ import { type ViewManualLayout } from '@likec4/core';
3
+ import type { ast } from '../ast';
4
+ export declare function serializeToComment(layout: ViewManualLayout): string;
5
+ export declare function hasManualLayout(comment: string): boolean;
6
+ export declare function deserializeFromComment(comment: string): ViewManualLayout;
7
+ export declare function parseViewManualLayout(node: ast.LikeC4View): c4.ViewManualLayout | undefined;
@@ -0,0 +1,99 @@
1
+ import { isAutoLayoutDirection } from "@likec4/core";
2
+ import { decode, encode } from "@msgpack/msgpack";
3
+ import { fromBase64, toBase64 } from "@smithy/util-base64";
4
+ import { AstUtils, CstUtils } from "langium";
5
+ import { mapValues } from "remeda";
6
+ import { logger } from "../logger.js";
7
+ const { getDocument } = AstUtils;
8
+ function pack({
9
+ nodes,
10
+ edges,
11
+ ...rest
12
+ }) {
13
+ return {
14
+ ...rest,
15
+ nodes: mapValues(nodes, ({ x, y, width, height, isCompound, ...n }) => ({
16
+ ...n,
17
+ b: [x, y, width, height],
18
+ c: isCompound
19
+ })),
20
+ edges: mapValues(edges, ({ points, controlPoints, labelBBox, dotpos, ...e }) => ({
21
+ ...!!controlPoints && { cp: controlPoints },
22
+ ...!!labelBBox && { l: labelBBox },
23
+ ...!!dotpos && { dp: dotpos },
24
+ ...e,
25
+ p: points
26
+ }))
27
+ };
28
+ }
29
+ function unpack({
30
+ nodes,
31
+ edges,
32
+ autoLayout,
33
+ ...rest
34
+ }) {
35
+ return {
36
+ ...rest,
37
+ /// Try to parse the old format for backward compatibility
38
+ autoLayout: isAutoLayoutDirection(autoLayout) ? { direction: autoLayout } : autoLayout,
39
+ nodes: mapValues(nodes, ({ b, c, ...n }) => ({
40
+ x: b[0],
41
+ y: b[1],
42
+ width: b[2],
43
+ height: b[3],
44
+ isCompound: c,
45
+ ...n
46
+ })),
47
+ edges: mapValues(edges, ({ p, cp, l, dp, ...e }) => ({
48
+ ...!!cp && { controlPoints: cp },
49
+ ...!!l && { labelBBox: l },
50
+ ...!!dp && { dotpos: dp },
51
+ ...e,
52
+ points: p
53
+ }))
54
+ };
55
+ }
56
+ const MAX_LINE_LENGTH = 500;
57
+ export function serializeToComment(layout) {
58
+ const bytes = encode(pack(layout));
59
+ const base64 = toBase64(bytes);
60
+ const lines = [];
61
+ let offset = 0;
62
+ while (offset < base64.length) {
63
+ lines.push(" * " + base64.slice(offset, Math.min(offset + MAX_LINE_LENGTH, base64.length)));
64
+ offset += MAX_LINE_LENGTH;
65
+ }
66
+ lines.unshift(
67
+ "/**",
68
+ " * @likec4-generated(v1)"
69
+ );
70
+ lines.push(" */");
71
+ return lines.join("\n");
72
+ }
73
+ export function hasManualLayout(comment) {
74
+ return comment.includes("@likec4-generated");
75
+ }
76
+ export function deserializeFromComment(comment) {
77
+ if (!hasManualLayout(comment)) {
78
+ throw new Error(`Not a likec4-generated comment: ${comment}`);
79
+ }
80
+ const b64 = comment.trim().split("\n").filter((l) => !l.includes("**") && !l.includes("@likec4-") && !l.includes("*/")).map((l) => l.replaceAll("*", "").trim()).join("");
81
+ const decodedb64 = fromBase64(b64);
82
+ return unpack(decode(decodedb64));
83
+ }
84
+ export function parseViewManualLayout(node) {
85
+ const commentNode = CstUtils.findCommentNode(node.$cstNode, ["BLOCK_COMMENT"]);
86
+ if (!commentNode || !hasManualLayout(commentNode.text)) {
87
+ return void 0;
88
+ }
89
+ try {
90
+ return deserializeFromComment(commentNode.text);
91
+ } catch (e) {
92
+ const doc = getDocument(node);
93
+ logger.warn(e);
94
+ logger.warn(
95
+ `Ignoring manual layout of "${node.name ?? "unnamed"}" at ${doc.uri.fsPath}:${1 + (commentNode.range.start.line || 0)}`
96
+ );
97
+ return void 0;
98
+ }
99
+ }
@@ -0,0 +1,2 @@
1
+ import type { LikeC4View } from '@likec4/core';
2
+ export declare function resolveRelativePaths(views: LikeC4View[]): LikeC4View[];
@@ -0,0 +1,78 @@
1
+ import { compareNatural, invariant } from "@likec4/core";
2
+ import { filter, hasAtLeast, isTruthy, map, pipe, unique } from "remeda";
3
+ import { parsePath } from "ufo";
4
+ function commonAncestorPath(views, sep = "/") {
5
+ const uniqURIs = pipe(
6
+ views,
7
+ map((v) => v.docUri),
8
+ filter(isTruthy),
9
+ unique()
10
+ );
11
+ if (uniqURIs.length === 0) return "";
12
+ if (uniqURIs.length === 1) {
13
+ const parts2 = parsePath(uniqURIs[0]).pathname.split(sep);
14
+ if (parts2.length <= 1) return sep;
15
+ parts2.pop();
16
+ return parts2.join(sep) + sep;
17
+ }
18
+ invariant(hasAtLeast(uniqURIs, 2), "Expected at least 2 unique URIs");
19
+ const [baseUri, ...tail] = uniqURIs;
20
+ const parts = parsePath(baseUri).pathname.split(sep);
21
+ let endOfPrefix = parts.length;
22
+ for (const uri of tail) {
23
+ if (uri === baseUri) {
24
+ continue;
25
+ }
26
+ const compare = parsePath(uri).pathname.split(sep);
27
+ for (let i = 0; i < endOfPrefix; i++) {
28
+ if (compare[i] !== parts[i]) {
29
+ endOfPrefix = i;
30
+ }
31
+ }
32
+ if (endOfPrefix === 0) return "";
33
+ }
34
+ const prefix = parts.slice(0, endOfPrefix).join(sep);
35
+ return prefix.endsWith(sep) ? prefix : prefix + sep;
36
+ }
37
+ export function resolveRelativePaths(views) {
38
+ const sep = "/";
39
+ const commonPrefix = commonAncestorPath(views, sep);
40
+ return views.map((view) => {
41
+ if (!view.docUri) {
42
+ return {
43
+ view,
44
+ parts: []
45
+ };
46
+ }
47
+ let path = parsePath(view.docUri).pathname;
48
+ if (commonPrefix.length > 0) {
49
+ invariant(
50
+ path.startsWith(commonPrefix),
51
+ `Expect path "${path}" to start with common prefix: "${commonPrefix}"`
52
+ );
53
+ path = path.slice(commonPrefix.length);
54
+ } else {
55
+ path = path.includes(sep) ? path.slice(path.lastIndexOf(sep) + 1) : path;
56
+ }
57
+ return {
58
+ view,
59
+ parts: path.split(sep)
60
+ };
61
+ }).sort((a, b) => {
62
+ if (a.parts.length !== b.parts.length) {
63
+ return a.parts.length - b.parts.length;
64
+ }
65
+ for (let i = 0; i < a.parts.length; i++) {
66
+ const compare = compareNatural(a.parts[i], b.parts[i]);
67
+ if (compare !== 0) {
68
+ return compare;
69
+ }
70
+ }
71
+ return compareNatural(a.view.title ?? a.view.id, b.view.title ?? b.view.id);
72
+ }).map(({ parts, view }) => {
73
+ return {
74
+ ...view,
75
+ relativePath: parts.join(sep)
76
+ };
77
+ });
78
+ }
@@ -0,0 +1,7 @@
1
+ import { GraphvizLayouter } from '@likec4/layouts';
2
+ import type { LikeC4Services } from '../module';
3
+ export declare const ConfigurableLayouter: {
4
+ likec4: {
5
+ Layouter(services: LikeC4Services): GraphvizLayouter;
6
+ };
7
+ };
@@ -0,0 +1,55 @@
1
+ import { GraphvizLayouter, GraphvizWasmAdapter } from "@likec4/layouts";
2
+ import { GraphvizBinaryAdapter } from "@likec4/layouts/graphviz/binary";
3
+ import { isEmpty } from "remeda";
4
+ import which from "which";
5
+ import { logger } from "../logger.js";
6
+ function graphvizBinPath() {
7
+ try {
8
+ return which.sync("dot");
9
+ } catch (error) {
10
+ logger.error("Error checking for native Graphviz:", error);
11
+ return null;
12
+ }
13
+ }
14
+ export const ConfigurableLayouter = {
15
+ likec4: {
16
+ Layouter(services) {
17
+ logger.debug("Creating ConfigurableLayouter");
18
+ const wasmAdapter = new GraphvizWasmAdapter();
19
+ const layouter = new GraphvizLayouter(wasmAdapter);
20
+ const langId = services.LanguageMetaData.languageId;
21
+ services.shared.workspace.ConfigurationProvider.onConfigurationSectionUpdate((update) => {
22
+ logger.debug("Configuration update", update);
23
+ if (update.section === langId) {
24
+ try {
25
+ const { mode, path } = update.configuration.graphviz ?? {
26
+ mode: "wasm",
27
+ path: ""
28
+ };
29
+ if (mode === "wasm") {
30
+ layouter.changePort(wasmAdapter);
31
+ logger.info("use graphviz wasm");
32
+ return;
33
+ }
34
+ let binaryPath = isEmpty(path) ? graphvizBinPath() : path;
35
+ if (binaryPath === null) {
36
+ layouter.changePort(wasmAdapter);
37
+ logger.warn(`No Graphviz binaries found on PATH, use graphviz wasm`);
38
+ services.shared.lsp.Connection?.window.showWarningMessage(
39
+ "No Graphviz binaries found on PATH, set path to binaries in settings."
40
+ );
41
+ return;
42
+ }
43
+ layouter.changePort(new GraphvizBinaryAdapter(binaryPath));
44
+ logger.info(`use graphviz binary: ${binaryPath}`);
45
+ } catch (error) {
46
+ logger.error("Failed to update configuration", error);
47
+ }
48
+ return;
49
+ }
50
+ logger.warn("Unexpected configuration update", update);
51
+ });
52
+ return layouter;
53
+ }
54
+ }
55
+ };
@@ -0,0 +1 @@
1
+ export { LikeC4Views } from './likec4-views';
@@ -0,0 +1 @@
1
+ export { LikeC4Views } from "./likec4-views.js";
@@ -0,0 +1,26 @@
1
+ import type { ComputedView, DiagramView, OverviewGraph, ViewId } from '@likec4/core';
2
+ import { type Cancellation } from 'langium';
3
+ import type { LikeC4Services } from '../module';
4
+ export type GraphvizOut = {
5
+ dot: string;
6
+ diagram: DiagramView;
7
+ };
8
+ type GraphvizSvgOut = {
9
+ id: ViewId;
10
+ dot: string;
11
+ svg: string;
12
+ };
13
+ export declare class LikeC4Views {
14
+ private services;
15
+ private cache;
16
+ private viewsWithReportedErrors;
17
+ constructor(services: LikeC4Services);
18
+ private get layouter();
19
+ computedViews(cancelToken?: Cancellation.CancellationToken): Promise<ComputedView[]>;
20
+ layoutAllViews(cancelToken?: Cancellation.CancellationToken): Promise<Array<Readonly<GraphvizOut>>>;
21
+ layoutView(viewId: ViewId, cancelToken?: Cancellation.CancellationToken): Promise<GraphvizOut | null>;
22
+ diagrams(): Promise<Array<DiagramView>>;
23
+ viewsAsGraphvizOut(): Promise<Array<GraphvizSvgOut>>;
24
+ overviewGraph(): Promise<OverviewGraph>;
25
+ }
26
+ export {};
@@ -0,0 +1,113 @@
1
+ import { values } from "remeda";
2
+ import { logger, logWarnError } from "../logger.js";
3
+ export class LikeC4Views {
4
+ constructor(services) {
5
+ this.services = services;
6
+ }
7
+ cache = /* @__PURE__ */ new WeakMap();
8
+ viewsWithReportedErrors = /* @__PURE__ */ new Set();
9
+ get layouter() {
10
+ return this.services.likec4.Layouter;
11
+ }
12
+ async computedViews(cancelToken) {
13
+ const model = await this.services.likec4.ModelBuilder.buildComputedModel(cancelToken);
14
+ return model ? values(model.views) : [];
15
+ }
16
+ async layoutAllViews(cancelToken) {
17
+ const views = await this.computedViews(cancelToken);
18
+ if (views.length === 0) {
19
+ return [];
20
+ }
21
+ const results = [];
22
+ const tasks = [];
23
+ for (const view of views) {
24
+ this.viewsWithReportedErrors.delete(view.id);
25
+ tasks.push(
26
+ this.layouter.layout(view).then((result) => {
27
+ this.cache.set(view, result);
28
+ return result;
29
+ }).catch((e) => {
30
+ this.cache.delete(view);
31
+ logWarnError(e);
32
+ return Promise.reject(e);
33
+ })
34
+ );
35
+ }
36
+ for (const task of await Promise.allSettled(tasks)) {
37
+ if (task.status === "fulfilled") {
38
+ results.push(task.value);
39
+ }
40
+ }
41
+ return results;
42
+ }
43
+ async layoutView(viewId, cancelToken) {
44
+ const model = await this.services.likec4.ModelBuilder.buildComputedModel(cancelToken);
45
+ if (!model) {
46
+ return null;
47
+ }
48
+ const view = model.views[viewId];
49
+ if (!view) {
50
+ return null;
51
+ }
52
+ let cached = this.cache.get(view);
53
+ if (cached) {
54
+ return cached;
55
+ }
56
+ try {
57
+ const result = await this.layouter.layout(view);
58
+ this.viewsWithReportedErrors.delete(viewId);
59
+ this.cache.set(view, result);
60
+ return result;
61
+ } catch (e) {
62
+ if (!this.viewsWithReportedErrors.has(viewId)) {
63
+ const errMessage = e instanceof Error ? e.message : "" + e;
64
+ this.services.shared.lsp.Connection?.window.showErrorMessage(`LikeC4: ${errMessage}`);
65
+ this.viewsWithReportedErrors.add(viewId);
66
+ }
67
+ logger.error(e);
68
+ return Promise.reject(e);
69
+ }
70
+ }
71
+ async diagrams() {
72
+ const layouted = await this.layoutAllViews();
73
+ return layouted.map((l) => l.diagram);
74
+ }
75
+ async viewsAsGraphvizOut() {
76
+ const KEY = "All-LayoutedViews-DotWithSvg";
77
+ const cache = this.services.WorkspaceCache;
78
+ if (cache.has(KEY)) {
79
+ return await Promise.resolve(cache.get(KEY));
80
+ }
81
+ const views = await this.computedViews();
82
+ const tasks = views.map(async (view) => {
83
+ const { dot, svg } = await this.layouter.svg(view);
84
+ return {
85
+ id: view.id,
86
+ dot,
87
+ svg
88
+ };
89
+ });
90
+ const succeed = [];
91
+ const settledResult = await Promise.allSettled(tasks);
92
+ for (const result of settledResult) {
93
+ if (result.status === "fulfilled") {
94
+ succeed.push(result.value);
95
+ } else {
96
+ logWarnError(result.reason);
97
+ }
98
+ }
99
+ cache.set(KEY, succeed);
100
+ return succeed;
101
+ }
102
+ async overviewGraph() {
103
+ const KEY = "OverviewGraph";
104
+ const cache = this.services.WorkspaceCache;
105
+ if (cache.has(KEY)) {
106
+ return await Promise.resolve(cache.get(KEY));
107
+ }
108
+ const views = await this.computedViews();
109
+ const overviewGraph = await this.layouter.layoutOverviewGraph(views);
110
+ cache.set(KEY, overviewGraph);
111
+ return overviewGraph;
112
+ }
113
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "1.18.0",
4
+ "version": "1.19.1",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -24,65 +24,46 @@
24
24
  "sideEffects": false,
25
25
  "exports": {
26
26
  ".": {
27
- "browser": {
28
- "types": "./dist/browser.d.ts",
29
- "import": "./dist/browser.mjs",
30
- "require": "./dist/browser.cjs"
27
+ "sources": "./src/index.ts",
28
+ "node": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "default": "./dist/index.js"
31
32
  },
32
33
  "default": {
33
- "types": "./dist/index.d.ts",
34
- "import": "./dist/index.mjs",
35
- "require": "./dist/index.cjs"
34
+ "types": "./dist/browser.d.ts",
35
+ "import": "./dist/browser.js",
36
+ "default": "./dist/browser.js"
36
37
  }
37
38
  },
38
39
  "./likec4lib": {
39
- "types": "./dist/likec4lib.d.ts",
40
- "import": "./dist/likec4lib.mjs",
41
- "require": "./dist/likec4lib.cjs"
40
+ "sources": "./src/likec4lib.ts",
41
+ "default": {
42
+ "types": "./dist/likec4lib.d.ts",
43
+ "import": "./dist/likec4lib.js",
44
+ "default": "./dist/likec4lib.js"
45
+ }
42
46
  },
43
47
  "./browser": {
44
- "types": "./dist/browser.d.ts",
45
- "import": "./dist/browser.mjs",
46
- "require": "./dist/browser.cjs"
48
+ "sources": "./src/browser.ts",
49
+ "default": {
50
+ "types": "./dist/browser.d.ts",
51
+ "import": "./dist/browser.js",
52
+ "default": "./dist/browser.js"
53
+ }
47
54
  },
48
55
  "./protocol": {
49
- "types": "./dist/protocol.d.ts",
50
- "import": "./dist/protocol.mjs",
51
- "require": "./dist/protocol.cjs"
56
+ "sources": "./src/protocol.ts",
57
+ "default": {
58
+ "types": "./dist/protocol.d.ts",
59
+ "import": "./dist/protocol.js",
60
+ "default": "./dist/protocol.js"
61
+ }
52
62
  }
53
63
  },
54
64
  "publishConfig": {
55
65
  "registry": "https://registry.npmjs.org",
56
- "access": "public",
57
- "exports": {
58
- ".": {
59
- "browser": {
60
- "types": "./dist/browser.d.ts",
61
- "import": "./dist/browser.mjs",
62
- "require": "./dist/browser.cjs"
63
- },
64
- "default": {
65
- "types": "./dist/index.d.ts",
66
- "import": "./dist/index.mjs",
67
- "require": "./dist/index.cjs"
68
- }
69
- },
70
- "./likec4lib": {
71
- "types": "./dist/likec4lib.d.ts",
72
- "import": "./dist/likec4lib.mjs",
73
- "require": "./dist/likec4lib.cjs"
74
- },
75
- "./browser": {
76
- "types": "./dist/browser.d.ts",
77
- "import": "./dist/browser.mjs",
78
- "require": "./dist/browser.cjs"
79
- },
80
- "./protocol": {
81
- "types": "./dist/protocol.d.ts",
82
- "import": "./dist/protocol.mjs",
83
- "require": "./dist/protocol.cjs"
84
- }
85
- }
66
+ "access": "public"
86
67
  },
87
68
  "scripts": {
88
69
  "typecheck": "tsc --noEmit",
@@ -101,12 +82,14 @@
101
82
  "test:watch": "vitest"
102
83
  },
103
84
  "dependencies": {
104
- "@likec4/core": "1.18.0",
105
- "@likec4/log": "1.18.0",
85
+ "@likec4/core": "1.19.1",
86
+ "@likec4/layouts": "1.19.1",
87
+ "@likec4/log": "1.19.1",
106
88
  "@msgpack/msgpack": "^3.0.0-beta2",
107
89
  "@smithy/util-base64": "^3.0.0",
108
90
  "esm-env": "^1.2.1",
109
91
  "fast-equals": "^5.0.1",
92
+ "fdir": "^6.4.2",
110
93
  "indent-string": "^5.0.0",
111
94
  "json5": "^2.2.3",
112
95
  "langium": "3.3.0",
@@ -118,20 +101,23 @@
118
101
  "vscode-jsonrpc": "8.2.0",
119
102
  "vscode-languageserver": "9.0.1",
120
103
  "vscode-languageserver-types": "3.17.5",
121
- "vscode-uri": "3.0.8"
104
+ "vscode-uri": "3.0.8",
105
+ "which": "^4.0.0"
122
106
  },
123
107
  "devDependencies": {
124
- "@likec4/icons": "1.18.0",
125
- "@likec4/tsconfig": "1.18.0",
108
+ "@likec4/icons": "1.19.1",
109
+ "@likec4/tsconfig": "1.19.1",
126
110
  "@types/node": "^20.17.7",
111
+ "@types/which": "^3.0.4",
127
112
  "@vitest/coverage-v8": "^2.1.8",
128
113
  "execa": "^9.3.1",
129
114
  "langium-cli": "3.3.0",
115
+ "natural-compare-lite": "^1.4.0",
130
116
  "npm-run-all2": "^7.0.1",
131
- "tsx": "~4.9.3",
117
+ "tsx": "~4.19.2",
132
118
  "turbo": "^2.3.3",
133
119
  "typescript": "^5.7.2",
134
- "unbuild": "^3.0.0-rc.11",
120
+ "unbuild": "^3.1.0",
135
121
  "vitest": "^2.1.8"
136
122
  },
137
123
  "packageManager": "yarn@4.5.3"
@@ -1,11 +1,11 @@
1
- import { type FileSystemNode, URI, UriUtils } from 'langium'
1
+ import { fdir } from 'fdir'
2
+ import { type FileSystemNode, URI } from 'langium'
2
3
  import { NodeFileSystemProvider } from 'langium/node'
3
- import { lstatSync, readlinkSync } from 'node:fs'
4
- import { readdir } from 'node:fs/promises'
5
- import { dirname, resolve } from 'node:path'
4
+ import { stat } from 'node:fs/promises'
5
+ import { logger } from './logger'
6
6
 
7
7
  export const LikeC4FileSystem = {
8
- fileSystemProvider: () => new SymLinkTraversingFileSystemProvider()
8
+ fileSystemProvider: () => new SymLinkTraversingFileSystemProvider(),
9
9
  }
10
10
 
11
11
  /**
@@ -14,23 +14,24 @@ export const LikeC4FileSystem = {
14
14
  */
15
15
  class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
16
16
  override async readDirectory(folderPath: URI): Promise<FileSystemNode[]> {
17
- const dirents = await readdir(folderPath.fsPath, { withFileTypes: true })
18
- return dirents.map(dirent => (this.followUri(UriUtils.joinPath(folderPath, dirent.name))))
19
- }
20
-
21
- followUri(uri: URI): FileSystemNode {
22
- const directoryPath = dirname(uri.fsPath)
23
- const stat = lstatSync(uri.fsPath)
24
- if (stat.isSymbolicLink()) {
25
- const resolved_link = readlinkSync(uri.fsPath)
26
- const linked_path = resolve(directoryPath, resolved_link)
27
- return this.followUri(URI.file(linked_path))
28
- } else {
29
- return {
30
- isFile: stat.isFile(),
31
- isDirectory: stat.isDirectory(),
32
- uri
17
+ const crawled = await new fdir()
18
+ .withSymlinks()
19
+ .withFullPaths()
20
+ .crawl(folderPath.fsPath)
21
+ .withPromise()
22
+ const entries = [] as FileSystemNode[]
23
+ for (const path of crawled) {
24
+ try {
25
+ const stats = await stat(path)
26
+ entries.push({
27
+ isFile: stats.isFile(),
28
+ isDirectory: stats.isDirectory(),
29
+ uri: URI.file(path),
30
+ })
31
+ } catch (error) {
32
+ logger.error(error)
33
33
  }
34
34
  }
35
+ return entries
35
36
  }
36
37
  }