@likec4/language-server 0.60.2 → 1.0.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Rpc.d.ts +4 -1
- package/dist/Rpc.js +69 -44
- package/dist/ast.d.ts +1 -0
- package/dist/ast.js +20 -0
- package/dist/elementRef.d.ts +0 -1
- package/dist/lsp/index.d.ts +1 -1
- package/dist/lsp/index.js +1 -1
- package/dist/model/fqn-computation.js +2 -2
- package/dist/model/model-builder.d.ts +2 -2
- package/dist/model/model-builder.js +14 -8
- package/dist/model/model-locator.d.ts +6 -1
- package/dist/model/model-locator.js +31 -20
- package/dist/model/model-parser.js +19 -17
- package/dist/model-change/ModelChanges.d.ts +15 -0
- package/dist/model-change/ModelChanges.js +94 -0
- package/dist/model-change/changeViewLayout.d.ts +13 -0
- package/dist/model-change/changeViewLayout.js +30 -0
- package/dist/model-change/changeViewStyle.d.ts +15 -0
- package/dist/model-change/changeViewStyle.js +123 -0
- package/dist/module.d.ts +2 -0
- package/dist/module.js +2 -0
- package/dist/protocol.d.ts +34 -6
- package/dist/protocol.js +6 -3
- package/dist/references/scope-computation.d.ts +1 -1
- package/dist/reset.d.ts +2 -0
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/test/testServices.d.ts +1 -1
- package/dist/test/testServices.js +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/stringHash.d.ts +2 -0
- package/dist/utils/stringHash.js +5 -0
- package/dist/validation/view-predicates/incoming.js +2 -2
- package/dist/validation/view-predicates/index.d.ts +1 -1
- package/dist/validation/view-predicates/index.js +1 -1
- package/dist/validation/view-predicates/outgoing.js +1 -1
- package/dist/view-utils/resolve-relative-paths.js +1 -1
- package/package.json +20 -11
package/dist/Rpc.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { LikeC4Services } from './module';
|
|
2
|
-
|
|
2
|
+
import { Disposable } from 'langium';
|
|
3
|
+
export declare class Rpc implements Disposable {
|
|
3
4
|
private services;
|
|
5
|
+
private disposables;
|
|
4
6
|
constructor(services: LikeC4Services);
|
|
5
7
|
init(): void;
|
|
8
|
+
dispose(): void;
|
|
6
9
|
}
|
|
7
10
|
//# sourceMappingURL=Rpc.d.ts.map
|
package/dist/Rpc.js
CHANGED
|
@@ -3,14 +3,24 @@ import { logError, logger } from "./logger.js";
|
|
|
3
3
|
import { nonexhaustive } from "@likec4/core";
|
|
4
4
|
import { URI, UriUtils } from "langium";
|
|
5
5
|
import { isLikeC4LangiumDocument } from "./ast.js";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
buildDocuments,
|
|
8
|
+
changeView,
|
|
9
|
+
computeView,
|
|
10
|
+
fetchComputedModel,
|
|
11
|
+
fetchModel,
|
|
12
|
+
locate,
|
|
13
|
+
onDidChangeModel
|
|
14
|
+
} from "./protocol.js";
|
|
7
15
|
export class Rpc {
|
|
8
16
|
constructor(services) {
|
|
9
17
|
this.services = services;
|
|
10
18
|
}
|
|
19
|
+
disposables = [];
|
|
11
20
|
init() {
|
|
12
21
|
const modelBuilder = this.services.likec4.ModelBuilder;
|
|
13
22
|
const modelLocator = this.services.likec4.ModelLocator;
|
|
23
|
+
const modelEditor = this.services.likec4.ModelChanges;
|
|
14
24
|
const connection = this.services.shared.lsp.Connection;
|
|
15
25
|
if (!connection) {
|
|
16
26
|
logger.info(`[ServerRpc] no connection, not initializing`);
|
|
@@ -19,51 +29,66 @@ export class Rpc {
|
|
|
19
29
|
logger.info(`[ServerRpc] init`);
|
|
20
30
|
const LangiumDocuments = this.services.shared.workspace.LangiumDocuments;
|
|
21
31
|
const DocumentBuilder = this.services.shared.workspace.DocumentBuilder;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
32
|
+
this.disposables.push(
|
|
33
|
+
modelBuilder.onModelParsed(
|
|
34
|
+
debounceFunction(
|
|
35
|
+
() => void connection.sendNotification(onDidChangeModel, "").catch(logError),
|
|
36
|
+
{
|
|
37
|
+
before: true,
|
|
38
|
+
after: true,
|
|
39
|
+
wait: 250,
|
|
40
|
+
maxWait: 1e3
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
),
|
|
44
|
+
connection.onRequest(fetchComputedModel, async (cancelToken) => {
|
|
45
|
+
const model = await modelBuilder.buildComputedModel(cancelToken);
|
|
46
|
+
return { model };
|
|
47
|
+
}),
|
|
48
|
+
connection.onRequest(fetchModel, async (cancelToken) => {
|
|
49
|
+
const model = await modelBuilder.buildModel(cancelToken);
|
|
50
|
+
return { model };
|
|
51
|
+
}),
|
|
52
|
+
connection.onRequest(computeView, async ({ viewId }, cancelToken) => {
|
|
53
|
+
const view = await modelBuilder.computeView(viewId, cancelToken);
|
|
54
|
+
return { view };
|
|
55
|
+
}),
|
|
56
|
+
connection.onRequest(buildDocuments, async ({ docs }, cancelToken) => {
|
|
57
|
+
const changed = docs.map((d) => URI.parse(d));
|
|
58
|
+
const notChanged = (uri) => changed.every((c) => !UriUtils.equals(c, uri));
|
|
59
|
+
const deleted = LangiumDocuments.all.filter((d) => isLikeC4LangiumDocument(d) && notChanged(d.uri)).map((d) => d.uri).toArray();
|
|
60
|
+
logger.debug(
|
|
61
|
+
`[ServerRpc] received request to build:
|
|
51
62
|
changed (total ${changed.length}):${docs.map((d) => "\n - " + d).join("")}
|
|
52
63
|
deleted (total ${deleted.length}):${deleted.map((d) => "\n - " + d.toString()).join("\n")}`
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
);
|
|
65
|
+
await DocumentBuilder.update(changed, deleted, cancelToken);
|
|
66
|
+
}),
|
|
67
|
+
connection.onRequest(locate, (params) => {
|
|
68
|
+
if ("element" in params) {
|
|
69
|
+
return modelLocator.locateElement(params.element, params.property ?? "name");
|
|
70
|
+
}
|
|
71
|
+
if ("relation" in params) {
|
|
72
|
+
return modelLocator.locateRelation(params.relation);
|
|
73
|
+
}
|
|
74
|
+
if ("view" in params) {
|
|
75
|
+
return modelLocator.locateView(params.view);
|
|
76
|
+
}
|
|
77
|
+
nonexhaustive(params);
|
|
78
|
+
}),
|
|
79
|
+
connection.onRequest(changeView, async (request, _cancelToken) => {
|
|
80
|
+
return await modelEditor.applyChange(request);
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
dispose() {
|
|
85
|
+
let item;
|
|
86
|
+
while (item = this.disposables.pop()) {
|
|
87
|
+
try {
|
|
88
|
+
item.dispose();
|
|
89
|
+
} catch (e) {
|
|
90
|
+
logError(e);
|
|
65
91
|
}
|
|
66
|
-
|
|
67
|
-
});
|
|
92
|
+
}
|
|
68
93
|
}
|
|
69
94
|
}
|
package/dist/ast.d.ts
CHANGED
|
@@ -136,4 +136,5 @@ export declare function toRelationshipStyleExcludeDefaults(props?: ast.Specifica
|
|
|
136
136
|
color?: "amber" | "blue" | "green" | "indigo" | "muted" | "primary" | "red" | "secondary" | "sky" | "slate";
|
|
137
137
|
};
|
|
138
138
|
export declare function toAutoLayout(direction: ast.ViewLayoutDirection): c4.ViewRuleAutoLayout['autoLayout'];
|
|
139
|
+
export declare function toAstViewLayoutDirection(c4: c4.ViewRuleAutoLayout['autoLayout']): ast.ViewLayoutDirection;
|
|
139
140
|
//# sourceMappingURL=ast.d.ts.map
|
package/dist/ast.js
CHANGED
|
@@ -236,5 +236,25 @@ export function toAutoLayout(direction) {
|
|
|
236
236
|
case "RightLeft": {
|
|
237
237
|
return "RL";
|
|
238
238
|
}
|
|
239
|
+
default:
|
|
240
|
+
nonexhaustive(direction);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
export function toAstViewLayoutDirection(c4) {
|
|
244
|
+
switch (c4) {
|
|
245
|
+
case "TB": {
|
|
246
|
+
return "TopBottom";
|
|
247
|
+
}
|
|
248
|
+
case "BT": {
|
|
249
|
+
return "BottomTop";
|
|
250
|
+
}
|
|
251
|
+
case "LR": {
|
|
252
|
+
return "LeftRight";
|
|
253
|
+
}
|
|
254
|
+
case "RL": {
|
|
255
|
+
return "RightLeft";
|
|
256
|
+
}
|
|
257
|
+
default:
|
|
258
|
+
nonexhaustive(c4);
|
|
239
259
|
}
|
|
240
260
|
}
|
package/dist/elementRef.d.ts
CHANGED
package/dist/lsp/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './CodeLensProvider';
|
|
2
|
+
export * from './DocumentHighlightProvider';
|
|
2
3
|
export * from './DocumentLinkProvider';
|
|
3
4
|
export * from './DocumentSymbolProvider';
|
|
4
5
|
export * from './HoverProvider';
|
|
5
6
|
export * from './SemanticTokenProvider';
|
|
6
|
-
export * from './DocumentHighlightProvider';
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/lsp/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from "./CodeLensProvider.js";
|
|
2
|
+
export * from "./DocumentHighlightProvider.js";
|
|
2
3
|
export * from "./DocumentLinkProvider.js";
|
|
3
4
|
export * from "./DocumentSymbolProvider.js";
|
|
4
5
|
export * from "./HoverProvider.js";
|
|
5
6
|
export * from "./SemanticTokenProvider.js";
|
|
6
|
-
export * from "./DocumentHighlightProvider.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AsFqn, nonexhaustive } from "@likec4/core";
|
|
2
2
|
import { MultiMap } from "langium";
|
|
3
|
-
import { isEmpty, isNil } from "remeda";
|
|
4
|
-
import {
|
|
3
|
+
import { isEmpty, isNullish as isNil } from "remeda";
|
|
4
|
+
import { ast, ElementOps } from "../ast.js";
|
|
5
5
|
import { getFqnElementRef } from "../elementRef.js";
|
|
6
6
|
export function computeDocumentFqn(document, services) {
|
|
7
7
|
const c4fqns = document.c4fqns = new MultiMap();
|
|
@@ -8,9 +8,9 @@ export declare class LikeC4ModelBuilder {
|
|
|
8
8
|
private langiumDocuments;
|
|
9
9
|
private listeners;
|
|
10
10
|
constructor(services: LikeC4Services);
|
|
11
|
-
buildRawModel(cancelToken?: CancellationToken): Promise<c4.LikeC4RawModel | null>;
|
|
12
|
-
private previousViews;
|
|
13
11
|
buildModel(cancelToken?: CancellationToken): Promise<c4.LikeC4Model | null>;
|
|
12
|
+
private previousViews;
|
|
13
|
+
buildComputedModel(cancelToken?: CancellationToken): Promise<c4.LikeC4ComputedModel | null>;
|
|
14
14
|
computeView(viewId: ViewID, cancelToken?: CancellationToken): Promise<c4.ComputedView | null>;
|
|
15
15
|
onModelParsed(callback: ModelParsedListener): Disposable;
|
|
16
16
|
private documents;
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
parentFqn
|
|
5
5
|
} from "@likec4/core";
|
|
6
6
|
import { computeView, LikeC4ModelGraph } from "@likec4/graph";
|
|
7
|
+
import { deepEqual as eq } from "fast-equals";
|
|
7
8
|
import { DocumentState, interruptAndCheck } from "langium";
|
|
8
9
|
import * as R from "remeda";
|
|
9
10
|
import { Disposable } from "vscode-languageserver";
|
|
@@ -183,7 +184,8 @@ ${printDocs(docs)}`);
|
|
|
183
184
|
}
|
|
184
185
|
langiumDocuments;
|
|
185
186
|
listeners = [];
|
|
186
|
-
async
|
|
187
|
+
async buildModel(cancelToken) {
|
|
188
|
+
await this.services.shared.workspace.DocumentBuilder.waitUntil(DocumentState.Validated, cancelToken);
|
|
187
189
|
return await this.services.shared.workspace.WorkspaceLock.read(async () => {
|
|
188
190
|
if (cancelToken) {
|
|
189
191
|
await interruptAndCheck(cancelToken);
|
|
@@ -202,11 +204,14 @@ ${printDocs(docs)}`);
|
|
|
202
204
|
});
|
|
203
205
|
}
|
|
204
206
|
previousViews = {};
|
|
205
|
-
async
|
|
206
|
-
const model = await this.
|
|
207
|
+
async buildComputedModel(cancelToken) {
|
|
208
|
+
const model = await this.buildModel(cancelToken);
|
|
207
209
|
if (!model) {
|
|
208
210
|
return null;
|
|
209
211
|
}
|
|
212
|
+
if (cancelToken) {
|
|
213
|
+
await interruptAndCheck(cancelToken);
|
|
214
|
+
}
|
|
210
215
|
const cache = this.services.WorkspaceCache;
|
|
211
216
|
const viewsCache = this.services.WorkspaceCache;
|
|
212
217
|
return cache.get(MODEL_CACHE, () => {
|
|
@@ -223,7 +228,7 @@ ${printDocs(docs)}`);
|
|
|
223
228
|
assignNavigateTo(allViews);
|
|
224
229
|
const views = R.mapToObj(allViews, (v) => {
|
|
225
230
|
const previous = this.previousViews[v.id];
|
|
226
|
-
const view = previous &&
|
|
231
|
+
const view = previous && eq(v, previous) ? previous : v;
|
|
227
232
|
viewsCache.set(computedViewKey(v.id), view);
|
|
228
233
|
return [v.id, view];
|
|
229
234
|
});
|
|
@@ -236,12 +241,15 @@ ${printDocs(docs)}`);
|
|
|
236
241
|
});
|
|
237
242
|
}
|
|
238
243
|
async computeView(viewId, cancelToken) {
|
|
239
|
-
const model = await this.
|
|
244
|
+
const model = await this.buildModel(cancelToken);
|
|
240
245
|
const view = model?.views[viewId];
|
|
241
246
|
if (!view) {
|
|
242
247
|
logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
|
|
243
248
|
return null;
|
|
244
249
|
}
|
|
250
|
+
if (cancelToken) {
|
|
251
|
+
await interruptAndCheck(cancelToken);
|
|
252
|
+
}
|
|
245
253
|
const cache = this.services.WorkspaceCache;
|
|
246
254
|
return cache.get(computedViewKey(viewId), () => {
|
|
247
255
|
const index = new LikeC4ModelGraph(model);
|
|
@@ -263,9 +271,7 @@ ${printDocs(docs)}`);
|
|
|
263
271
|
}
|
|
264
272
|
});
|
|
265
273
|
const previous = this.previousViews[viewId];
|
|
266
|
-
|
|
267
|
-
computedView = R.equals(computedView, previous) ? previous : computedView;
|
|
268
|
-
}
|
|
274
|
+
computedView = previous && eq(computedView, previous) ? previous : computedView;
|
|
269
275
|
this.previousViews[viewId] = computedView;
|
|
270
276
|
return computedView;
|
|
271
277
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { likec4 as c4 } from '@likec4/core';
|
|
2
2
|
import type { Location } from 'vscode-languageserver-protocol';
|
|
3
|
-
import type { ParsedAstElement } from '../ast';
|
|
3
|
+
import type { ParsedAstElement, ParsedLikeC4LangiumDocument } from '../ast';
|
|
4
4
|
import { ast } from '../ast';
|
|
5
5
|
import type { LikeC4Services } from '../module';
|
|
6
6
|
export declare class LikeC4ModelLocator {
|
|
@@ -12,6 +12,11 @@ export declare class LikeC4ModelLocator {
|
|
|
12
12
|
getParsedElement(astNode: ast.Element): ParsedAstElement | null;
|
|
13
13
|
locateElement(fqn: c4.Fqn, property?: string): Location | null;
|
|
14
14
|
locateRelation(relationId: c4.RelationID): Location | null;
|
|
15
|
+
locateViewAst(viewId: c4.ViewID): {
|
|
16
|
+
doc: ParsedLikeC4LangiumDocument;
|
|
17
|
+
view: import("../ast").ParsedAstElementView;
|
|
18
|
+
viewAst: ast.ElementView;
|
|
19
|
+
} | null;
|
|
15
20
|
locateView(viewId: c4.ViewID): Location | null;
|
|
16
21
|
}
|
|
17
22
|
//# sourceMappingURL=model-locator.d.ts.map
|
|
@@ -73,36 +73,47 @@ export class LikeC4ModelLocator {
|
|
|
73
73
|
}
|
|
74
74
|
return null;
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
locateViewAst(viewId) {
|
|
77
77
|
for (const doc of this.documents()) {
|
|
78
78
|
const view = doc.c4Views.find((r) => r.id === viewId);
|
|
79
79
|
if (!view) {
|
|
80
80
|
continue;
|
|
81
81
|
}
|
|
82
|
-
const
|
|
82
|
+
const viewAst = this.services.workspace.AstNodeLocator.getAstNode(
|
|
83
83
|
doc.parseResult.value,
|
|
84
84
|
view.astPath
|
|
85
85
|
);
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
} else if ("viewOf" in node) {
|
|
93
|
-
targetNode = findNodeForProperty(node.$cstNode, "viewOf") ?? targetNode;
|
|
94
|
-
}
|
|
95
|
-
if (!targetNode) {
|
|
96
|
-
return null;
|
|
86
|
+
if (ast.isElementView(viewAst)) {
|
|
87
|
+
return {
|
|
88
|
+
doc,
|
|
89
|
+
view,
|
|
90
|
+
viewAst
|
|
91
|
+
};
|
|
97
92
|
}
|
|
98
|
-
return {
|
|
99
|
-
uri: doc.uri.toString(),
|
|
100
|
-
range: {
|
|
101
|
-
start: targetNode.range.start,
|
|
102
|
-
end: targetNode.range.start
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
93
|
}
|
|
106
94
|
return null;
|
|
107
95
|
}
|
|
96
|
+
locateView(viewId) {
|
|
97
|
+
const res = this.locateViewAst(viewId);
|
|
98
|
+
if (!res) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const node = res.viewAst;
|
|
102
|
+
let targetNode = node.$cstNode;
|
|
103
|
+
if (node.name) {
|
|
104
|
+
targetNode = findNodeForProperty(node.$cstNode, "name") ?? targetNode;
|
|
105
|
+
} else if ("viewOf" in node) {
|
|
106
|
+
targetNode = findNodeForProperty(node.$cstNode, "viewOf") ?? targetNode;
|
|
107
|
+
}
|
|
108
|
+
if (!targetNode) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
uri: res.doc.uri.toString(),
|
|
113
|
+
range: {
|
|
114
|
+
start: targetNode.range.start,
|
|
115
|
+
end: targetNode.range.start
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
108
119
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { InvalidModelError, invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
|
|
2
2
|
import { AstUtils } from "langium";
|
|
3
|
-
import objectHash from "object-hash";
|
|
4
3
|
import { isTruthy } from "remeda";
|
|
5
4
|
import stripIndent from "strip-indent";
|
|
6
5
|
import {
|
|
@@ -18,6 +17,7 @@ import {
|
|
|
18
17
|
} from "../ast.js";
|
|
19
18
|
import { elementRef, getFqnElementRef } from "../elementRef.js";
|
|
20
19
|
import { logError, logger, logWarnError } from "../logger.js";
|
|
20
|
+
import { stringHash } from "../utils/index.js";
|
|
21
21
|
const { getDocument } = AstUtils;
|
|
22
22
|
function toSingleLine(str) {
|
|
23
23
|
return str ? removeIndent(str).split("\n").join(" ") : void 0;
|
|
@@ -142,11 +142,11 @@ export class LikeC4ModelParser {
|
|
|
142
142
|
astNode.title ?? astNode.body?.props.find((p) => p.key === "title")?.value
|
|
143
143
|
) ?? "";
|
|
144
144
|
const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty);
|
|
145
|
-
const id =
|
|
145
|
+
const id = stringHash(
|
|
146
146
|
astPath,
|
|
147
147
|
source,
|
|
148
148
|
target
|
|
149
|
-
|
|
149
|
+
);
|
|
150
150
|
return {
|
|
151
151
|
id,
|
|
152
152
|
astPath,
|
|
@@ -311,13 +311,23 @@ export class LikeC4ModelParser {
|
|
|
311
311
|
const body = astNode.body;
|
|
312
312
|
invariant(body, "ElementView body is not defined");
|
|
313
313
|
const astPath = this.getAstNodePath(astNode);
|
|
314
|
+
let viewOf = null;
|
|
315
|
+
if ("viewOf" in astNode) {
|
|
316
|
+
const viewOfEl = elementRef(astNode.viewOf);
|
|
317
|
+
const _viewOf = viewOfEl && this.resolveFqn(viewOfEl);
|
|
318
|
+
if (!_viewOf) {
|
|
319
|
+
logger.warn("viewOf is not resolved: " + astNode.$cstNode?.text);
|
|
320
|
+
} else {
|
|
321
|
+
viewOf = _viewOf;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
314
324
|
let id = astNode.name;
|
|
315
325
|
if (!id) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
326
|
+
id = "view_" + stringHash(
|
|
327
|
+
getDocument(astNode).uri.toString(),
|
|
328
|
+
astPath,
|
|
329
|
+
viewOf ?? ""
|
|
330
|
+
);
|
|
321
331
|
}
|
|
322
332
|
const title = body.props.find((p) => p.key === "title")?.value;
|
|
323
333
|
const description = body.props.find((p) => p.key === "description")?.value;
|
|
@@ -326,6 +336,7 @@ export class LikeC4ModelParser {
|
|
|
326
336
|
const basic = {
|
|
327
337
|
id,
|
|
328
338
|
astPath,
|
|
339
|
+
...viewOf && { viewOf },
|
|
329
340
|
...title && { title },
|
|
330
341
|
...description && { description },
|
|
331
342
|
...tags && { tags },
|
|
@@ -340,15 +351,6 @@ export class LikeC4ModelParser {
|
|
|
340
351
|
})
|
|
341
352
|
};
|
|
342
353
|
ElementViewOps.writeId(astNode, basic.id);
|
|
343
|
-
if ("viewOf" in astNode) {
|
|
344
|
-
const viewOfEl = elementRef(astNode.viewOf);
|
|
345
|
-
const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
|
|
346
|
-
invariant(viewOf, " viewOf is not resolved: " + astNode.$cstNode?.text);
|
|
347
|
-
return {
|
|
348
|
-
...basic,
|
|
349
|
-
viewOf
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
354
|
if ("extends" in astNode) {
|
|
353
355
|
const extendsView = astNode.extends.view.ref;
|
|
354
356
|
invariant(extendsView?.name, "view extends is not resolved: " + astNode.$cstNode?.text);
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { invariant, nonexhaustive } from "@likec4/core";
|
|
2
|
+
import { Range, TextDocumentEdit } from "vscode-languageserver-protocol";
|
|
3
|
+
import { changeViewLayout } from "./changeViewLayout.js";
|
|
4
|
+
import { changeViewStyle } from "./changeViewStyle.js";
|
|
5
|
+
function unionRangeOfAllEdits(edits) {
|
|
6
|
+
let start = Number.MAX_SAFE_INTEGER;
|
|
7
|
+
let end = Number.MIN_SAFE_INTEGER;
|
|
8
|
+
for (const edit of edits) {
|
|
9
|
+
start = Math.min(start, edit.range.start.line);
|
|
10
|
+
end = Math.max(end, edit.range.end.line);
|
|
11
|
+
}
|
|
12
|
+
return Range.create(start, 0, end, 0);
|
|
13
|
+
}
|
|
14
|
+
export class LikeC4ModelChanges {
|
|
15
|
+
constructor(services) {
|
|
16
|
+
this.services = services;
|
|
17
|
+
this.locator = services.likec4.ModelLocator;
|
|
18
|
+
}
|
|
19
|
+
locator;
|
|
20
|
+
async applyChange(changeView) {
|
|
21
|
+
const lspConnection = this.services.shared.lsp.Connection;
|
|
22
|
+
invariant(lspConnection, "LSP Connection not available");
|
|
23
|
+
let result = null;
|
|
24
|
+
await this.services.shared.workspace.WorkspaceLock.write(async () => {
|
|
25
|
+
const { doc, edits } = this.convertToTextEdit(changeView);
|
|
26
|
+
const textDocument = {
|
|
27
|
+
uri: doc.textDocument.uri,
|
|
28
|
+
version: doc.textDocument.version
|
|
29
|
+
};
|
|
30
|
+
if (!edits.length) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const applyResult = await lspConnection.workspace.applyEdit({
|
|
34
|
+
label: `LikeC4 - change view ${changeView.viewId}`,
|
|
35
|
+
edit: {
|
|
36
|
+
documentChanges: [
|
|
37
|
+
TextDocumentEdit.create(textDocument, edits)
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (!applyResult.applied) {
|
|
42
|
+
lspConnection.window.showErrorMessage(`Failed to apply changes${applyResult.failureReason}`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
result = {
|
|
46
|
+
uri: textDocument.uri,
|
|
47
|
+
range: unionRangeOfAllEdits(edits)
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
convertToTextEdit({ viewId, changes }) {
|
|
53
|
+
const lookup = this.locator.locateViewAst(viewId);
|
|
54
|
+
if (!lookup) {
|
|
55
|
+
throw new Error(`View not found: ${viewId}`);
|
|
56
|
+
}
|
|
57
|
+
const edits = [];
|
|
58
|
+
for (const change of changes) {
|
|
59
|
+
switch (change.op) {
|
|
60
|
+
case "change-color": {
|
|
61
|
+
edits.push(...changeViewStyle(this.services, {
|
|
62
|
+
...lookup,
|
|
63
|
+
targets: change.targets,
|
|
64
|
+
key: "color",
|
|
65
|
+
value: change.color
|
|
66
|
+
}));
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "change-shape": {
|
|
70
|
+
edits.push(...changeViewStyle(this.services, {
|
|
71
|
+
...lookup,
|
|
72
|
+
targets: change.targets,
|
|
73
|
+
key: "shape",
|
|
74
|
+
value: change.shape
|
|
75
|
+
}));
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
case "change-autolayout": {
|
|
79
|
+
edits.push(...changeViewLayout(this.services, {
|
|
80
|
+
...lookup,
|
|
81
|
+
layout: change.layout
|
|
82
|
+
}));
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
default:
|
|
86
|
+
nonexhaustive(change);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
doc: lookup.doc,
|
|
91
|
+
edits
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { invariant } from "@likec4/core";
|
|
2
|
+
import { GrammarUtils } from "langium";
|
|
3
|
+
import { last } from "remeda";
|
|
4
|
+
import { TextEdit } from "vscode-languageserver-protocol";
|
|
5
|
+
import { ast, toAstViewLayoutDirection } from "../ast.js";
|
|
6
|
+
const { findNodeForProperty } = GrammarUtils;
|
|
7
|
+
export function changeViewLayout(services, {
|
|
8
|
+
view,
|
|
9
|
+
viewAst,
|
|
10
|
+
layout
|
|
11
|
+
}) {
|
|
12
|
+
const viewCstNode = viewAst.$cstNode;
|
|
13
|
+
invariant(viewCstNode, "viewCstNode");
|
|
14
|
+
const newlayout = toAstViewLayoutDirection(layout);
|
|
15
|
+
const existingRule = viewAst.body.rules.findLast(ast.isViewRuleAutoLayout);
|
|
16
|
+
if (existingRule && existingRule.$cstNode) {
|
|
17
|
+
const directionCstNode = findNodeForProperty(existingRule.$cstNode, "direction");
|
|
18
|
+
if (directionCstNode) {
|
|
19
|
+
return [TextEdit.replace(directionCstNode.range, newlayout)];
|
|
20
|
+
}
|
|
21
|
+
return [TextEdit.replace(existingRule.$cstNode.range, `autoLayout ${newlayout}`)];
|
|
22
|
+
}
|
|
23
|
+
const insertPos = last(viewAst.body.rules)?.$cstNode?.range.end ?? last(viewAst.body.props)?.$cstNode?.range.end ?? viewAst.body.$cstNode?.range.start;
|
|
24
|
+
invariant(insertPos, "insertPos is not defined");
|
|
25
|
+
const indent = " ".repeat(2 + viewCstNode.range.start.character);
|
|
26
|
+
const insert = `
|
|
27
|
+
|
|
28
|
+
${indent}autoLayout ${newlayout}`;
|
|
29
|
+
return [TextEdit.insert(insertPos, insert)];
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
type ChangeViewStyleArg = {
|
|
6
|
+
view: ParsedAstElementView;
|
|
7
|
+
doc: ParsedLikeC4LangiumDocument;
|
|
8
|
+
viewAst: ast.ElementView;
|
|
9
|
+
key: string;
|
|
10
|
+
value: string;
|
|
11
|
+
targets: NonEmptyArray<Fqn>;
|
|
12
|
+
};
|
|
13
|
+
export declare function changeViewStyle(services: LikeC4Services, { view, viewAst, targets, key, value }: ChangeViewStyleArg): TextEdit[];
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=changeViewStyle.d.ts.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { invariant, isAncestor, nonNullable } from "@likec4/core";
|
|
2
|
+
import { GrammarUtils } from "langium";
|
|
3
|
+
import { findLast, last, partition } from "remeda";
|
|
4
|
+
import { TextEdit } from "vscode-languageserver-protocol";
|
|
5
|
+
import { ast } from "../ast.js";
|
|
6
|
+
const { findNodeForKeyword, findNodeForProperty } = GrammarUtils;
|
|
7
|
+
const asViewStyleRule = (target, key, value, indent = 0) => {
|
|
8
|
+
const indentStr = indent > 0 ? " ".repeat(indent) : "";
|
|
9
|
+
return [
|
|
10
|
+
indentStr + `style ${target} {`,
|
|
11
|
+
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 changeViewStyle(services, {
|
|
28
|
+
view,
|
|
29
|
+
viewAst,
|
|
30
|
+
targets,
|
|
31
|
+
key,
|
|
32
|
+
value
|
|
33
|
+
}) {
|
|
34
|
+
const viewCstNode = viewAst.$cstNode;
|
|
35
|
+
invariant(viewCstNode, "viewCstNode");
|
|
36
|
+
const insertPos = last(viewAst.body.rules)?.$cstNode?.range.end ?? last(viewAst.body.props)?.$cstNode?.range.end ?? viewAst.body.$cstNode?.range.start;
|
|
37
|
+
invariant(insertPos, "insertPos is not defined");
|
|
38
|
+
const indent = viewCstNode.range.start.character + 2;
|
|
39
|
+
const fqnIndex = services.likec4.FqnIndex;
|
|
40
|
+
const styleRules = viewAst.body.rules.filter(ast.isViewRuleStyle);
|
|
41
|
+
const viewOf = view.viewOf;
|
|
42
|
+
const targetsWithRules = targets.map((target) => {
|
|
43
|
+
const rule = findLast(styleRules, isMatchingViewRule(target, fqnIndex));
|
|
44
|
+
const fqn = viewOf && isAncestor(viewOf, target) ? target.substring(viewOf.length + 1) : target;
|
|
45
|
+
if (rule) {
|
|
46
|
+
return {
|
|
47
|
+
fqn,
|
|
48
|
+
rule
|
|
49
|
+
};
|
|
50
|
+
} else {
|
|
51
|
+
return {
|
|
52
|
+
fqn
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
const [existing, insert] = partition(
|
|
57
|
+
targetsWithRules,
|
|
58
|
+
(a) => !!a.rule
|
|
59
|
+
);
|
|
60
|
+
const modifiedRange = {
|
|
61
|
+
start: insertPos,
|
|
62
|
+
end: insertPos
|
|
63
|
+
};
|
|
64
|
+
const includeRange = (range) => {
|
|
65
|
+
if (range.start.line < modifiedRange.start.line) {
|
|
66
|
+
modifiedRange.start = range.start;
|
|
67
|
+
}
|
|
68
|
+
if (range.end.line > modifiedRange.end.line) {
|
|
69
|
+
modifiedRange.end = range.end;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const edits = [];
|
|
73
|
+
if (insert.length > 0) {
|
|
74
|
+
const linesToInsert = [
|
|
75
|
+
"",
|
|
76
|
+
...insert.map(({ fqn }) => asViewStyleRule(fqn, key, value, indent))
|
|
77
|
+
];
|
|
78
|
+
edits.push(
|
|
79
|
+
TextEdit.insert(
|
|
80
|
+
insertPos,
|
|
81
|
+
linesToInsert.join("\n")
|
|
82
|
+
)
|
|
83
|
+
);
|
|
84
|
+
modifiedRange.end = {
|
|
85
|
+
line: modifiedRange.end.line + linesToInsert.length,
|
|
86
|
+
character: last(linesToInsert)?.length ?? insertPos.character
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (existing.length > 0) {
|
|
90
|
+
for (const { rule } of existing) {
|
|
91
|
+
const ruleCstNode = rule.$cstNode;
|
|
92
|
+
invariant(ruleCstNode, "RuleCstNode not found");
|
|
93
|
+
const ruleProp = rule.styleprops.find((p) => p.key === key);
|
|
94
|
+
if (ruleProp && ruleProp.$cstNode) {
|
|
95
|
+
const { range: { start, end } } = nonNullable(
|
|
96
|
+
findNodeForProperty(ruleProp.$cstNode, "value"),
|
|
97
|
+
"cant find value cst node"
|
|
98
|
+
);
|
|
99
|
+
includeRange(ruleProp.$cstNode.range);
|
|
100
|
+
edits.push(TextEdit.replace({ start, end }, value));
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const insertPos2 = findNodeForKeyword(ruleCstNode, "{")?.range.end;
|
|
104
|
+
invariant(insertPos2, "Opening brace not found");
|
|
105
|
+
const indentStr = " ".repeat(2 + ruleCstNode.range.start.character);
|
|
106
|
+
const insertKeyValue = indentStr + key + " " + value;
|
|
107
|
+
edits.push(
|
|
108
|
+
TextEdit.insert(
|
|
109
|
+
insertPos2,
|
|
110
|
+
"\n" + insertKeyValue
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
includeRange({
|
|
114
|
+
start: insertPos2,
|
|
115
|
+
end: {
|
|
116
|
+
line: insertPos2.line + 1,
|
|
117
|
+
character: insertKeyValue.length
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return edits;
|
|
123
|
+
}
|
package/dist/module.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type Module, WorkspaceCache } from 'langium';
|
|
|
2
2
|
import { type DefaultSharedModuleContext, type LangiumServices, type LangiumSharedServices, type PartialLangiumServices } from 'langium/lsp';
|
|
3
3
|
import { LikeC4CodeLensProvider, LikeC4DocumentHighlightProvider, LikeC4DocumentLinkProvider, LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
|
|
4
4
|
import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model';
|
|
5
|
+
import { LikeC4ModelChanges } from './model-change/ModelChanges';
|
|
5
6
|
import { LikeC4ScopeComputation, LikeC4ScopeProvider } from './references';
|
|
6
7
|
import { Rpc } from './Rpc';
|
|
7
8
|
import { LikeC4WorkspaceManager, NodeKindProvider, WorkspaceSymbolProvider } from './shared';
|
|
@@ -26,6 +27,7 @@ export interface LikeC4AddedServices {
|
|
|
26
27
|
ModelParser: LikeC4ModelParser;
|
|
27
28
|
ModelBuilder: LikeC4ModelBuilder;
|
|
28
29
|
ModelLocator: LikeC4ModelLocator;
|
|
30
|
+
ModelChanges: LikeC4ModelChanges;
|
|
29
31
|
};
|
|
30
32
|
lsp: {
|
|
31
33
|
DocumentHighlightProvider: LikeC4DocumentHighlightProvider;
|
package/dist/module.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
LikeC4SemanticTokenProvider
|
|
16
16
|
} from "./lsp/index.js";
|
|
17
17
|
import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from "./model/index.js";
|
|
18
|
+
import { LikeC4ModelChanges } from "./model-change/ModelChanges.js";
|
|
18
19
|
import { LikeC4ScopeComputation, LikeC4ScopeProvider } from "./references/index.js";
|
|
19
20
|
import { Rpc } from "./Rpc.js";
|
|
20
21
|
import { LikeC4WorkspaceManager, NodeKindProvider, WorkspaceSymbolProvider } from "./shared/index.js";
|
|
@@ -35,6 +36,7 @@ export const LikeC4Module = {
|
|
|
35
36
|
WorkspaceCache: (services) => new WorkspaceCache(services.shared),
|
|
36
37
|
Rpc: bind(Rpc),
|
|
37
38
|
likec4: {
|
|
39
|
+
ModelChanges: bind(LikeC4ModelChanges),
|
|
38
40
|
FqnIndex: bind(FqnIndex),
|
|
39
41
|
ModelParser: bind(LikeC4ModelParser),
|
|
40
42
|
ModelBuilder: bind(LikeC4ModelBuilder),
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
import type { ComputedView, Fqn, LikeC4Model,
|
|
1
|
+
import type { AutoLayoutDirection, ComputedView, ElementShape, Fqn, LikeC4ComputedModel, LikeC4Model, NonEmptyArray, RelationID, ThemeColor, ViewID } from '@likec4/core';
|
|
2
2
|
import type { DocumentUri, Location } from 'vscode-languageserver-protocol';
|
|
3
3
|
import { NotificationType, RequestType, RequestType0 } from 'vscode-languageserver-protocol';
|
|
4
4
|
export declare const onDidChangeModel: NotificationType<string>;
|
|
5
|
-
export
|
|
6
|
-
rawmodel: LikeC4RawModel | null;
|
|
7
|
-
}, void>;
|
|
5
|
+
export type OnDidChangeModelNotification = typeof onDidChangeModel;
|
|
8
6
|
export declare const fetchModel: RequestType0<{
|
|
9
7
|
model: LikeC4Model | null;
|
|
10
8
|
}, void>;
|
|
9
|
+
export type FetchModelRequest = typeof fetchModel;
|
|
10
|
+
export declare const fetchComputedModel: RequestType0<{
|
|
11
|
+
model: LikeC4ComputedModel | null;
|
|
12
|
+
}, void>;
|
|
13
|
+
export type FetchComputedModelRequest = typeof fetchComputedModel;
|
|
11
14
|
export declare const computeView: RequestType<{
|
|
12
15
|
viewId: ViewID;
|
|
13
16
|
}, {
|
|
14
17
|
view: ComputedView | null;
|
|
15
18
|
}, void>;
|
|
16
|
-
|
|
19
|
+
export type ComputeViewRequest = typeof computeView;
|
|
20
|
+
export interface BuildDocumentsParams {
|
|
17
21
|
docs: DocumentUri[];
|
|
18
22
|
}
|
|
19
23
|
export declare const buildDocuments: RequestType<BuildDocumentsParams, void, void>;
|
|
24
|
+
export type BuildDocumentsRequest = typeof buildDocuments;
|
|
20
25
|
export type LocateParams = {
|
|
21
26
|
element: Fqn;
|
|
22
27
|
property?: string;
|
|
@@ -26,5 +31,28 @@ export type LocateParams = {
|
|
|
26
31
|
view: ViewID;
|
|
27
32
|
};
|
|
28
33
|
export declare const locate: RequestType<LocateParams, Location | null, void>;
|
|
29
|
-
export
|
|
34
|
+
export type LocateRequest = typeof locate;
|
|
35
|
+
export declare namespace ChangeView {
|
|
36
|
+
interface ChangeColor {
|
|
37
|
+
op: 'change-color';
|
|
38
|
+
color: ThemeColor;
|
|
39
|
+
targets: NonEmptyArray<Fqn>;
|
|
40
|
+
}
|
|
41
|
+
interface ChangeShape {
|
|
42
|
+
op: 'change-shape';
|
|
43
|
+
shape: ElementShape;
|
|
44
|
+
targets: NonEmptyArray<Fqn>;
|
|
45
|
+
}
|
|
46
|
+
interface ChangeAutoLayout {
|
|
47
|
+
op: 'change-autolayout';
|
|
48
|
+
layout: AutoLayoutDirection;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export type ChangeView = ChangeView.ChangeColor | ChangeView.ChangeShape | ChangeView.ChangeAutoLayout;
|
|
52
|
+
export interface ChangeViewRequestParams {
|
|
53
|
+
viewId: ViewID;
|
|
54
|
+
changes: NonEmptyArray<ChangeView>;
|
|
55
|
+
}
|
|
56
|
+
export declare const changeView: RequestType<ChangeViewRequestParams, Location | null, void>;
|
|
57
|
+
export type ChangeViewRequest = typeof changeView;
|
|
30
58
|
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/protocol.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { NotificationType, RequestType, RequestType0 } from "vscode-languageserver-protocol";
|
|
2
2
|
export const onDidChangeModel = new NotificationType("likec4/onDidChangeModel");
|
|
3
|
-
export const
|
|
4
|
-
"likec4/
|
|
3
|
+
export const fetchModel = new RequestType0(
|
|
4
|
+
"likec4/fetchModel"
|
|
5
|
+
);
|
|
6
|
+
export const fetchComputedModel = new RequestType0(
|
|
7
|
+
"likec4/fetchComputedModel"
|
|
5
8
|
);
|
|
6
|
-
export const fetchModel = new RequestType0("likec4/fetchModel");
|
|
7
9
|
export const computeView = new RequestType(
|
|
8
10
|
"likec4/computeView"
|
|
9
11
|
);
|
|
10
12
|
export const buildDocuments = new RequestType("likec4/build");
|
|
11
13
|
export const locate = new RequestType("likec4/locate");
|
|
14
|
+
export const changeView = new RequestType("likec4/change-view");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DefaultScopeComputation, MultiMap, type
|
|
1
|
+
import { type AstNodeDescription, DefaultScopeComputation, MultiMap, type PrecomputedScopes } from 'langium';
|
|
2
2
|
import type { CancellationToken } from 'vscode-languageserver';
|
|
3
3
|
import { ast, type LikeC4LangiumDocument } from '../ast';
|
|
4
4
|
type ElementsContainer = ast.Model | ast.ElementBody | ast.ExtendElementBody;
|
package/dist/reset.d.ts
ADDED
package/dist/shared/index.d.ts
CHANGED
package/dist/shared/index.js
CHANGED
|
@@ -14,7 +14,7 @@ export declare function createTestServices(workspace?: string): {
|
|
|
14
14
|
errors: string[];
|
|
15
15
|
warnings: string[];
|
|
16
16
|
}>;
|
|
17
|
-
buildModel: () => Promise<import("@likec4/core").
|
|
17
|
+
buildModel: () => Promise<import("@likec4/core").LikeC4ComputedModel>;
|
|
18
18
|
resetState: () => Promise<void>;
|
|
19
19
|
};
|
|
20
20
|
export type TestServices = ReturnType<typeof createTestServices>;
|
|
@@ -80,7 +80,7 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
80
80
|
};
|
|
81
81
|
const buildModel = async () => {
|
|
82
82
|
await validateAll();
|
|
83
|
-
const model = await modelBuilder.
|
|
83
|
+
const model = await modelBuilder.buildComputedModel();
|
|
84
84
|
if (!model)
|
|
85
85
|
throw new Error("No model found");
|
|
86
86
|
return model;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./stringHash.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { isNullish } from "remeda";
|
|
1
2
|
import { ast } from "../../ast.js";
|
|
2
|
-
import { isNil } from "remeda";
|
|
3
3
|
export const incomingExpressionChecks = (_services) => {
|
|
4
4
|
return (el, accept) => {
|
|
5
5
|
if (ast.isWildcardExpr(el.to) && ast.isViewRulePredicate(el.$container)) {
|
|
6
6
|
const view = el.$container.$container.$container;
|
|
7
|
-
if (
|
|
7
|
+
if (isNullish(view.viewOf)) {
|
|
8
8
|
accept("warning", "Predicate is ignored as it concerns all relationships", {
|
|
9
9
|
node: el
|
|
10
10
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/language-server",
|
|
3
3
|
"description": "LikeC4 Language Server",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.0-next.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
"types": "./dist/browser/index.d.ts",
|
|
30
30
|
"default": "./dist/browser/index.js"
|
|
31
31
|
},
|
|
32
|
+
"./protocol": {
|
|
33
|
+
"types": "./dist/protocol.d.ts",
|
|
34
|
+
"default": "./dist/protocol.js"
|
|
35
|
+
},
|
|
32
36
|
"./node": {
|
|
33
37
|
"types": "./dist/node/index.d.ts",
|
|
34
38
|
"default": "./dist/node/index.js"
|
|
@@ -46,6 +50,10 @@
|
|
|
46
50
|
"types": "./dist/browser/index.d.ts",
|
|
47
51
|
"default": "./dist/browser/index.js"
|
|
48
52
|
},
|
|
53
|
+
"./protocol": {
|
|
54
|
+
"types": "./dist/protocol.d.ts",
|
|
55
|
+
"default": "./dist/protocol.js"
|
|
56
|
+
},
|
|
49
57
|
"./node": {
|
|
50
58
|
"types": "./dist/node/index.d.ts",
|
|
51
59
|
"default": "./dist/node/index.js"
|
|
@@ -66,16 +74,19 @@
|
|
|
66
74
|
"test": "vitest run"
|
|
67
75
|
},
|
|
68
76
|
"dependencies": {
|
|
69
|
-
"@likec4/core": "0.
|
|
70
|
-
"@likec4/graph": "0.
|
|
77
|
+
"@likec4/core": "1.0.0-next.0",
|
|
78
|
+
"@likec4/graph": "1.0.0-next.0",
|
|
79
|
+
"@total-typescript/ts-reset": "^0.5.1",
|
|
71
80
|
"debounce-fn": "^6.0.0",
|
|
81
|
+
"fast-equals": "^5.0.1",
|
|
72
82
|
"langium": "^3.0.0",
|
|
73
83
|
"object-hash": "^3.0.0",
|
|
74
84
|
"p-debounce": "^4.0.0",
|
|
75
85
|
"rambdax": "^9.1.1",
|
|
76
|
-
"remeda": "^1.
|
|
86
|
+
"remeda": "^1.56.0",
|
|
87
|
+
"string-hash": "^1.1.3",
|
|
77
88
|
"strip-indent": "^4.0.0",
|
|
78
|
-
"type-fest": "^4.
|
|
89
|
+
"type-fest": "^4.13.1",
|
|
79
90
|
"ufo": "^1.3.2",
|
|
80
91
|
"vscode-languageserver": "9.0.1",
|
|
81
92
|
"vscode-languageserver-protocol": "3.17.5",
|
|
@@ -84,15 +95,13 @@
|
|
|
84
95
|
"devDependencies": {
|
|
85
96
|
"@types/node": "^20.11.25",
|
|
86
97
|
"@types/object-hash": "^3.0.6",
|
|
98
|
+
"@types/string-hash": "^1",
|
|
87
99
|
"execa": "^8.0.1",
|
|
88
100
|
"langium-cli": "^3.0.1",
|
|
89
|
-
"npm-run-all2": "^
|
|
90
|
-
"typescript": "^5.4.
|
|
101
|
+
"npm-run-all2": "^6.1.2",
|
|
102
|
+
"typescript": "^5.4.3",
|
|
91
103
|
"unbuild": "^2.0.0",
|
|
92
104
|
"vitest": "^1.4.0"
|
|
93
105
|
},
|
|
94
|
-
"packageManager": "yarn@4.1.1"
|
|
95
|
-
"volta": {
|
|
96
|
-
"extends": "../../package.json"
|
|
97
|
-
}
|
|
106
|
+
"packageManager": "yarn@4.1.1"
|
|
98
107
|
}
|