@likec4/language-server 1.1.1 → 1.2.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 (173) hide show
  1. package/contrib/likec4.monarch.ts +4 -4
  2. package/contrib/likec4.tmLanguage.json +1 -1
  3. package/package.json +8 -10
  4. package/src/Rpc.ts +108 -0
  5. package/src/ast.ts +443 -0
  6. package/src/browser/index.ts +30 -0
  7. package/src/elementRef.ts +26 -0
  8. package/src/generated/ast.ts +1632 -0
  9. package/src/generated/grammar.ts +10 -0
  10. package/src/generated/module.ts +32 -0
  11. package/src/index.ts +4 -0
  12. package/src/like-c4.langium +395 -0
  13. package/src/logger.ts +54 -0
  14. package/src/lsp/CodeLensProvider.ts +51 -0
  15. package/src/lsp/DocumentHighlightProvider.ts +12 -0
  16. package/src/lsp/DocumentLinkProvider.test.ts +66 -0
  17. package/src/lsp/DocumentLinkProvider.ts +53 -0
  18. package/src/lsp/DocumentSymbolProvider.ts +201 -0
  19. package/src/lsp/HoverProvider.ts +58 -0
  20. package/{dist/lsp/SemanticTokenProvider.js → src/lsp/SemanticTokenProvider.ts} +58 -43
  21. package/src/lsp/index.ts +6 -0
  22. package/src/model/fqn-computation.ts +47 -0
  23. package/src/model/fqn-index.ts +161 -0
  24. package/src/model/index.ts +5 -0
  25. package/src/model/model-builder.ts +447 -0
  26. package/src/model/model-locator.ts +130 -0
  27. package/src/model/model-parser.ts +580 -0
  28. package/src/model-change/ModelChanges.ts +120 -0
  29. package/src/model-change/changeElementStyle.ts +176 -0
  30. package/src/model-change/changeViewLayout.ts +41 -0
  31. package/src/module.ts +197 -0
  32. package/src/node/index.ts +20 -0
  33. package/src/protocol.ts +87 -0
  34. package/src/references/index.ts +2 -0
  35. package/src/references/scope-computation.ts +142 -0
  36. package/src/references/scope-provider.ts +166 -0
  37. package/src/shared/NodeKindProvider.ts +67 -0
  38. package/src/shared/WorkspaceManager.ts +39 -0
  39. package/src/shared/WorkspaceSymbolProvider.ts +3 -0
  40. package/src/shared/index.ts +3 -0
  41. package/src/test/index.ts +1 -0
  42. package/src/test/testServices.ts +119 -0
  43. package/src/utils/index.ts +1 -0
  44. package/src/utils/printDocs.ts +3 -0
  45. package/src/utils/stringHash.ts +6 -0
  46. package/src/validation/dynamic-view-rule.ts +35 -0
  47. package/src/validation/dynamic-view-step.ts +39 -0
  48. package/src/validation/element.ts +52 -0
  49. package/{dist/validation/index.js → src/validation/index.ts} +25 -17
  50. package/src/validation/property-checks.ts +17 -0
  51. package/src/validation/relation.ts +57 -0
  52. package/src/validation/specification.ts +118 -0
  53. package/src/validation/view-predicates/custom-element-expr.ts +21 -0
  54. package/src/validation/view-predicates/expanded-element.ts +34 -0
  55. package/src/validation/view-predicates/incoming.ts +19 -0
  56. package/src/validation/view-predicates/index.ts +4 -0
  57. package/src/validation/view-predicates/outgoing.ts +19 -0
  58. package/src/validation/view.ts +26 -0
  59. package/src/view-utils/assignNavigateTo.ts +30 -0
  60. package/src/view-utils/index.ts +3 -0
  61. package/src/view-utils/resolve-extended-views.ts +57 -0
  62. package/src/view-utils/resolve-relative-paths.ts +84 -0
  63. package/dist/Rpc.d.ts +0 -10
  64. package/dist/Rpc.js +0 -98
  65. package/dist/ast.d.ts +0 -133
  66. package/dist/ast.js +0 -267
  67. package/dist/browser/index.d.ts +0 -9
  68. package/dist/browser/index.js +0 -16
  69. package/dist/elementRef.d.ts +0 -12
  70. package/dist/elementRef.js +0 -15
  71. package/dist/generated/ast.d.ts +0 -559
  72. package/dist/generated/ast.js +0 -868
  73. package/dist/generated/grammar.d.ts +0 -7
  74. package/dist/generated/grammar.js +0 -3
  75. package/dist/generated/module.d.ts +0 -14
  76. package/dist/generated/module.js +0 -22
  77. package/dist/index.d.ts +0 -5
  78. package/dist/index.js +0 -2
  79. package/dist/logger.d.ts +0 -12
  80. package/dist/logger.js +0 -51
  81. package/dist/lsp/CodeLensProvider.d.ts +0 -10
  82. package/dist/lsp/CodeLensProvider.js +0 -37
  83. package/dist/lsp/DocumentHighlightProvider.d.ts +0 -10
  84. package/dist/lsp/DocumentHighlightProvider.js +0 -10
  85. package/dist/lsp/DocumentLinkProvider.d.ts +0 -11
  86. package/dist/lsp/DocumentLinkProvider.js +0 -41
  87. package/dist/lsp/DocumentLinkProvider.test.d.ts +0 -2
  88. package/dist/lsp/DocumentLinkProvider.test.js +0 -54
  89. package/dist/lsp/DocumentSymbolProvider.d.ts +0 -22
  90. package/dist/lsp/DocumentSymbolProvider.js +0 -184
  91. package/dist/lsp/HoverProvider.d.ts +0 -10
  92. package/dist/lsp/HoverProvider.js +0 -36
  93. package/dist/lsp/SemanticTokenProvider.d.ts +0 -8
  94. package/dist/lsp/index.d.ts +0 -7
  95. package/dist/lsp/index.js +0 -6
  96. package/dist/model/fqn-computation.d.ts +0 -4
  97. package/dist/model/fqn-computation.js +0 -43
  98. package/dist/model/fqn-index.d.ts +0 -26
  99. package/dist/model/fqn-index.js +0 -114
  100. package/dist/model/index.d.ts +0 -6
  101. package/dist/model/index.js +0 -5
  102. package/dist/model/model-builder.d.ts +0 -20
  103. package/dist/model/model-builder.js +0 -352
  104. package/dist/model/model-locator.d.ts +0 -22
  105. package/dist/model/model-locator.js +0 -119
  106. package/dist/model/model-parser.d.ts +0 -27
  107. package/dist/model/model-parser.js +0 -410
  108. package/dist/model-change/ModelChanges.d.ts +0 -15
  109. package/dist/model-change/ModelChanges.js +0 -100
  110. package/dist/model-change/changeElementStyle.d.ts +0 -15
  111. package/dist/model-change/changeElementStyle.js +0 -141
  112. package/dist/model-change/changeViewLayout.d.ts +0 -13
  113. package/dist/model-change/changeViewLayout.js +0 -30
  114. package/dist/module.d.ts +0 -59
  115. package/dist/module.js +0 -121
  116. package/dist/node/index.d.ts +0 -6
  117. package/dist/node/index.js +0 -13
  118. package/dist/protocol.d.ts +0 -58
  119. package/dist/protocol.js +0 -14
  120. package/dist/references/index.d.ts +0 -3
  121. package/dist/references/index.js +0 -2
  122. package/dist/references/scope-computation.d.ts +0 -11
  123. package/dist/references/scope-computation.js +0 -108
  124. package/dist/references/scope-provider.d.ts +0 -18
  125. package/dist/references/scope-provider.js +0 -136
  126. package/dist/shared/NodeKindProvider.d.ts +0 -16
  127. package/dist/shared/NodeKindProvider.js +0 -58
  128. package/dist/shared/WorkspaceManager.d.ts +0 -17
  129. package/dist/shared/WorkspaceManager.js +0 -29
  130. package/dist/shared/WorkspaceSymbolProvider.d.ts +0 -4
  131. package/dist/shared/WorkspaceSymbolProvider.js +0 -3
  132. package/dist/shared/index.d.ts +0 -4
  133. package/dist/shared/index.js +0 -3
  134. package/dist/test/index.d.ts +0 -2
  135. package/dist/test/index.js +0 -1
  136. package/dist/test/testServices.d.ts +0 -23
  137. package/dist/test/testServices.js +0 -102
  138. package/dist/utils/index.d.ts +0 -2
  139. package/dist/utils/index.js +0 -1
  140. package/dist/utils/printDocs.d.ts +0 -3
  141. package/dist/utils/printDocs.js +0 -1
  142. package/dist/utils/stringHash.d.ts +0 -2
  143. package/dist/utils/stringHash.js +0 -5
  144. package/dist/validation/element.d.ts +0 -6
  145. package/dist/validation/element.js +0 -38
  146. package/dist/validation/index.d.ts +0 -3
  147. package/dist/validation/property-checks.d.ts +0 -5
  148. package/dist/validation/property-checks.js +0 -11
  149. package/dist/validation/relation.d.ts +0 -5
  150. package/dist/validation/relation.js +0 -50
  151. package/dist/validation/specification.d.ts +0 -10
  152. package/dist/validation/specification.js +0 -97
  153. package/dist/validation/view-predicates/custom-element-expr.d.ts +0 -5
  154. package/dist/validation/view-predicates/custom-element-expr.js +0 -16
  155. package/dist/validation/view-predicates/expanded-element.d.ts +0 -5
  156. package/dist/validation/view-predicates/expanded-element.js +0 -28
  157. package/dist/validation/view-predicates/incoming.d.ts +0 -5
  158. package/dist/validation/view-predicates/incoming.js +0 -14
  159. package/dist/validation/view-predicates/index.d.ts +0 -5
  160. package/dist/validation/view-predicates/index.js +0 -4
  161. package/dist/validation/view-predicates/outgoing.d.ts +0 -5
  162. package/dist/validation/view-predicates/outgoing.js +0 -14
  163. package/dist/validation/view.d.ts +0 -5
  164. package/dist/validation/view.js +0 -18
  165. package/dist/view-utils/assignNavigateTo.d.ts +0 -3
  166. package/dist/view-utils/assignNavigateTo.js +0 -23
  167. package/dist/view-utils/index.d.ts +0 -4
  168. package/dist/view-utils/index.js +0 -3
  169. package/dist/view-utils/resolve-extended-views.d.ts +0 -7
  170. package/dist/view-utils/resolve-extended-views.js +0 -41
  171. package/dist/view-utils/resolve-relative-paths.d.ts +0 -3
  172. package/dist/view-utils/resolve-relative-paths.js +0 -76
  173. /package/{dist → src}/reset.d.ts +0 -0
@@ -1,410 +0,0 @@
1
- import { InvalidModelError, invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
2
- import { AstUtils } from "langium";
3
- import { isTruthy } from "remeda";
4
- import stripIndent from "strip-indent";
5
- import {
6
- ast,
7
- checksFromDiagnostics,
8
- cleanParsedModel,
9
- ElementViewOps,
10
- isFqnIndexedDocument,
11
- parseAstOpacityProperty,
12
- resolveRelationPoints,
13
- streamModel,
14
- toAutoLayout,
15
- toElementStyle,
16
- toRelationshipStyleExcludeDefaults
17
- } from "../ast.js";
18
- import { elementRef, getFqnElementRef } from "../elementRef.js";
19
- import { logError, logger, logWarnError } from "../logger.js";
20
- import { stringHash } from "../utils/index.js";
21
- const { getDocument } = AstUtils;
22
- function toSingleLine(str) {
23
- return str ? removeIndent(str).split("\n").join(" ") : void 0;
24
- }
25
- function removeIndent(str) {
26
- return str ? stripIndent(str).trim() : void 0;
27
- }
28
- export class LikeC4ModelParser {
29
- constructor(services) {
30
- this.services = services;
31
- this.fqnIndex = services.likec4.FqnIndex;
32
- logger.debug(`[ModelParser] Created`);
33
- }
34
- fqnIndex;
35
- parse(doc) {
36
- const docs = Array.isArray(doc) ? doc : [doc];
37
- const result = [];
38
- for (const doc2 of docs) {
39
- if (!isFqnIndexedDocument(doc2)) {
40
- logger.warn(`Not a FqnIndexedDocument: ${doc2.uri.toString(true)}`);
41
- continue;
42
- }
43
- try {
44
- result.push(this.parseLikeC4Document(doc2));
45
- } catch (cause) {
46
- logError(new InvalidModelError(`Error parsing document ${doc2.uri.toString()}`, { cause }));
47
- }
48
- }
49
- return result;
50
- }
51
- parseLikeC4Document(_doc) {
52
- const doc = cleanParsedModel(_doc);
53
- this.parseSpecification(doc);
54
- this.parseModel(doc);
55
- this.parseViews(doc);
56
- return doc;
57
- }
58
- parseSpecification(doc) {
59
- const { isValid: isValid2 } = checksFromDiagnostics(doc);
60
- const { parseResult, c4Specification } = doc;
61
- const specifications = parseResult.value.specifications.filter(isValid2);
62
- const element_specs = specifications.flatMap((s) => s.elements.filter(isValid2));
63
- for (const { kind, style } of element_specs) {
64
- try {
65
- const kindName = kind.name;
66
- c4Specification.kinds[kindName] = {
67
- ...c4Specification.kinds[kindName],
68
- ...toElementStyle(style?.props)
69
- };
70
- } catch (e) {
71
- logWarnError(e);
72
- }
73
- }
74
- const relations_specs = specifications.flatMap((s) => s.relationships.filter(isValid2));
75
- for (const { kind, props } of relations_specs) {
76
- try {
77
- const kindName = kind.name;
78
- c4Specification.relationships[kindName] = {
79
- ...c4Specification.relationships[kindName],
80
- ...toRelationshipStyleExcludeDefaults(props)
81
- };
82
- } catch (e) {
83
- logWarnError(e);
84
- }
85
- }
86
- }
87
- parseModel(doc) {
88
- for (const el of streamModel(doc)) {
89
- if (ast.isElement(el)) {
90
- try {
91
- doc.c4Elements.push(this.parseElement(el));
92
- } catch (e) {
93
- logWarnError(e);
94
- }
95
- continue;
96
- }
97
- if (ast.isRelation(el)) {
98
- try {
99
- doc.c4Relations.push(this.parseRelation(el));
100
- } catch (e) {
101
- logWarnError(e);
102
- }
103
- continue;
104
- }
105
- nonexhaustive(el);
106
- }
107
- }
108
- parseElement(astNode) {
109
- const id = this.resolveFqn(astNode);
110
- const kind = astNode.kind.$refText;
111
- const tags = this.convertTags(astNode.body);
112
- const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
113
- const style = toElementStyle(stylePropsAst);
114
- const astPath = this.getAstNodePath(astNode);
115
- let [title, description, technology] = astNode.props ?? [];
116
- const bodyProps = astNode.body?.props.filter(ast.isElementStringProperty) ?? [];
117
- title = toSingleLine(title ?? bodyProps.find((p) => p.key === "title")?.value);
118
- description = removeIndent(description ?? bodyProps.find((p) => p.key === "description")?.value);
119
- technology = toSingleLine(technology ?? bodyProps.find((p) => p.key === "technology")?.value);
120
- const links = astNode.body?.props.filter(ast.isLinkProperty).map((p) => p.value);
121
- return {
122
- id,
123
- kind,
124
- astPath,
125
- title: title ?? astNode.name,
126
- ...tags && { tags },
127
- ...links && isNonEmptyArray(links) && { links },
128
- ...isTruthy(technology) && { technology },
129
- ...isTruthy(description) && { description },
130
- style
131
- };
132
- }
133
- parseRelation(astNode) {
134
- const coupling = resolveRelationPoints(astNode);
135
- const target = this.resolveFqn(coupling.target);
136
- const source = this.resolveFqn(coupling.source);
137
- const tags = this.convertTags(astNode) ?? this.convertTags(astNode.body);
138
- const links = astNode.body?.props.filter(ast.isLinkProperty).map((p) => p.value);
139
- const kind = astNode.kind?.ref?.name;
140
- const astPath = this.getAstNodePath(astNode);
141
- const title = toSingleLine(
142
- astNode.title ?? astNode.body?.props.find((p) => p.key === "title")?.value
143
- ) ?? "";
144
- const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty);
145
- const id = stringHash(
146
- astPath,
147
- source,
148
- target
149
- );
150
- return {
151
- id,
152
- astPath,
153
- source,
154
- target,
155
- title,
156
- ...kind && { kind },
157
- ...tags && { tags },
158
- ...isNonEmptyArray(links) && { links },
159
- ...toRelationshipStyleExcludeDefaults(styleProp?.props)
160
- };
161
- }
162
- parseViews(doc) {
163
- const { isValid: isValid2 } = checksFromDiagnostics(doc);
164
- const views = doc.parseResult.value.views.flatMap((v) => isValid2(v) ? v.views.filter(isValid2) : []);
165
- for (const view of views) {
166
- try {
167
- const v = this.parseElementView(view, isValid2);
168
- doc.c4Views.push(v);
169
- } catch (e) {
170
- logWarnError(e);
171
- }
172
- }
173
- }
174
- parseElementExpr(astNode) {
175
- if (ast.isWildcardExpr(astNode)) {
176
- return {
177
- wildcard: true
178
- };
179
- }
180
- if (ast.isElementKindExpr(astNode)) {
181
- return {
182
- elementKind: astNode.kind.$refText,
183
- isEqual: astNode.isEqual
184
- };
185
- }
186
- if (ast.isElementTagExpr(astNode)) {
187
- let elementTag = astNode.tag.$refText;
188
- if (elementTag.startsWith("#")) {
189
- elementTag = elementTag.slice(1);
190
- }
191
- return {
192
- elementTag,
193
- isEqual: astNode.isEqual
194
- };
195
- }
196
- if (ast.isExpandElementExpr(astNode)) {
197
- const elementNode = elementRef(astNode.parent);
198
- invariant(elementNode, "Element not found " + astNode.parent.$cstNode?.text);
199
- const expanded = this.resolveFqn(elementNode);
200
- return {
201
- expanded
202
- };
203
- }
204
- if (ast.isDescedantsExpr(astNode)) {
205
- const elementNode = elementRef(astNode.parent);
206
- invariant(elementNode, "Element not found " + astNode.parent.$cstNode?.text);
207
- const element = this.resolveFqn(elementNode);
208
- return {
209
- element,
210
- isDescedants: true
211
- };
212
- }
213
- if (ast.isElementRef(astNode)) {
214
- const elementNode = elementRef(astNode);
215
- invariant(elementNode, "Element not found " + astNode.$cstNode?.text);
216
- const element = this.resolveFqn(elementNode);
217
- return {
218
- element
219
- };
220
- }
221
- nonexhaustive(astNode);
222
- }
223
- parseCustomElementExpr(astNode) {
224
- let targetRef;
225
- if (ast.isElementRef(astNode.target)) {
226
- targetRef = astNode.target;
227
- } else if (ast.isExpandElementExpr(astNode.target)) {
228
- targetRef = astNode.target.parent;
229
- } else {
230
- invariant(false, "ElementRef expected as target of custom element");
231
- }
232
- const elementNode = elementRef(targetRef);
233
- invariant(elementNode, "element not found: " + astNode.$cstNode?.text);
234
- const element = this.resolveFqn(elementNode);
235
- const props = astNode.body?.props ?? [];
236
- return props.reduce(
237
- (acc, prop) => {
238
- if (ast.isNavigateToProperty(prop)) {
239
- const viewId = prop.value.view.$refText;
240
- if (isTruthy(viewId)) {
241
- acc.custom.navigateTo = viewId;
242
- }
243
- return acc;
244
- }
245
- if (ast.isElementStringProperty(prop)) {
246
- const value = prop.key === "description" ? removeIndent(prop.value) : toSingleLine(prop.value);
247
- acc.custom[prop.key] = value.trim();
248
- return acc;
249
- }
250
- if (ast.isIconProperty(prop)) {
251
- acc.custom[prop.key] = prop.value;
252
- return acc;
253
- }
254
- if (ast.isColorProperty(prop)) {
255
- acc.custom[prop.key] = prop.value;
256
- return acc;
257
- }
258
- if (ast.isShapeProperty(prop)) {
259
- acc.custom[prop.key] = prop.value;
260
- return acc;
261
- }
262
- if (ast.isBorderProperty(prop)) {
263
- acc.custom[prop.key] = prop.value;
264
- return acc;
265
- }
266
- if (ast.isOpacityProperty(prop)) {
267
- acc.custom[prop.key] = parseAstOpacityProperty(prop);
268
- return acc;
269
- }
270
- nonexhaustive(prop);
271
- },
272
- {
273
- custom: {
274
- element
275
- }
276
- }
277
- );
278
- }
279
- parsePredicateExpr(astNode) {
280
- if (ast.isRelationExpr(astNode)) {
281
- return {
282
- source: this.parseElementExpr(astNode.source),
283
- target: this.parseElementExpr(astNode.target),
284
- isBidirectional: astNode.isBidirectional
285
- };
286
- }
287
- if (ast.isInOutExpr(astNode)) {
288
- return {
289
- inout: this.parseElementExpr(astNode.inout.to)
290
- };
291
- }
292
- if (ast.isOutgoingExpr(astNode)) {
293
- return {
294
- outgoing: this.parseElementExpr(astNode.from)
295
- };
296
- }
297
- if (ast.isIncomingExpr(astNode)) {
298
- return {
299
- incoming: this.parseElementExpr(astNode.to)
300
- };
301
- }
302
- if (ast.isCustomElementExpr(astNode)) {
303
- return this.parseCustomElementExpr(astNode);
304
- }
305
- if (ast.isElementExpr(astNode)) {
306
- return this.parseElementExpr(astNode);
307
- }
308
- nonexhaustive(astNode);
309
- }
310
- parseViewRule(astRule, isValid2) {
311
- if (ast.isIncludePredicate(astRule) || ast.isExcludePredicate(astRule)) {
312
- const exprs = astRule.expressions.flatMap((n) => {
313
- try {
314
- return isValid2(n) ? this.parsePredicateExpr(n) : [];
315
- } catch (e) {
316
- logWarnError(e);
317
- return [];
318
- }
319
- });
320
- return ast.isIncludePredicate(astRule) ? { include: exprs } : { exclude: exprs };
321
- }
322
- if (ast.isViewRuleStyle(astRule)) {
323
- const styleProps = toElementStyle(astRule.styleprops);
324
- return {
325
- targets: astRule.targets.map((n) => this.parseElementExpr(n)),
326
- style: {
327
- ...styleProps
328
- }
329
- };
330
- }
331
- if (ast.isViewRuleAutoLayout(astRule)) {
332
- return {
333
- autoLayout: toAutoLayout(astRule.direction)
334
- };
335
- }
336
- nonexhaustive(astRule);
337
- }
338
- parseElementView(astNode, isValid2) {
339
- const body = astNode.body;
340
- invariant(body, "ElementView body is not defined");
341
- const astPath = this.getAstNodePath(astNode);
342
- let viewOf = null;
343
- if ("viewOf" in astNode) {
344
- const viewOfEl = elementRef(astNode.viewOf);
345
- const _viewOf = viewOfEl && this.resolveFqn(viewOfEl);
346
- if (!_viewOf) {
347
- logger.warn("viewOf is not resolved: " + astNode.$cstNode?.text);
348
- } else {
349
- viewOf = _viewOf;
350
- }
351
- }
352
- let id = astNode.name;
353
- if (!id) {
354
- id = "view_" + stringHash(
355
- getDocument(astNode).uri.toString(),
356
- astPath,
357
- viewOf ?? ""
358
- );
359
- }
360
- const title = toSingleLine(body.props.find((p) => p.key === "title")?.value);
361
- const description = removeIndent(body.props.find((p) => p.key === "description")?.value);
362
- const tags = this.convertTags(body);
363
- const links = body.props.filter(ast.isLinkProperty).map((p) => p.value);
364
- const basic = {
365
- id,
366
- astPath,
367
- ...viewOf && { viewOf },
368
- ...title && { title },
369
- ...description && { description },
370
- ...tags && { tags },
371
- ...isNonEmptyArray(links) && { links },
372
- rules: body.rules.flatMap((n) => {
373
- try {
374
- return isValid2(n) ? this.parseViewRule(n, isValid2) : [];
375
- } catch (e) {
376
- logWarnError(e);
377
- return [];
378
- }
379
- })
380
- };
381
- ElementViewOps.writeId(astNode, basic.id);
382
- if ("extends" in astNode) {
383
- const extendsView = astNode.extends.view.ref;
384
- invariant(extendsView?.name, "view extends is not resolved: " + astNode.$cstNode?.text);
385
- return {
386
- ...basic,
387
- extends: extendsView.name
388
- };
389
- }
390
- return basic;
391
- }
392
- resolveFqn(node) {
393
- if (ast.isExtendElement(node)) {
394
- return getFqnElementRef(node.element);
395
- }
396
- const fqn = this.fqnIndex.getFqn(node);
397
- invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
398
- return fqn;
399
- }
400
- getAstNodePath(node) {
401
- return this.services.workspace.AstNodeLocator.getAstNodePath(node);
402
- }
403
- convertTags(withTags) {
404
- if (!withTags) {
405
- return null;
406
- }
407
- const tags = withTags.tags?.value.flatMap(({ ref }) => ref ? ref.name : []);
408
- return isNonEmptyArray(tags) ? tags : null;
409
- }
410
- }
@@ -1,15 +0,0 @@
1
- import { Location, TextEdit } from 'vscode-languageserver-protocol';
2
- import { type ParsedLikeC4LangiumDocument } from '../ast';
3
- import type { LikeC4Services } from '../module';
4
- import type { ChangeViewRequestParams } from '../protocol';
5
- export declare class LikeC4ModelChanges {
6
- private services;
7
- private locator;
8
- constructor(services: LikeC4Services);
9
- applyChange(changeView: ChangeViewRequestParams): Promise<Location | null>;
10
- protected convertToTextEdit({ viewId, changes }: ChangeViewRequestParams): {
11
- doc: ParsedLikeC4LangiumDocument;
12
- edits: TextEdit[];
13
- };
14
- }
15
- //# sourceMappingURL=ModelChanges.d.ts.map
@@ -1,100 +0,0 @@
1
- import { invariant, nonexhaustive } from "@likec4/core";
2
- import { Range } from "vscode-languageserver-protocol";
3
- import { changeElementStyle } from "./changeElementStyle.js";
4
- import { changeViewLayout } from "./changeViewLayout.js";
5
- function unionRangeOfAllEdits(edits) {
6
- let start = Number.MAX_SAFE_INTEGER;
7
- let end = Number.MIN_SAFE_INTEGER;
8
- let startCharacter = Number.MAX_SAFE_INTEGER;
9
- let endCharacter = Number.MIN_SAFE_INTEGER;
10
- for (const { range } of edits) {
11
- if (range.start.line <= start) {
12
- if (start == range.start.line) {
13
- startCharacter = Math.min(range.start.character, startCharacter);
14
- } else {
15
- start = range.start.line;
16
- startCharacter = range.start.character;
17
- }
18
- }
19
- if (end <= range.end.line) {
20
- if (end == range.end.line) {
21
- endCharacter = Math.max(range.end.character, endCharacter);
22
- } else {
23
- end = range.end.line;
24
- endCharacter = range.end.character;
25
- }
26
- }
27
- }
28
- return Range.create(start, startCharacter, end, endCharacter);
29
- }
30
- export class LikeC4ModelChanges {
31
- constructor(services) {
32
- this.services = services;
33
- this.locator = services.likec4.ModelLocator;
34
- }
35
- locator;
36
- async applyChange(changeView) {
37
- const lspConnection = this.services.shared.lsp.Connection;
38
- invariant(lspConnection, "LSP Connection not available");
39
- let result = null;
40
- await this.services.shared.workspace.WorkspaceLock.write(async () => {
41
- const { doc, edits } = this.convertToTextEdit(changeView);
42
- const textDocument = {
43
- uri: doc.textDocument.uri,
44
- version: doc.textDocument.version
45
- };
46
- if (!edits.length) {
47
- return;
48
- }
49
- const applyResult = await lspConnection.workspace.applyEdit({
50
- label: `LikeC4 - change view ${changeView.viewId}`,
51
- edit: {
52
- changes: {
53
- [textDocument.uri]: edits
54
- }
55
- }
56
- });
57
- if (!applyResult.applied) {
58
- lspConnection.window.showErrorMessage(`Failed to apply changes ${applyResult.failureReason}`);
59
- return;
60
- }
61
- result = {
62
- uri: textDocument.uri,
63
- range: unionRangeOfAllEdits(edits)
64
- };
65
- });
66
- return result;
67
- }
68
- convertToTextEdit({ viewId, changes }) {
69
- const lookup = this.locator.locateViewAst(viewId);
70
- if (!lookup) {
71
- throw new Error(`View not found: ${viewId}`);
72
- }
73
- const edits = [];
74
- for (const change of changes) {
75
- switch (change.op) {
76
- case "change-element-style": {
77
- edits.push(...changeElementStyle(this.services, {
78
- ...lookup,
79
- targets: change.targets,
80
- style: change.style
81
- }));
82
- break;
83
- }
84
- case "change-autolayout": {
85
- edits.push(...changeViewLayout(this.services, {
86
- ...lookup,
87
- layout: change.layout
88
- }));
89
- break;
90
- }
91
- default:
92
- nonexhaustive(change);
93
- }
94
- }
95
- return {
96
- doc: lookup.doc,
97
- edits
98
- };
99
- }
100
- }
@@ -1,15 +0,0 @@
1
- import { type Fqn, type NonEmptyArray } from '@likec4/core';
2
- import { TextEdit } from 'vscode-languageserver-protocol';
3
- import { ast, type ParsedAstElementView, type ParsedLikeC4LangiumDocument } from '../ast';
4
- import type { LikeC4Services } from '../module';
5
- import type { ChangeView } from '../protocol';
6
- type ChangeElementStyleArg = {
7
- view: ParsedAstElementView;
8
- doc: ParsedLikeC4LangiumDocument;
9
- viewAst: ast.ElementView;
10
- targets: NonEmptyArray<Fqn>;
11
- style: ChangeView.ChangeElementStyle['style'];
12
- };
13
- export declare function changeElementStyle(services: LikeC4Services, { view, viewAst, targets, style }: ChangeElementStyleArg): TextEdit[];
14
- export {};
15
- //# sourceMappingURL=changeElementStyle.d.ts.map
@@ -1,141 +0,0 @@
1
- import { invariant, isAncestor, nonNullable } from "@likec4/core";
2
- import { GrammarUtils } from "langium";
3
- import { entries, findLast, isNumber, last } from "remeda";
4
- import { TextEdit } from "vscode-languageserver-protocol";
5
- import { ast } from "../ast.js";
6
- const { findNodeForKeyword, findNodeForProperty } = GrammarUtils;
7
- const asViewStyleRule = (target, style, indent = 0) => {
8
- const indentStr = indent > 0 ? " ".repeat(indent) : "";
9
- return [
10
- indentStr + `style ${target} {`,
11
- ...entries(style).map(([key, value]) => indentStr + ` ${key} ${value}`),
12
- indentStr + `}`
13
- ].join("\n");
14
- };
15
- const isMatchingViewRule = (fqn, index) => (rule) => {
16
- if (!ast.isViewRuleStyle(rule)) {
17
- return false;
18
- }
19
- const [target, ...rest] = rule.targets;
20
- if (!target || rest.length > 0 || !ast.isElementRef(target)) {
21
- return false;
22
- }
23
- const ref = target.el.ref;
24
- const _fqn = ref ? index.getFqn(ref) : null;
25
- return _fqn === fqn;
26
- };
27
- export function changeElementStyle(services, {
28
- view,
29
- viewAst,
30
- targets,
31
- style
32
- }) {
33
- const viewCstNode = viewAst.$cstNode;
34
- invariant(viewCstNode, "viewCstNode");
35
- const insertPos = last(viewAst.body.rules)?.$cstNode?.range.end ?? last(viewAst.body.props)?.$cstNode?.range.end ?? viewAst.body.$cstNode?.range.start;
36
- invariant(insertPos, "insertPos is not defined");
37
- const indent = viewCstNode.range.start.character + 2;
38
- const fqnIndex = services.likec4.FqnIndex;
39
- const styleRules = viewAst.body.rules.filter(ast.isViewRuleStyle);
40
- const viewOf = view.viewOf;
41
- const existing = [];
42
- const insert = [];
43
- targets.forEach((target) => {
44
- const rule = findLast(styleRules, isMatchingViewRule(target, fqnIndex));
45
- const fqn = viewOf && isAncestor(viewOf, target) ? target.substring(viewOf.length + 1) : target;
46
- if (rule) {
47
- existing.push({ fqn, rule });
48
- } else {
49
- insert.push({ fqn });
50
- }
51
- });
52
- const modifiedRange = {
53
- start: insertPos,
54
- end: insertPos
55
- };
56
- const includeRange = (range) => {
57
- if (range.start.line <= modifiedRange.start.line) {
58
- if (range.start.line == modifiedRange.start.line) {
59
- modifiedRange.start.character = Math.min(range.start.character, modifiedRange.start.character);
60
- } else {
61
- modifiedRange.start.line = range.start.line;
62
- modifiedRange.start.character = range.start.character;
63
- }
64
- }
65
- if (range.end.line >= modifiedRange.end.line) {
66
- if (range.end.line == modifiedRange.end.line) {
67
- modifiedRange.end.character = Math.max(range.end.character, modifiedRange.end.character);
68
- } else {
69
- modifiedRange.end.line = range.end.line;
70
- modifiedRange.end.character = range.end.character;
71
- }
72
- }
73
- };
74
- const edits = [];
75
- if (insert.length > 0) {
76
- const linesToInsert = [
77
- "",
78
- ...insert.map(({ fqn }) => asViewStyleRule(fqn, style, indent))
79
- ];
80
- edits.push(
81
- TextEdit.insert(
82
- insertPos,
83
- linesToInsert.join("\n")
84
- )
85
- );
86
- modifiedRange.start = {
87
- line: insertPos.line + 1,
88
- character: indent
89
- };
90
- modifiedRange.end = {
91
- line: insertPos.line + linesToInsert.length,
92
- character: indent + (last(linesToInsert)?.length ?? 0) + 1
93
- };
94
- }
95
- if (existing.length > 0) {
96
- for (const { rule } of existing) {
97
- const ruleCstNode = rule.$cstNode;
98
- invariant(ruleCstNode, "RuleCstNode not found");
99
- for (const [key, _value] of entries.strict(style)) {
100
- const value = isNumber(_value) ? _value.toString() + "%" : _value;
101
- const ruleProp = rule.styleprops.find((p) => p.key === key);
102
- if (ruleProp && ruleProp.$cstNode) {
103
- const { range: { start, end } } = nonNullable(
104
- findNodeForProperty(ruleProp.$cstNode, "value"),
105
- "cant find value cst node"
106
- );
107
- includeRange({
108
- start,
109
- end: {
110
- line: end.line,
111
- character: start.character + value.length + 1
112
- }
113
- });
114
- edits.push(TextEdit.replace({ start, end }, value));
115
- continue;
116
- }
117
- const insertPos2 = findNodeForKeyword(ruleCstNode, "{")?.range.end;
118
- invariant(insertPos2, "Opening brace not found");
119
- const indentStr = " ".repeat(2 + ruleCstNode.range.start.character);
120
- const insertKeyValue = indentStr + key + " " + value;
121
- edits.push(
122
- TextEdit.insert(
123
- insertPos2,
124
- "\n" + insertKeyValue
125
- )
126
- );
127
- includeRange({
128
- start: {
129
- line: insertPos2.line + 1,
130
- character: indentStr.length + 1
131
- },
132
- end: {
133
- line: insertPos2.line + 1,
134
- character: insertKeyValue.length + 1
135
- }
136
- });
137
- }
138
- }
139
- }
140
- return edits;
141
- }
@@ -1,13 +0,0 @@
1
- import { type AutoLayoutDirection } from '@likec4/core';
2
- import { TextEdit } from 'vscode-languageserver-protocol';
3
- import { ast, type ParsedAstElementView, type ParsedLikeC4LangiumDocument } from '../ast';
4
- import type { LikeC4Services } from '../module';
5
- type ChangeViewLayoutArg = {
6
- view: ParsedAstElementView;
7
- doc: ParsedLikeC4LangiumDocument;
8
- viewAst: ast.ElementView;
9
- layout: AutoLayoutDirection;
10
- };
11
- export declare function changeViewLayout(services: LikeC4Services, { view, viewAst, layout }: ChangeViewLayoutArg): TextEdit[];
12
- export {};
13
- //# sourceMappingURL=changeViewLayout.d.ts.map