@likec4/language-server 1.42.1 → 1.44.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 (303) hide show
  1. package/browser/package.json +1 -1
  2. package/browser-worker/package.json +1 -1
  3. package/dist/LikeC4LanguageServices.d.ts +12 -19
  4. package/dist/LikeC4LanguageServices.js +182 -0
  5. package/dist/Rpc.js +245 -0
  6. package/dist/ast.d.ts +10 -6
  7. package/dist/ast.js +253 -0
  8. package/dist/browser-worker.js +4 -0
  9. package/dist/browser.js +35 -0
  10. package/dist/bundled.js +42 -0
  11. package/dist/bundled.mjs +3830 -4172
  12. package/dist/documentation/documentation-provider.js +51 -0
  13. package/dist/documentation/index.js +1 -0
  14. package/dist/empty.js +2 -0
  15. package/dist/filesystem/ChokidarWatcher.js +97 -0
  16. package/dist/filesystem/FileSystemWatcher.js +14 -0
  17. package/dist/filesystem/LikeC4FileSystem.d.ts +1 -2
  18. package/dist/filesystem/LikeC4FileSystem.js +126 -0
  19. package/dist/filesystem/index.d.ts +26 -0
  20. package/dist/filesystem/index.js +29 -0
  21. package/dist/formatting/LikeC4Formatter.js +637 -0
  22. package/dist/formatting/utils.js +18 -0
  23. package/dist/generated/ast.d.ts +19 -3
  24. package/dist/generated/ast.js +2155 -0
  25. package/dist/generated/grammar.js +7 -0
  26. package/dist/generated/module.d.ts +6 -1
  27. package/dist/generated/module.js +27 -0
  28. package/dist/generated-lib/{icons.mjs → icons.js} +11 -7
  29. package/dist/index.d.ts +7 -0
  30. package/dist/index.js +53 -0
  31. package/dist/{likec4lib.mjs → likec4lib.js} +3 -3
  32. package/dist/logger.js +81 -0
  33. package/dist/lsp/CodeActionProvider.d.ts +14 -0
  34. package/dist/lsp/CodeActionProvider.js +33 -0
  35. package/dist/lsp/CodeLensProvider.js +44 -0
  36. package/dist/lsp/CompletionProvider.d.ts +3 -1
  37. package/dist/lsp/CompletionProvider.js +200 -0
  38. package/dist/lsp/DocumentHighlightProvider.js +10 -0
  39. package/dist/lsp/DocumentLinkProvider.js +58 -0
  40. package/dist/lsp/DocumentSymbolProvider.js +306 -0
  41. package/dist/lsp/HoverProvider.js +106 -0
  42. package/dist/lsp/RenameProvider.js +6 -0
  43. package/dist/lsp/SemanticTokenProvider.d.ts +5 -0
  44. package/dist/lsp/SemanticTokenProvider.js +257 -0
  45. package/dist/lsp/index.d.ts +1 -0
  46. package/dist/lsp/index.js +9 -0
  47. package/dist/mcp/MCPServerFactory.js +73 -0
  48. package/dist/mcp/NoopLikeC4MCPServer.js +17 -0
  49. package/dist/mcp/interfaces.js +5 -0
  50. package/dist/mcp/server/StdioLikeC4MCPServer.js +47 -0
  51. package/dist/mcp/server/StreamableLikeC4MCPServer.js +145 -0
  52. package/dist/mcp/server/WithMCPServer.js +56 -0
  53. package/dist/mcp/tools/_common.d.ts +8 -7
  54. package/dist/mcp/tools/_common.js +49 -0
  55. package/dist/mcp/tools/find-relationships.d.ts +7 -8
  56. package/dist/mcp/tools/find-relationships.js +150 -0
  57. package/dist/mcp/tools/list-projects.d.ts +3 -3
  58. package/dist/mcp/tools/list-projects.js +62 -0
  59. package/dist/mcp/tools/open-view.d.ts +6 -7
  60. package/dist/mcp/tools/open-view.js +52 -0
  61. package/dist/mcp/tools/read-deployment.d.ts +6 -7
  62. package/dist/mcp/tools/read-deployment.js +132 -0
  63. package/dist/mcp/tools/read-element.d.ts +6 -7
  64. package/dist/mcp/tools/read-element.js +194 -0
  65. package/dist/mcp/tools/read-project-summary.d.ts +5 -6
  66. package/dist/mcp/tools/read-project-summary.js +176 -0
  67. package/dist/mcp/tools/read-view.d.ts +6 -7
  68. package/dist/mcp/tools/read-view.js +203 -0
  69. package/dist/mcp/tools/search-element.d.ts +3 -3
  70. package/dist/mcp/tools/search-element.js +177 -0
  71. package/dist/mcp/utils.d.ts +2 -2
  72. package/dist/mcp/utils.js +48 -0
  73. package/dist/model/builder/MergedExtends.d.ts +2 -1
  74. package/dist/model/builder/MergedExtends.js +74 -0
  75. package/dist/model/builder/MergedSpecification.js +175 -0
  76. package/dist/model/builder/buildModel.js +176 -0
  77. package/dist/model/deployments-index.js +102 -0
  78. package/dist/model/fqn-index.js +250 -0
  79. package/dist/model/index.js +6 -0
  80. package/dist/model/model-builder.d.ts +13 -11
  81. package/dist/model/model-builder.js +234 -0
  82. package/dist/model/model-locator.d.ts +6 -5
  83. package/dist/model/model-locator.js +240 -0
  84. package/dist/model/model-parser-where.js +81 -0
  85. package/dist/model/model-parser.d.ts +318 -313
  86. package/dist/model/model-parser.js +119 -0
  87. package/dist/model/parser/Base.d.ts +3 -3
  88. package/dist/model/parser/Base.js +367 -0
  89. package/dist/model/parser/DeploymentModelParser.d.ts +3 -3
  90. package/dist/model/parser/DeploymentModelParser.js +176 -0
  91. package/dist/model/parser/DeploymentViewParser.d.ts +4 -4
  92. package/dist/model/parser/DeploymentViewParser.js +86 -0
  93. package/dist/model/parser/FqnRefParser.d.ts +3 -3
  94. package/dist/model/parser/FqnRefParser.js +382 -0
  95. package/dist/model/parser/GlobalsParser.d.ts +7 -7
  96. package/dist/model/parser/GlobalsParser.js +84 -0
  97. package/dist/model/parser/ImportsParser.d.ts +12 -13
  98. package/dist/model/parser/ImportsParser.js +24 -0
  99. package/dist/model/parser/ModelParser.d.ts +3 -3
  100. package/dist/model/parser/ModelParser.js +165 -0
  101. package/dist/model/parser/PredicatesParser.d.ts +3 -3
  102. package/dist/model/parser/PredicatesParser.js +45 -0
  103. package/dist/model/parser/SpecificationParser.d.ts +3 -3
  104. package/dist/model/parser/SpecificationParser.js +109 -0
  105. package/dist/model/parser/ValueConverter.js +12 -0
  106. package/dist/model/parser/ViewsParser.d.ts +4 -4
  107. package/dist/model/parser/ViewsParser.js +477 -0
  108. package/dist/model-change/ModelChanges.d.ts +6 -3
  109. package/dist/model-change/ModelChanges.js +102 -0
  110. package/dist/model-change/changeElementStyle.js +134 -0
  111. package/dist/model-change/changeViewLayout.d.ts +2 -2
  112. package/dist/model-change/changeViewLayout.js +28 -0
  113. package/dist/model-change/removeManualLayoutV1.d.ts +7 -0
  114. package/dist/model-change/removeManualLayoutV1.js +27 -0
  115. package/dist/module.d.ts +10 -5
  116. package/dist/module.js +143 -0
  117. package/dist/protocol.d.ts +1 -17
  118. package/dist/protocol.js +114 -0
  119. package/dist/references/index.js +3 -0
  120. package/dist/references/name-provider.js +37 -0
  121. package/dist/references/scope-computation.js +288 -0
  122. package/dist/references/scope-provider.d.ts +3 -3
  123. package/dist/references/scope-provider.js +242 -0
  124. package/dist/shared/NodeKindProvider.js +57 -0
  125. package/dist/shared/{WorkspaceSymbolProvider.mjs → WorkspaceSymbolProvider.js} +1 -1
  126. package/dist/shared/index.js +2 -0
  127. package/dist/test/index.js +1 -0
  128. package/dist/test/testServices.d.ts +16 -16
  129. package/dist/test/testServices.js +210 -0
  130. package/dist/utils/disposable.js +26 -0
  131. package/dist/utils/elementRef.d.ts +1 -1
  132. package/dist/utils/elementRef.js +27 -0
  133. package/dist/utils/fqnRef.js +63 -0
  134. package/dist/utils/index.js +35 -0
  135. package/dist/utils/printDocs.js +1 -0
  136. package/dist/utils/projectId.js +16 -0
  137. package/dist/utils/stringHash.js +5 -0
  138. package/dist/validation/DocumentValidator.js +17 -0
  139. package/dist/validation/_shared.js +26 -0
  140. package/dist/validation/deployment-checks.js +140 -0
  141. package/dist/validation/dynamic-view.js +67 -0
  142. package/dist/validation/element-ref.js +12 -0
  143. package/dist/validation/element.js +49 -0
  144. package/dist/validation/imports.js +46 -0
  145. package/dist/validation/index.d.ts +1 -1
  146. package/dist/validation/index.js +157 -0
  147. package/dist/validation/property-checks.js +108 -0
  148. package/dist/validation/relation.js +55 -0
  149. package/dist/validation/specification.js +190 -0
  150. package/dist/validation/view-predicates/fqn-expr-with.js +43 -0
  151. package/dist/validation/view-predicates/fqn-ref-expr.js +51 -0
  152. package/dist/validation/view-predicates/incoming.js +16 -0
  153. package/dist/validation/view-predicates/index.js +6 -0
  154. package/dist/validation/view-predicates/outgoing.js +20 -0
  155. package/dist/validation/view-predicates/relation-expr.js +46 -0
  156. package/dist/validation/view-predicates/relation-with.js +16 -0
  157. package/dist/validation/view.d.ts +1 -1
  158. package/dist/validation/view.js +42 -0
  159. package/dist/view-utils/assignNavigateTo.js +27 -0
  160. package/dist/view-utils/index.d.ts +1 -0
  161. package/dist/view-utils/index.js +2 -0
  162. package/dist/view-utils/manual-layout.d.ts +6 -0
  163. package/dist/view-utils/manual-layout.js +151 -0
  164. package/dist/views/ConfigurableLayouter.js +51 -0
  165. package/dist/views/LikeC4ManualLayouts.d.ts +28 -0
  166. package/dist/views/LikeC4ManualLayouts.js +132 -0
  167. package/dist/views/{likec4-views.d.ts → LikeC4Views.d.ts} +9 -8
  168. package/dist/views/LikeC4Views.js +200 -0
  169. package/dist/views/index.d.ts +4 -1
  170. package/dist/views/index.js +11 -0
  171. package/dist/workspace/AstNodeDescriptionProvider.js +15 -0
  172. package/dist/workspace/IndexManager.js +21 -0
  173. package/dist/workspace/LangiumDocuments.d.ts +1 -1
  174. package/dist/workspace/LangiumDocuments.js +58 -0
  175. package/dist/workspace/ProjectsManager.d.ts +8 -3
  176. package/dist/workspace/ProjectsManager.js +373 -0
  177. package/dist/workspace/WorkspaceManager.d.ts +3 -2
  178. package/dist/workspace/WorkspaceManager.js +93 -0
  179. package/dist/workspace/index.js +5 -0
  180. package/likec4lib/package.json +1 -1
  181. package/package.json +32 -31
  182. package/protocol/package.json +1 -1
  183. package/dist/LikeC4LanguageServices.mjs +0 -197
  184. package/dist/Rpc.mjs +0 -296
  185. package/dist/ast.mjs +0 -221
  186. package/dist/browser-worker.mjs +0 -2
  187. package/dist/browser.mjs +0 -32
  188. package/dist/documentation/documentation-provider.mjs +0 -48
  189. package/dist/documentation/index.mjs +0 -1
  190. package/dist/empty.mjs +0 -1
  191. package/dist/filesystem/ChokidarWatcher.mjs +0 -68
  192. package/dist/filesystem/FileSystemWatcher.mjs +0 -11
  193. package/dist/filesystem/LikeC4FileSystem.mjs +0 -64
  194. package/dist/filesystem/index.mjs +0 -19
  195. package/dist/formatting/LikeC4Formatter.mjs +0 -511
  196. package/dist/formatting/utils.mjs +0 -15
  197. package/dist/generated/ast.mjs +0 -2118
  198. package/dist/generated/grammar.mjs +0 -3
  199. package/dist/generated/module.mjs +0 -23
  200. package/dist/index.mjs +0 -50
  201. package/dist/logger.mjs +0 -82
  202. package/dist/lsp/CodeLensProvider.mjs +0 -42
  203. package/dist/lsp/CompletionProvider.mjs +0 -208
  204. package/dist/lsp/DocumentHighlightProvider.mjs +0 -10
  205. package/dist/lsp/DocumentLinkProvider.mjs +0 -53
  206. package/dist/lsp/DocumentSymbolProvider.mjs +0 -287
  207. package/dist/lsp/HoverProvider.mjs +0 -104
  208. package/dist/lsp/RenameProvider.mjs +0 -6
  209. package/dist/lsp/SemanticTokenProvider.mjs +0 -350
  210. package/dist/lsp/index.mjs +0 -7
  211. package/dist/mcp/MCPServerFactory.mjs +0 -70
  212. package/dist/mcp/NoopLikeC4MCPServer.mjs +0 -17
  213. package/dist/mcp/interfaces.mjs +0 -4
  214. package/dist/mcp/server/StdioLikeC4MCPServer.mjs +0 -46
  215. package/dist/mcp/server/StreamableLikeC4MCPServer.mjs +0 -153
  216. package/dist/mcp/server/WithMCPServer.mjs +0 -58
  217. package/dist/mcp/tools/_common.mjs +0 -42
  218. package/dist/mcp/tools/find-relationships.mjs +0 -151
  219. package/dist/mcp/tools/list-projects.mjs +0 -62
  220. package/dist/mcp/tools/open-view.mjs +0 -52
  221. package/dist/mcp/tools/read-deployment.mjs +0 -130
  222. package/dist/mcp/tools/read-element.mjs +0 -198
  223. package/dist/mcp/tools/read-project-summary.mjs +0 -178
  224. package/dist/mcp/tools/read-view.mjs +0 -205
  225. package/dist/mcp/tools/search-element.mjs +0 -171
  226. package/dist/mcp/utils.mjs +0 -47
  227. package/dist/model/builder/MergedExtends.mjs +0 -67
  228. package/dist/model/builder/MergedSpecification.mjs +0 -205
  229. package/dist/model/builder/assignTagColors.d.ts +0 -7
  230. package/dist/model/builder/assignTagColors.mjs +0 -51
  231. package/dist/model/builder/buildModel.mjs +0 -226
  232. package/dist/model/deployments-index.mjs +0 -100
  233. package/dist/model/fqn-index.mjs +0 -243
  234. package/dist/model/index.mjs +0 -6
  235. package/dist/model/model-builder.mjs +0 -285
  236. package/dist/model/model-locator.mjs +0 -239
  237. package/dist/model/model-parser-where.mjs +0 -81
  238. package/dist/model/model-parser.mjs +0 -127
  239. package/dist/model/parser/Base.mjs +0 -342
  240. package/dist/model/parser/DeploymentModelParser.mjs +0 -212
  241. package/dist/model/parser/DeploymentViewParser.mjs +0 -95
  242. package/dist/model/parser/FqnRefParser.mjs +0 -398
  243. package/dist/model/parser/GlobalsParser.mjs +0 -82
  244. package/dist/model/parser/ImportsParser.mjs +0 -28
  245. package/dist/model/parser/ModelParser.mjs +0 -190
  246. package/dist/model/parser/PredicatesParser.mjs +0 -45
  247. package/dist/model/parser/SpecificationParser.mjs +0 -120
  248. package/dist/model/parser/ValueConverter.mjs +0 -12
  249. package/dist/model/parser/ViewsParser.mjs +0 -490
  250. package/dist/model-change/ModelChanges.mjs +0 -89
  251. package/dist/model-change/changeElementStyle.mjs +0 -143
  252. package/dist/model-change/changeViewLayout.mjs +0 -32
  253. package/dist/model-change/saveManualLayout.d.ts +0 -11
  254. package/dist/model-change/saveManualLayout.mjs +0 -27
  255. package/dist/module.mjs +0 -180
  256. package/dist/protocol.mjs +0 -65
  257. package/dist/references/index.mjs +0 -3
  258. package/dist/references/name-provider.mjs +0 -39
  259. package/dist/references/scope-computation.mjs +0 -312
  260. package/dist/references/scope-provider.mjs +0 -239
  261. package/dist/shared/NodeKindProvider.mjs +0 -110
  262. package/dist/shared/index.mjs +0 -2
  263. package/dist/test/index.mjs +0 -1
  264. package/dist/test/testServices.mjs +0 -200
  265. package/dist/utils/disposable.mjs +0 -25
  266. package/dist/utils/elementRef.mjs +0 -20
  267. package/dist/utils/fqnRef.mjs +0 -57
  268. package/dist/utils/index.mjs +0 -33
  269. package/dist/utils/printDocs.mjs +0 -1
  270. package/dist/utils/projectId.mjs +0 -16
  271. package/dist/utils/stringHash.mjs +0 -5
  272. package/dist/validation/DocumentValidator.mjs +0 -16
  273. package/dist/validation/_shared.mjs +0 -25
  274. package/dist/validation/deployment-checks.mjs +0 -146
  275. package/dist/validation/dynamic-view.mjs +0 -67
  276. package/dist/validation/element-ref.mjs +0 -12
  277. package/dist/validation/element.mjs +0 -50
  278. package/dist/validation/imports.mjs +0 -25
  279. package/dist/validation/index.mjs +0 -180
  280. package/dist/validation/property-checks.mjs +0 -107
  281. package/dist/validation/relation.mjs +0 -53
  282. package/dist/validation/specification.mjs +0 -173
  283. package/dist/validation/view-predicates/fqn-expr-with.mjs +0 -43
  284. package/dist/validation/view-predicates/fqn-ref-expr.mjs +0 -53
  285. package/dist/validation/view-predicates/incoming.mjs +0 -16
  286. package/dist/validation/view-predicates/index.mjs +0 -6
  287. package/dist/validation/view-predicates/outgoing.mjs +0 -20
  288. package/dist/validation/view-predicates/relation-expr.mjs +0 -39
  289. package/dist/validation/view-predicates/relation-with.mjs +0 -16
  290. package/dist/validation/view.mjs +0 -25
  291. package/dist/view-utils/assignNavigateTo.mjs +0 -25
  292. package/dist/view-utils/index.mjs +0 -1
  293. package/dist/view-utils/manual-layout.mjs +0 -99
  294. package/dist/views/configurable-layouter.mjs +0 -51
  295. package/dist/views/index.mjs +0 -1
  296. package/dist/views/likec4-views.mjs +0 -166
  297. package/dist/workspace/AstNodeDescriptionProvider.mjs +0 -17
  298. package/dist/workspace/IndexManager.mjs +0 -17
  299. package/dist/workspace/LangiumDocuments.mjs +0 -53
  300. package/dist/workspace/ProjectsManager.mjs +0 -360
  301. package/dist/workspace/WorkspaceManager.mjs +0 -83
  302. package/dist/workspace/index.mjs +0 -5
  303. /package/dist/views/{configurable-layouter.d.ts → ConfigurableLayouter.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "types": "../dist/browser.d.ts",
3
- "module": "../dist/browser.mjs"
3
+ "module": "../dist/browser.js"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "types": "../dist/browser-worker.d.ts",
3
- "module": "../dist/browser-worker.mjs"
3
+ "module": "../dist/browser-worker.js"
4
4
  }
@@ -1,12 +1,13 @@
1
1
  import type { LikeC4ProjectConfig } from '@likec4/config';
2
- import { type DiagramView, type NonEmptyArray, type ProjectId } from '@likec4/core';
2
+ import { type DiagramView, type NonEmptyArray, type ProjectId, type UnknownComputed, type UnknownLayouted } from '@likec4/core';
3
3
  import { LikeC4Model } from '@likec4/core/model';
4
- import { type LangiumDocument, URI } from 'langium';
5
- import { type Range } from 'vscode-languageserver-types';
4
+ import { URI } from 'langium';
5
+ import type { CancellationToken } from 'vscode-jsonrpc';
6
+ import type { Range } from 'vscode-languageserver-types';
6
7
  import type { LikeC4ModelBuilder } from './model';
7
8
  import type { LikeC4Services } from './module';
8
9
  import type { Locate } from './protocol';
9
- import type { LikeC4Views } from './views/likec4-views';
10
+ import type { LikeC4Views } from './views/LikeC4Views';
10
11
  import { ProjectsManager } from './workspace';
11
12
  export interface LikeC4LanguageServices {
12
13
  readonly views: LikeC4Views;
@@ -38,9 +39,9 @@ export interface LikeC4LanguageServices {
38
39
  * Returns diagrams (i.e. views with layout computed) for the specified project
39
40
  * If no project is specified, returns diagrams for default project
40
41
  */
41
- diagrams(project?: ProjectId | undefined): Promise<DiagramView[]>;
42
- computedModel(project?: ProjectId | undefined): Promise<LikeC4Model.Computed>;
43
- layoutedModel(project?: ProjectId | undefined): Promise<LikeC4Model.Layouted>;
42
+ diagrams(project?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<DiagramView[]>;
43
+ computedModel(project?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<LikeC4Model<UnknownComputed>>;
44
+ layoutedModel(project?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<LikeC4Model<UnknownLayouted>>;
44
45
  getErrors(): Array<{
45
46
  message: string;
46
47
  line: number;
@@ -59,10 +60,6 @@ export interface LikeC4LanguageServices {
59
60
  * Returns the location of the specified element, relation, view or deployment element
60
61
  */
61
62
  locate(params: Locate.Params): Locate.Res;
62
- /**
63
- * Checks if the specified document should be excluded from processing.
64
- */
65
- isExcluded(doc: LangiumDocument): boolean;
66
63
  dispose(): Promise<void>;
67
64
  }
68
65
  /**
@@ -91,20 +88,20 @@ export declare class DefaultLikeC4LanguageServices implements LikeC4LanguageServ
91
88
  };
92
89
  /**
93
90
  * Diagram is a computed view, layouted using Graphviz
94
- * Used in React components
91
+ * If diagram has manual layout, it will be used.
95
92
  */
96
- diagrams(): Promise<DiagramView[]>;
93
+ diagrams(project?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<DiagramView[]>;
97
94
  /**
98
95
  * Builds LikeC4Model from all documents
99
96
  * Only computes view predicates {@link ComputedView} - i.e. no layout
100
97
  * Not ready for rendering, but enough to traverse
101
98
  */
102
- computedModel(project?: ProjectId | undefined): Promise<LikeC4Model.Computed>;
99
+ computedModel(project?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<LikeC4Model<UnknownComputed>>;
103
100
  /**
104
101
  * Same as {@link computedModel()}, but also applies layout
105
102
  * Ready for rendering
106
103
  */
107
- layoutedModel(project?: ProjectId | undefined): Promise<LikeC4Model.Layouted>;
104
+ layoutedModel(project?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<LikeC4Model<UnknownLayouted>>;
108
105
  getErrors(): Array<{
109
106
  message: string;
110
107
  line: number;
@@ -119,9 +116,5 @@ export declare class DefaultLikeC4LanguageServices implements LikeC4LanguageServ
119
116
  removed?: string;
120
117
  }): Promise<boolean>;
121
118
  locate(params: Locate.Params): Locate.Res;
122
- /**
123
- * Checks if the specified document should be excluded from processing.
124
- */
125
- isExcluded(doc: LangiumDocument): boolean;
126
119
  dispose(): Promise<void>;
127
120
  }
@@ -0,0 +1,182 @@
1
+ import { nonexhaustive, } from '@likec4/core';
2
+ import { LikeC4Model } from '@likec4/core/model';
3
+ import { loggable } from '@likec4/log';
4
+ import { URI } from 'langium';
5
+ import { entries, hasAtLeast, indexBy, map, pipe, prop } from 'remeda';
6
+ import { DiagnosticSeverity } from 'vscode-languageserver-types';
7
+ import { isLikeC4LangiumDocument } from './ast';
8
+ import { logger as mainLogger, logWarnError } from './logger';
9
+ import { ProjectsManager } from './workspace';
10
+ const logger = mainLogger.getChild('LanguageServices');
11
+ /**
12
+ * Public Language Services
13
+ */
14
+ export class DefaultLikeC4LanguageServices {
15
+ services;
16
+ builder;
17
+ projectsManager;
18
+ constructor(services) {
19
+ this.services = services;
20
+ this.builder = services.likec4.ModelBuilder;
21
+ this.projectsManager = services.shared.workspace.ProjectsManager;
22
+ }
23
+ get views() {
24
+ return this.services.likec4.Views;
25
+ }
26
+ get workspaceUri() {
27
+ return this.services.shared.workspace.WorkspaceManager.workspaceUri;
28
+ }
29
+ projects() {
30
+ const projectsManager = this.services.shared.workspace.ProjectsManager;
31
+ const projectsWithDocs = pipe(this.services.shared.workspace.LangiumDocuments.groupedByProject(), entries(), map(([projectId, docs]) => {
32
+ const id = projectId;
33
+ const { folderUri, config } = projectsManager.getProject(id);
34
+ return {
35
+ id,
36
+ folder: folderUri,
37
+ title: config.title ?? config.name,
38
+ documents: map(docs, prop('uri')),
39
+ config,
40
+ };
41
+ }));
42
+ // if there are multiple projects and default project is set, ensure it is first
43
+ if (hasAtLeast(projectsWithDocs, 2) && projectsManager.defaultProjectId) {
44
+ const idx = projectsWithDocs.findIndex(p => p.id === projectsManager.defaultProjectId);
45
+ if (idx > 0) {
46
+ const [defaultProject] = projectsWithDocs.splice(idx, 1);
47
+ return [defaultProject, ...projectsWithDocs];
48
+ }
49
+ return projectsWithDocs;
50
+ }
51
+ if (hasAtLeast(projectsWithDocs, 1)) {
52
+ return projectsWithDocs;
53
+ }
54
+ const { folderUri, config } = projectsManager.getProject(ProjectsManager.DefaultProjectId);
55
+ const documents = map(this.services.shared.workspace.LangiumDocuments.projectDocuments(ProjectsManager.DefaultProjectId).toArray(), prop('uri'));
56
+ return [{
57
+ id: ProjectsManager.DefaultProjectId,
58
+ folder: folderUri,
59
+ title: config.title ?? config.name,
60
+ documents,
61
+ config,
62
+ }];
63
+ }
64
+ project(projectId) {
65
+ projectId = this.projectsManager.ensureProjectId(projectId);
66
+ const projectsManager = this.services.shared.workspace.ProjectsManager;
67
+ const { folderUri, config } = projectsManager.getProject(projectId);
68
+ const documents = map(this.services.shared.workspace.LangiumDocuments.projectDocuments(projectId).toArray(), prop('uri'));
69
+ return {
70
+ id: projectId,
71
+ folder: folderUri,
72
+ title: config.title ?? config.name,
73
+ documents,
74
+ config,
75
+ };
76
+ }
77
+ /**
78
+ * Diagram is a computed view, layouted using Graphviz
79
+ * If diagram has manual layout, it will be used.
80
+ */
81
+ async diagrams(project, cancelToken) {
82
+ const projectId = this.projectsManager.ensureProjectId(project);
83
+ return await this.views.diagrams(projectId, cancelToken);
84
+ }
85
+ /**
86
+ * Builds LikeC4Model from all documents
87
+ * Only computes view predicates {@link ComputedView} - i.e. no layout
88
+ * Not ready for rendering, but enough to traverse
89
+ */
90
+ async computedModel(project, cancelToken) {
91
+ const projectId = this.projectsManager.ensureProjectId(project);
92
+ return await this.builder.computeModel(projectId, cancelToken);
93
+ }
94
+ /**
95
+ * Same as {@link computedModel()}, but also applies layout
96
+ * Ready for rendering
97
+ */
98
+ async layoutedModel(project, cancelToken) {
99
+ const projectId = this.projectsManager.ensureProjectId(project);
100
+ const model = await this.builder.computeModel(projectId, cancelToken);
101
+ if (!model) {
102
+ throw new Error('Failed to parse model');
103
+ }
104
+ const layouted = await this.views.layoutAllViews(projectId, cancelToken);
105
+ return LikeC4Model.create({
106
+ ...model.$data,
107
+ _stage: 'layouted',
108
+ views: pipe(layouted, map(prop('diagram')), indexBy(prop('id'))),
109
+ });
110
+ }
111
+ getErrors() {
112
+ const docs = this.services.shared.workspace.LangiumDocuments.allExcludingBuiltin.toArray();
113
+ return docs.flatMap(doc => {
114
+ return (doc.diagnostics ?? [])
115
+ .filter(d => d.severity === DiagnosticSeverity.Error)
116
+ .map(({ message, range }) => ({
117
+ message,
118
+ line: range.start.line,
119
+ range,
120
+ sourceFsPath: doc.uri.fsPath,
121
+ }));
122
+ });
123
+ }
124
+ /**
125
+ * TODO Replace with watcher
126
+ */
127
+ async notifyUpdate({ changed, removed }) {
128
+ if (!changed && !removed) {
129
+ return false;
130
+ }
131
+ const _changed = changed ? URI.file(changed) : undefined;
132
+ const _removed = removed ? URI.file(removed) : undefined;
133
+ const pm = this.services.shared.workspace.ProjectsManager;
134
+ if ((_changed && pm.isConfigFile(_changed)) || (_removed && pm.isConfigFile(_removed))) {
135
+ await pm.reloadProjects();
136
+ return true;
137
+ }
138
+ const mutex = this.services.shared.workspace.WorkspaceLock;
139
+ try {
140
+ let completed = false;
141
+ await mutex.write(async (token) => {
142
+ await this.services.shared.workspace.DocumentBuilder.update(_changed ? [_changed] : [], _removed ? [_removed] : [], token);
143
+ // we come here if only the update was successful, did not throw and not cancelled
144
+ completed = !token.isCancellationRequested;
145
+ });
146
+ return completed;
147
+ }
148
+ catch (e) {
149
+ logger.error(loggable(e));
150
+ return false;
151
+ }
152
+ }
153
+ locate(params) {
154
+ switch (true) {
155
+ case 'element' in params:
156
+ return this.services.likec4.ModelLocator.locateElement(params.element, params.projectId);
157
+ case 'relation' in params:
158
+ return this.services.likec4.ModelLocator.locateRelation(params.relation, params.projectId);
159
+ case 'view' in params:
160
+ return this.services.likec4.ModelLocator.locateView(params.view, params.projectId);
161
+ case 'deployment' in params:
162
+ return this.services.likec4.ModelLocator.locateDeploymentElement(params.deployment, params.projectId);
163
+ default:
164
+ nonexhaustive(params);
165
+ }
166
+ }
167
+ async dispose() {
168
+ try {
169
+ logger.debug('disposing LikeC4LanguageServices');
170
+ await this.services.shared.workspace.FileSystemWatcher.dispose();
171
+ if (this.services.mcp.Server.isStarted) {
172
+ await this.services.mcp.Server.stop();
173
+ }
174
+ this.services.Rpc.dispose();
175
+ this.services.likec4.ModelBuilder.dispose();
176
+ logger.debug('LikeC4LanguageServices disposed');
177
+ }
178
+ catch (e) {
179
+ logger.error(loggable(e));
180
+ }
181
+ }
182
+ }
package/dist/Rpc.js ADDED
@@ -0,0 +1,245 @@
1
+ import { filter, flatMap, funnel, indexBy, keys, map, mapValues, pipe, sort } from 'remeda';
2
+ import { logger as rootLogger } from './logger';
3
+ import { serializableLikeC4ProjectConfig } from '@likec4/config';
4
+ import { invariant, nonexhaustive, } from '@likec4/core';
5
+ import { LikeC4Model } from '@likec4/core/model';
6
+ import { Disposable, interruptAndCheck, URI, UriUtils } from 'langium';
7
+ import { DiagnosticSeverity } from 'vscode-languageserver-protocol';
8
+ import { BuildDocuments, ChangeView, DidChangeModelNotification, DidRequestOpenViewNotification, FetchComputedModel, FetchLayoutedModel, FetchProjects, FetchTelemetryMetrics, FetchViewsFromAllProjects, GetDocumentTags, LayoutView, Locate, RegisterProject, ReloadProjects, ValidateLayout, } from './protocol';
9
+ import { ADisposable } from './utils';
10
+ const logger = rootLogger.getChild('rpc');
11
+ export class Rpc extends ADisposable {
12
+ services;
13
+ constructor(services) {
14
+ super();
15
+ this.services = services;
16
+ }
17
+ init() {
18
+ const connection = this.services.shared.lsp.Connection;
19
+ if (!connection) {
20
+ logger.info(`[ServerRpc] no connection, not initializing`);
21
+ return;
22
+ }
23
+ logger.info(`[ServerRpc] init`);
24
+ const likec4Services = this.services.likec4;
25
+ const projects = this.services.shared.workspace.ProjectsManager;
26
+ const LangiumDocuments = this.services.shared.workspace.LangiumDocuments;
27
+ const DocumentBuilder = this.services.shared.workspace.DocumentBuilder;
28
+ const notifyModelParsed = funnel(() => {
29
+ logger.debug `sendNotification ${'onDidChangeModel'}`;
30
+ connection.sendNotification(DidChangeModelNotification.type, '').catch(error => {
31
+ logger.warn(`[ServerRpc] error sending onDidChangeModel:`, { error });
32
+ return;
33
+ });
34
+ }, {
35
+ triggerAt: 'end',
36
+ minQuietPeriodMs: 150,
37
+ maxBurstDurationMs: 500,
38
+ minGapMs: 300,
39
+ });
40
+ let isFirstBuild = true;
41
+ this.onDispose(likec4Services.ModelBuilder.onModelParsed(() => notifyModelParsed.call()), connection.onRequest(FetchComputedModel.req, async ({ projectId, cleanCaches }, cancelToken) => {
42
+ logger.debug `received request ${'fetchComputedModel'} for project ${projectId}`;
43
+ if (cleanCaches) {
44
+ const docs = projectId
45
+ ? LangiumDocuments.projectDocuments(projectId)
46
+ : LangiumDocuments.allExcludingBuiltin;
47
+ const uris = docs.toArray().map(d => d.uri);
48
+ await DocumentBuilder.update(uris, [], cancelToken);
49
+ }
50
+ const likec4model = await likec4Services.ModelBuilder.computeModel(projectId, cancelToken);
51
+ if (likec4model !== LikeC4Model.EMPTY) {
52
+ return { model: likec4model.$model };
53
+ }
54
+ return { model: null };
55
+ }), connection.onRequest(FetchLayoutedModel.req, async ({ projectId }, cancelToken) => {
56
+ logger.debug `received request ${'fetchLayoutedModel'} for project ${projectId}`;
57
+ const model = await likec4Services.LanguageServices.layoutedModel(projectId);
58
+ if (model === null) {
59
+ return { model: null };
60
+ }
61
+ const diagrams = await likec4Services.Views.diagrams(projectId, cancelToken);
62
+ return {
63
+ model: {
64
+ ...model.$data,
65
+ _stage: 'layouted',
66
+ views: indexBy(diagrams, d => d.id),
67
+ },
68
+ };
69
+ }), connection.onRequest(LayoutView.req, async ({ viewId, projectId }, cancelToken) => {
70
+ logger.debug `received request ${'layoutView'} for ${viewId} from project ${projectId}`;
71
+ const result = await likec4Services.Views.layoutView(viewId, projectId, cancelToken);
72
+ return { result };
73
+ }), connection.onRequest(ValidateLayout.Req, async ({ projectId }, cancelToken) => {
74
+ logger.debug `received request ${'validateLayout'} for project ${projectId}`;
75
+ const layouts = await likec4Services.Views.layoutAllViews(projectId, cancelToken);
76
+ const result = reportLayoutDrift(layouts.map(l => l.diagram));
77
+ return { result };
78
+ }), connection.onRequest(FetchProjects.req, async (_cancelToken) => {
79
+ logger.debug `received request ${'FetchProjects'}`;
80
+ const docsByProject = LangiumDocuments.groupedByProject();
81
+ return {
82
+ projects: mapValues(docsByProject, (docs, projectId) => {
83
+ const { folderUri, config, } = projects.getProject(projectId);
84
+ return {
85
+ folder: folderUri.toString(),
86
+ config: serializableLikeC4ProjectConfig(config),
87
+ docs: map(docs, d => d.uri.toString()),
88
+ };
89
+ }),
90
+ };
91
+ }), connection.onRequest(ReloadProjects.req, async () => {
92
+ logger.debug `received request ${'ReloadProjects'}`;
93
+ likec4Services.ManualLayouts.clearCaches();
94
+ await projects.reloadProjects();
95
+ return;
96
+ }), connection.onRequest(RegisterProject.req, async (params) => {
97
+ logger.debug `received request ${'RegisterProject'}`;
98
+ const project = await projects.registerProject(params);
99
+ return { id: project.id };
100
+ }), connection.onRequest(FetchViewsFromAllProjects.req, async (cancelToken) => {
101
+ logger.debug `received request ${'FetchViewsFromAllProjects'}`;
102
+ const promises = projects.all.map(async (projectId) => {
103
+ const computedViews = await likec4Services.Views.computedViews(projectId, cancelToken);
104
+ return pipe(computedViews, map(v => ({
105
+ id: v.id,
106
+ title: v.title ?? v.id,
107
+ projectId,
108
+ })), sort((a, b) => {
109
+ if (a.id === 'index') {
110
+ return -1;
111
+ }
112
+ if (b.id === 'index') {
113
+ return 1;
114
+ }
115
+ return a.title.localeCompare(b.title);
116
+ }));
117
+ });
118
+ const results = await Promise.allSettled(promises);
119
+ await interruptAndCheck(cancelToken);
120
+ return {
121
+ views: pipe(results, filter(r => r.status === 'fulfilled'), flatMap(r => r.value)),
122
+ };
123
+ }), connection.onRequest(BuildDocuments.Req, async ({ docs }, cancelToken) => {
124
+ const changed = docs.map(d => URI.parse(d));
125
+ const notChanged = (uri) => changed.every(c => !UriUtils.equals(c, uri));
126
+ const deleted = LangiumDocuments.allExcludingBuiltin
127
+ .toArray()
128
+ .filter(d => notChanged(d.uri))
129
+ .map(d => d.uri);
130
+ logger.debug(`[ServerRpc] received request to build:
131
+ changed (total ${changed.length}):${docs.map(d => '\n - ' + d).join('')}
132
+ deleted (total ${deleted.length}):${deleted.map(d => '\n - ' + d.toString()).join('\n')}`);
133
+ if (!isFirstBuild && (changed.length + deleted.length) > 0) {
134
+ await Promise.allSettled([...changed, ...deleted].map(async (d) => {
135
+ const uri = d.toString();
136
+ logger.debug(`clear diagnostics for ${uri}`);
137
+ try {
138
+ await connection.sendDiagnostics({
139
+ uri,
140
+ diagnostics: [],
141
+ });
142
+ }
143
+ catch (e) {
144
+ // Ignore
145
+ logger.warn(`error clearing diagnostics for ${uri}: ${e}`);
146
+ }
147
+ }));
148
+ }
149
+ isFirstBuild = false;
150
+ await interruptAndCheck(cancelToken);
151
+ await DocumentBuilder.update(changed, deleted, cancelToken);
152
+ }), connection.onRequest(Locate.Req, params => {
153
+ logger.debug `received request ${'locate'}, ${params}`;
154
+ switch (true) {
155
+ case 'element' in params:
156
+ return likec4Services.ModelLocator.locateElement(params.element, params.projectId);
157
+ case 'relation' in params:
158
+ return likec4Services.ModelLocator.locateRelation(params.relation, params.projectId);
159
+ case 'astPath' in params:
160
+ return likec4Services.ModelLocator.locateDynamicViewStep({
161
+ view: params.view,
162
+ astPath: params.astPath,
163
+ projectId: params.projectId,
164
+ });
165
+ case 'view' in params:
166
+ return likec4Services.ModelLocator.locateView(params.view, params.projectId);
167
+ case 'deployment' in params:
168
+ return likec4Services.ModelLocator.locateDeploymentElement(params.deployment, params.projectId);
169
+ default:
170
+ nonexhaustive(params);
171
+ }
172
+ }), connection.onRequest(ChangeView.Req, async (request, _cancelToken) => {
173
+ logger.debug `received request ${'changeView'} of ${request.viewId} from project ${request.projectId}`;
174
+ return await likec4Services.ModelChanges.applyChange(request);
175
+ }), connection.onRequest(FetchTelemetryMetrics.req, async (cancelToken) => {
176
+ const projectsIds = [...projects.all];
177
+ const promises = projectsIds.map(async (projectId) => {
178
+ const model = await likec4Services.ModelBuilder.computeModel(projectId, cancelToken);
179
+ if (model === LikeC4Model.EMPTY) {
180
+ return Promise.reject(new Error(`Model is empty`));
181
+ }
182
+ return {
183
+ elementKinds: keys(model.specification.elements).length,
184
+ deploymentKinds: keys(model.specification.deployments).length,
185
+ relationshipKinds: keys(model.specification.relationships).length,
186
+ tags: keys(model.specification.tags).length,
187
+ customColors: keys(model.specification.customColors ?? {}).length,
188
+ elements: keys(model.$data.elements).length,
189
+ deploymentNodes: [...model.deployment.nodes()].length,
190
+ relationships: keys(model.$data.relations).length,
191
+ views: keys(model.$data.views).length,
192
+ projects: 1,
193
+ };
194
+ });
195
+ const results = await Promise.allSettled(promises);
196
+ await interruptAndCheck(cancelToken);
197
+ const values = results.filter(r => r.status === 'fulfilled').map(r => r.value);
198
+ const metrics = values.length > 0
199
+ ? values.reduce((acc, r) => ({
200
+ elementKinds: acc.elementKinds + r.elementKinds,
201
+ deploymentKinds: acc.deploymentKinds + r.deploymentKinds,
202
+ relationshipKinds: acc.relationshipKinds + r.relationshipKinds,
203
+ tags: acc.tags + r.tags,
204
+ customColors: acc.customColors + r.customColors,
205
+ elements: acc.elements + r.elements,
206
+ deploymentNodes: acc.deploymentNodes + r.deploymentNodes,
207
+ relationships: acc.relationships + r.relationships,
208
+ views: acc.views + r.views,
209
+ projects: acc.projects + 1,
210
+ }))
211
+ : null;
212
+ return {
213
+ metrics,
214
+ };
215
+ }), connection.onRequest(GetDocumentTags.req, async ({ documentUri }, cancelToken) => {
216
+ const tags = await likec4Services.ModelLocator.locateDocumentTags(URI.parse(documentUri), cancelToken);
217
+ return {
218
+ tags,
219
+ };
220
+ }), Disposable.create(() => {
221
+ notifyModelParsed.cancel();
222
+ }));
223
+ function reportLayoutDrift(diagrams) {
224
+ return pipe(diagrams, filter(d => !!d.hasLayoutDrift), map(d => {
225
+ const loc = likec4Services.ModelLocator.locateView(d.id);
226
+ invariant(loc, `View ${d.id} not found`);
227
+ return {
228
+ uri: loc.uri,
229
+ viewId: d.id,
230
+ severity: DiagnosticSeverity.Warning,
231
+ message: `Layout drift detected for view '${d.id}'`,
232
+ range: loc.range,
233
+ };
234
+ }));
235
+ }
236
+ }
237
+ async openView(params) {
238
+ const lspConnection = this.services.shared.lsp.Connection;
239
+ if (!lspConnection) {
240
+ logger.warn('No LSP connection');
241
+ return;
242
+ }
243
+ await lspConnection.sendNotification(DidRequestOpenViewNotification.type, params);
244
+ }
245
+ }
package/dist/ast.d.ts CHANGED
@@ -78,16 +78,16 @@ export interface ParsedAstElement {
78
78
  links?: c4.NonEmptyArray<c4.Link>;
79
79
  style: ParsedElementStyle;
80
80
  metadata?: {
81
- [key: string]: string;
81
+ [key: string]: string | string[];
82
82
  };
83
83
  }
84
84
  export interface ParsedAstExtend {
85
85
  id: c4.Fqn;
86
86
  astPath: string;
87
- tags: c4.NonEmptyArray<c4.Tag> | null;
88
- links: c4.NonEmptyArray<c4.Link> | null;
87
+ tags?: c4.NonEmptyArray<c4.Tag> | null;
88
+ links?: c4.NonEmptyArray<c4.Link> | null;
89
89
  metadata?: {
90
- [key: string]: string;
90
+ [key: string]: string | string[];
91
91
  };
92
92
  }
93
93
  export interface ParsedAstRelation {
@@ -107,7 +107,7 @@ export interface ParsedAstRelation {
107
107
  links?: c4.NonEmptyArray<c4.Link>;
108
108
  navigateTo?: c4.ViewId;
109
109
  metadata?: {
110
- [key: string]: string;
110
+ [key: string]: string | string[];
111
111
  };
112
112
  }
113
113
  export type ParsedAstDeployment = Simplify<MergeExclusive<ParsedAstDeployment.Node, ParsedAstDeployment.Instance>>;
@@ -122,6 +122,7 @@ export type ParsedAstDeploymentRelation = c4.DeploymentRelationship & {
122
122
  };
123
123
  export type ParsedAstGlobals = Writable<c4.ModelGlobals>;
124
124
  export interface ParsedAstElementView {
125
+ [c4._type]: 'element';
125
126
  id: c4.ViewId;
126
127
  viewOf?: c4.Fqn;
127
128
  extends?: c4.ViewId;
@@ -134,6 +135,7 @@ export interface ParsedAstElementView {
134
135
  manualLayout?: c4.ViewManualLayout;
135
136
  }
136
137
  export interface ParsedAstDynamicView {
138
+ [c4._type]: 'dynamic';
137
139
  id: c4.ViewId;
138
140
  astPath: string;
139
141
  title: string | null;
@@ -146,6 +148,7 @@ export interface ParsedAstDynamicView {
146
148
  manualLayout?: c4.ViewManualLayout;
147
149
  }
148
150
  export interface ParsedAstDeploymentView {
151
+ [c4._type]: 'deployment';
149
152
  id: c4.ViewId;
150
153
  astPath: string;
151
154
  title: string | null;
@@ -153,6 +156,7 @@ export interface ParsedAstDeploymentView {
153
156
  tags: c4.NonEmptyArray<c4.Tag> | null;
154
157
  links: c4.NonEmptyArray<c4.Link> | null;
155
158
  rules: Array<c4.DeploymentViewRule>;
159
+ manualLayout?: c4.ViewManualLayout;
156
160
  }
157
161
  export type ParsedAstView = ParsedAstElementView | ParsedAstDynamicView | ParsedAstDeploymentView;
158
162
  export declare const ViewOps: {
@@ -161,7 +165,7 @@ export declare const ViewOps: {
161
165
  };
162
166
  export declare const ElementOps: {
163
167
  writeId(node: ast.Element | ast.DeploymentElement, id: c4.Fqn | null): ast.DeploymentElement | ast.Element;
164
- readId(node: ast.Element | ast.DeploymentElement): any;
168
+ readId(node: ast.Element | ast.DeploymentElement): c4.Fqn<string> | undefined;
165
169
  };
166
170
  export interface AstNodeDescriptionWithFqn extends AstNodeDescription {
167
171
  likec4ProjectId: c4.ProjectId;