@likec4/language-server 1.19.0 → 1.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/Rpc.js +6 -0
  2. package/dist/browser.d.ts +1 -0
  3. package/dist/documentation/documentation-provider.d.ts +8 -0
  4. package/dist/documentation/documentation-provider.js +46 -0
  5. package/dist/documentation/index.d.ts +1 -0
  6. package/dist/documentation/index.js +1 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +3 -2
  9. package/dist/lsp/DocumentSymbolProvider.d.ts +11 -2
  10. package/dist/lsp/DocumentSymbolProvider.js +78 -6
  11. package/dist/model/fqn-computation.js +2 -2
  12. package/dist/model/model-builder.js +3 -3
  13. package/dist/module.d.ts +9 -1
  14. package/dist/module.js +47 -8
  15. package/dist/protocol.d.ts +19 -1
  16. package/dist/protocol.js +1 -0
  17. package/dist/references/scope-computation.d.ts +2 -2
  18. package/dist/references/scope-computation.js +9 -9
  19. package/dist/validation/dynamic-view-rule.js +3 -2
  20. package/dist/validation/dynamic-view-step.js +23 -27
  21. package/dist/validation/property-checks.js +3 -2
  22. package/dist/validation/specification.js +14 -14
  23. package/dist/validation/view-predicates/element-with.js +3 -2
  24. package/dist/validation/view-predicates/expanded-element.js +3 -2
  25. package/dist/validation/view-predicates/incoming.js +3 -2
  26. package/dist/validation/view-predicates/outgoing.js +3 -2
  27. package/dist/validation/view-predicates/relation-with.js +3 -2
  28. package/dist/validation/view.js +3 -3
  29. package/dist/views/configurable-layouter.d.ts +7 -0
  30. package/dist/views/configurable-layouter.js +55 -0
  31. package/dist/views/index.d.ts +1 -0
  32. package/dist/views/index.js +1 -0
  33. package/dist/views/likec4-views.d.ts +26 -0
  34. package/dist/views/likec4-views.js +113 -0
  35. package/package.json +13 -10
  36. package/src/Rpc.ts +13 -7
  37. package/src/browser.ts +1 -0
  38. package/src/documentation/documentation-provider.ts +52 -0
  39. package/src/documentation/index.ts +1 -0
  40. package/src/index.ts +4 -2
  41. package/src/lsp/DocumentSymbolProvider.ts +110 -28
  42. package/src/model/fqn-computation.ts +8 -8
  43. package/src/model/model-builder.ts +52 -52
  44. package/src/module.ts +56 -9
  45. package/src/protocol.ts +29 -4
  46. package/src/references/scope-computation.ts +35 -35
  47. package/src/validation/dynamic-view-rule.ts +5 -4
  48. package/src/validation/dynamic-view-step.ts +23 -26
  49. package/src/validation/property-checks.ts +11 -10
  50. package/src/validation/specification.ts +38 -38
  51. package/src/validation/view-predicates/element-with.ts +6 -5
  52. package/src/validation/view-predicates/expanded-element.ts +6 -5
  53. package/src/validation/view-predicates/incoming.ts +6 -5
  54. package/src/validation/view-predicates/outgoing.ts +6 -5
  55. package/src/validation/view-predicates/relation-with.ts +6 -5
  56. package/src/validation/view.ts +5 -5
  57. package/src/views/configurable-layouter.ts +65 -0
  58. package/src/views/index.ts +1 -0
  59. package/src/views/likec4-views.ts +139 -0
@@ -1,33 +1,29 @@
1
1
  import { isAncestor } from "@likec4/core";
2
- import { logError } from "../logger.js";
3
2
  import { elementRef } from "../utils/elementRef.js";
3
+ import { tryOrLog } from "./_shared.js";
4
4
  export const dynamicViewStep = (services) => {
5
5
  const fqnIndex = services.likec4.FqnIndex;
6
- return (el, accept) => {
7
- try {
8
- const sourceEl = elementRef(el.source);
9
- const source = sourceEl && fqnIndex.getFqn(sourceEl);
10
- if (!source) {
11
- accept("error", "Source not found (not parsed/indexed yet)", {
12
- node: el,
13
- property: "source"
14
- });
15
- }
16
- const targetEl = elementRef(el.target);
17
- const target = targetEl && fqnIndex.getFqn(targetEl);
18
- if (!target) {
19
- accept("error", "Target not found (not parsed/indexed yet)", {
20
- node: el,
21
- property: "target"
22
- });
23
- }
24
- if (source && target && (isAncestor(source, target) || isAncestor(target, source))) {
25
- accept("error", "Invalid parent-child relationship", {
26
- node: el
27
- });
28
- }
29
- } catch (e) {
30
- logError(e);
6
+ return tryOrLog((el, accept) => {
7
+ const sourceEl = elementRef(el.source);
8
+ const source = sourceEl && fqnIndex.getFqn(sourceEl);
9
+ if (!source) {
10
+ accept("error", "Source not found (not parsed/indexed yet)", {
11
+ node: el,
12
+ property: "source"
13
+ });
31
14
  }
32
- };
15
+ const targetEl = elementRef(el.target);
16
+ const target = targetEl && fqnIndex.getFqn(targetEl);
17
+ if (!target) {
18
+ accept("error", "Target not found (not parsed/indexed yet)", {
19
+ node: el,
20
+ property: "target"
21
+ });
22
+ }
23
+ if (source && target && (isAncestor(source, target) || isAncestor(target, source))) {
24
+ accept("error", "Invalid parent-child relationship", {
25
+ node: el
26
+ });
27
+ }
28
+ });
33
29
  };
@@ -1,7 +1,8 @@
1
1
  import { AstUtils } from "langium";
2
2
  import { ast } from "../ast.js";
3
+ import { tryOrLog } from "./_shared.js";
3
4
  export const opacityPropertyRuleChecks = (_) => {
4
- return (node, accept) => {
5
+ return tryOrLog((node, accept) => {
5
6
  const opacity = parseFloat(node.value);
6
7
  if (isNaN(opacity) || opacity < 0 || opacity > 100) {
7
8
  accept("warning", `Value ignored, must be between 0% and 100%`, {
@@ -9,7 +10,7 @@ export const opacityPropertyRuleChecks = (_) => {
9
10
  property: "value"
10
11
  });
11
12
  }
12
- };
13
+ });
13
14
  };
14
15
  export const iconPropertyRuleChecks = (_) => {
15
16
  return (node, accept) => {
@@ -2,34 +2,34 @@ import { AstUtils } from "langium";
2
2
  import { ast } from "../ast.js";
3
3
  import { RESERVED_WORDS, tryOrLog } from "./_shared.js";
4
4
  export const specificationRuleChecks = (_) => {
5
- return (node, accept) => {
5
+ return tryOrLog((node, accept) => {
6
6
  if (node.$containerIndex && node.$containerIndex > 0) {
7
7
  accept("warning", `Prefer one specification per document`, {
8
8
  node,
9
9
  property: "name"
10
10
  });
11
11
  }
12
- };
12
+ });
13
13
  };
14
14
  export const modelRuleChecks = (_) => {
15
- return (node, accept) => {
15
+ return tryOrLog((node, accept) => {
16
16
  if (node.$containerIndex && node.$containerIndex > 0) {
17
17
  accept("warning", `Prefer one model per document`, {
18
18
  node,
19
19
  property: "name"
20
20
  });
21
21
  }
22
- };
22
+ });
23
23
  };
24
24
  export const globalsChecks = (_) => {
25
- return (node, accept) => {
25
+ return tryOrLog((node, accept) => {
26
26
  if (node.$containerIndex && node.$containerIndex > 0) {
27
27
  accept("warning", `Prefer one global block per document`, {
28
28
  node,
29
29
  property: "name"
30
30
  });
31
31
  }
32
- };
32
+ });
33
33
  };
34
34
  export const elementKindChecks = (services) => {
35
35
  const index = services.shared.workspace.IndexManager;
@@ -63,7 +63,7 @@ export const elementKindChecks = (services) => {
63
63
  };
64
64
  export const tagChecks = (services) => {
65
65
  const index = services.shared.workspace.IndexManager;
66
- return (node, accept) => {
66
+ return tryOrLog((node, accept) => {
67
67
  const tagname = "#" + node.name;
68
68
  const sameTag = index.allElements(ast.Tag).filter((n) => n.name === tagname && n.node !== node).head();
69
69
  if (sameTag) {
@@ -88,11 +88,11 @@ export const tagChecks = (services) => {
88
88
  }
89
89
  );
90
90
  }
91
- };
91
+ });
92
92
  };
93
93
  export const relationshipChecks = (services) => {
94
94
  const index = services.shared.workspace.IndexManager;
95
- return (node, accept) => {
95
+ return tryOrLog((node, accept) => {
96
96
  if (RESERVED_WORDS.includes(node.name)) {
97
97
  accept("error", `Reserved word: ${node.name}`, {
98
98
  node,
@@ -106,11 +106,11 @@ export const relationshipChecks = (services) => {
106
106
  property: "name"
107
107
  });
108
108
  }
109
- };
109
+ });
110
110
  };
111
111
  export const globalPredicateChecks = (services) => {
112
112
  const index = services.shared.workspace.IndexManager;
113
- return (node, accept) => {
113
+ return tryOrLog((node, accept) => {
114
114
  const predicateGroups = index.allElements(ast.GlobalPredicateGroup);
115
115
  const dynamicPredicateGroups = index.allElements(ast.GlobalDynamicPredicateGroup);
116
116
  const sameName = predicateGroups.concat(dynamicPredicateGroups).filter((s) => s.name === node.name).limit(2).count();
@@ -120,11 +120,11 @@ export const globalPredicateChecks = (services) => {
120
120
  property: "name"
121
121
  });
122
122
  }
123
- };
123
+ });
124
124
  };
125
125
  export const globalStyleIdChecks = (services) => {
126
126
  const index = services.shared.workspace.IndexManager;
127
- return (node, accept) => {
127
+ return tryOrLog((node, accept) => {
128
128
  const sameName = index.allElements(ast.GlobalStyleId).filter((s) => s.name === node.name).limit(2).count();
129
129
  if (sameName > 1) {
130
130
  accept("error", `Duplicate GlobalStyleId name '${node.name}'`, {
@@ -132,5 +132,5 @@ export const globalStyleIdChecks = (services) => {
132
132
  property: "name"
133
133
  });
134
134
  }
135
- };
135
+ });
136
136
  };
@@ -1,8 +1,9 @@
1
1
  import { nonexhaustive } from "@likec4/core";
2
2
  import { AstUtils } from "langium";
3
3
  import { ast } from "../../ast.js";
4
+ import { tryOrLog } from "../_shared.js";
4
5
  export const elementPredicateWithChecks = (_services) => {
5
- return (el, accept) => {
6
+ return tryOrLog((el, accept) => {
6
7
  const container = AstUtils.getContainerOfType(el, ast.isViewRulePredicate);
7
8
  if (ast.isExcludePredicate(container)) {
8
9
  accept("error", 'Invalid usage inside "exclude"', {
@@ -26,5 +27,5 @@ export const elementPredicateWithChecks = (_services) => {
26
27
  default:
27
28
  nonexhaustive(subject);
28
29
  }
29
- };
30
+ });
30
31
  };
@@ -1,11 +1,12 @@
1
1
  import { AstUtils } from "langium";
2
2
  import { ast } from "../../ast.js";
3
+ import { tryOrLog } from "../_shared.js";
3
4
  export const expandElementExprChecks = (_services) => {
4
- return (el, accept) => {
5
+ return tryOrLog((el, accept) => {
5
6
  if (AstUtils.hasContainerOfType(el, ast.isRelationExpression)) {
6
7
  accept("warning", `Redundant usage, expand predicate resolves parent element only when used in relations`, {
7
8
  node: el
8
9
  });
9
10
  }
10
- };
11
+ });
11
12
  };
@@ -1,8 +1,9 @@
1
1
  import { AstUtils } from "langium";
2
2
  import { isNullish } from "remeda";
3
3
  import { ast } from "../../ast.js";
4
+ import { tryOrLog } from "../_shared.js";
4
5
  export const incomingExpressionChecks = (_services) => {
5
- return (el, accept) => {
6
+ return tryOrLog((el, accept) => {
6
7
  if (ast.isWildcardExpression(el.to) && !ast.isInOutRelationExpression(el.$container)) {
7
8
  const view = AstUtils.getContainerOfType(el, ast.isElementView);
8
9
  if (isNullish(view?.viewOf)) {
@@ -11,5 +12,5 @@ export const incomingExpressionChecks = (_services) => {
11
12
  });
12
13
  }
13
14
  }
14
- };
15
+ });
15
16
  };
@@ -1,8 +1,9 @@
1
1
  import { AstUtils } from "langium";
2
2
  import { isNullish } from "remeda";
3
3
  import { ast } from "../../ast.js";
4
+ import { tryOrLog } from "../_shared.js";
4
5
  export const outgoingExpressionChecks = (_services) => {
5
- return (el, accept) => {
6
+ return tryOrLog((el, accept) => {
6
7
  if (ast.isWildcardExpression(el.from) && !ast.isDirectedRelationExpression(el.$container)) {
7
8
  const view = AstUtils.getContainerOfType(el, ast.isElementView);
8
9
  if (isNullish(view?.viewOf)) {
@@ -11,5 +12,5 @@ export const outgoingExpressionChecks = (_services) => {
11
12
  });
12
13
  }
13
14
  }
14
- };
15
+ });
15
16
  };
@@ -1,12 +1,13 @@
1
1
  import { AstUtils } from "langium";
2
2
  import { ast } from "../../ast.js";
3
+ import { tryOrLog } from "../_shared.js";
3
4
  export const relationPredicateWithChecks = (_services) => {
4
- return (el, accept) => {
5
+ return tryOrLog((el, accept) => {
5
6
  const container = AstUtils.getContainerOfType(el, ast.isViewRulePredicate);
6
7
  if (ast.isExcludePredicate(container)) {
7
8
  accept("error", 'Invalid usage inside "exclude"', {
8
9
  node: el
9
10
  });
10
11
  }
11
- };
12
+ });
12
13
  };
@@ -1,8 +1,8 @@
1
1
  import { ast } from "../ast.js";
2
- import { RESERVED_WORDS } from "./_shared.js";
2
+ import { RESERVED_WORDS, tryOrLog } from "./_shared.js";
3
3
  export const viewChecks = (services) => {
4
4
  const index = services.shared.workspace.IndexManager;
5
- return (el, accept) => {
5
+ return tryOrLog((el, accept) => {
6
6
  if (!el.name) {
7
7
  return;
8
8
  }
@@ -19,5 +19,5 @@ export const viewChecks = (services) => {
19
19
  property: "name"
20
20
  });
21
21
  }
22
- };
22
+ });
23
23
  };
@@ -0,0 +1,7 @@
1
+ import { GraphvizLayouter } from '@likec4/layouts';
2
+ import type { LikeC4Services } from '../module';
3
+ export declare const ConfigurableLayouter: {
4
+ likec4: {
5
+ Layouter(services: LikeC4Services): GraphvizLayouter;
6
+ };
7
+ };
@@ -0,0 +1,55 @@
1
+ import { GraphvizLayouter, GraphvizWasmAdapter } from "@likec4/layouts";
2
+ import { GraphvizBinaryAdapter } from "@likec4/layouts/graphviz/binary";
3
+ import { isEmpty } from "remeda";
4
+ import which from "which";
5
+ import { logger } from "../logger.js";
6
+ function graphvizBinPath() {
7
+ try {
8
+ return which.sync("dot");
9
+ } catch (error) {
10
+ logger.error("Error checking for native Graphviz:", error);
11
+ return null;
12
+ }
13
+ }
14
+ export const ConfigurableLayouter = {
15
+ likec4: {
16
+ Layouter(services) {
17
+ logger.debug("Creating ConfigurableLayouter");
18
+ const wasmAdapter = new GraphvizWasmAdapter();
19
+ const layouter = new GraphvizLayouter(wasmAdapter);
20
+ const langId = services.LanguageMetaData.languageId;
21
+ services.shared.workspace.ConfigurationProvider.onConfigurationSectionUpdate((update) => {
22
+ logger.debug("Configuration update", update);
23
+ if (update.section === langId) {
24
+ try {
25
+ const { mode, path } = update.configuration.graphviz ?? {
26
+ mode: "wasm",
27
+ path: ""
28
+ };
29
+ if (mode === "wasm") {
30
+ layouter.changePort(wasmAdapter);
31
+ logger.info("use graphviz wasm");
32
+ return;
33
+ }
34
+ let binaryPath = isEmpty(path) ? graphvizBinPath() : path;
35
+ if (binaryPath === null) {
36
+ layouter.changePort(wasmAdapter);
37
+ logger.warn(`No Graphviz binaries found on PATH, use graphviz wasm`);
38
+ services.shared.lsp.Connection?.window.showWarningMessage(
39
+ "No Graphviz binaries found on PATH, set path to binaries in settings."
40
+ );
41
+ return;
42
+ }
43
+ layouter.changePort(new GraphvizBinaryAdapter(binaryPath));
44
+ logger.info(`use graphviz binary: ${binaryPath}`);
45
+ } catch (error) {
46
+ logger.error("Failed to update configuration", error);
47
+ }
48
+ return;
49
+ }
50
+ logger.warn("Unexpected configuration update", update);
51
+ });
52
+ return layouter;
53
+ }
54
+ }
55
+ };
@@ -0,0 +1 @@
1
+ export { LikeC4Views } from './likec4-views';
@@ -0,0 +1 @@
1
+ export { LikeC4Views } from "./likec4-views.js";
@@ -0,0 +1,26 @@
1
+ import type { ComputedView, DiagramView, OverviewGraph, ViewId } from '@likec4/core';
2
+ import { type Cancellation } from 'langium';
3
+ import type { LikeC4Services } from '../module';
4
+ export type GraphvizOut = {
5
+ dot: string;
6
+ diagram: DiagramView;
7
+ };
8
+ type GraphvizSvgOut = {
9
+ id: ViewId;
10
+ dot: string;
11
+ svg: string;
12
+ };
13
+ export declare class LikeC4Views {
14
+ private services;
15
+ private cache;
16
+ private viewsWithReportedErrors;
17
+ constructor(services: LikeC4Services);
18
+ private get layouter();
19
+ computedViews(cancelToken?: Cancellation.CancellationToken): Promise<ComputedView[]>;
20
+ layoutAllViews(cancelToken?: Cancellation.CancellationToken): Promise<Array<Readonly<GraphvizOut>>>;
21
+ layoutView(viewId: ViewId, cancelToken?: Cancellation.CancellationToken): Promise<GraphvizOut | null>;
22
+ diagrams(): Promise<Array<DiagramView>>;
23
+ viewsAsGraphvizOut(): Promise<Array<GraphvizSvgOut>>;
24
+ overviewGraph(): Promise<OverviewGraph>;
25
+ }
26
+ export {};
@@ -0,0 +1,113 @@
1
+ import { values } from "remeda";
2
+ import { logger, logWarnError } from "../logger.js";
3
+ export class LikeC4Views {
4
+ constructor(services) {
5
+ this.services = services;
6
+ }
7
+ cache = /* @__PURE__ */ new WeakMap();
8
+ viewsWithReportedErrors = /* @__PURE__ */ new Set();
9
+ get layouter() {
10
+ return this.services.likec4.Layouter;
11
+ }
12
+ async computedViews(cancelToken) {
13
+ const model = await this.services.likec4.ModelBuilder.buildComputedModel(cancelToken);
14
+ return model ? values(model.views) : [];
15
+ }
16
+ async layoutAllViews(cancelToken) {
17
+ const views = await this.computedViews(cancelToken);
18
+ if (views.length === 0) {
19
+ return [];
20
+ }
21
+ const results = [];
22
+ const tasks = [];
23
+ for (const view of views) {
24
+ this.viewsWithReportedErrors.delete(view.id);
25
+ tasks.push(
26
+ this.layouter.layout(view).then((result) => {
27
+ this.cache.set(view, result);
28
+ return result;
29
+ }).catch((e) => {
30
+ this.cache.delete(view);
31
+ logWarnError(e);
32
+ return Promise.reject(e);
33
+ })
34
+ );
35
+ }
36
+ for (const task of await Promise.allSettled(tasks)) {
37
+ if (task.status === "fulfilled") {
38
+ results.push(task.value);
39
+ }
40
+ }
41
+ return results;
42
+ }
43
+ async layoutView(viewId, cancelToken) {
44
+ const model = await this.services.likec4.ModelBuilder.buildComputedModel(cancelToken);
45
+ if (!model) {
46
+ return null;
47
+ }
48
+ const view = model.views[viewId];
49
+ if (!view) {
50
+ return null;
51
+ }
52
+ let cached = this.cache.get(view);
53
+ if (cached) {
54
+ return cached;
55
+ }
56
+ try {
57
+ const result = await this.layouter.layout(view);
58
+ this.viewsWithReportedErrors.delete(viewId);
59
+ this.cache.set(view, result);
60
+ return result;
61
+ } catch (e) {
62
+ if (!this.viewsWithReportedErrors.has(viewId)) {
63
+ const errMessage = e instanceof Error ? e.message : "" + e;
64
+ this.services.shared.lsp.Connection?.window.showErrorMessage(`LikeC4: ${errMessage}`);
65
+ this.viewsWithReportedErrors.add(viewId);
66
+ }
67
+ logger.error(e);
68
+ return Promise.reject(e);
69
+ }
70
+ }
71
+ async diagrams() {
72
+ const layouted = await this.layoutAllViews();
73
+ return layouted.map((l) => l.diagram);
74
+ }
75
+ async viewsAsGraphvizOut() {
76
+ const KEY = "All-LayoutedViews-DotWithSvg";
77
+ const cache = this.services.WorkspaceCache;
78
+ if (cache.has(KEY)) {
79
+ return await Promise.resolve(cache.get(KEY));
80
+ }
81
+ const views = await this.computedViews();
82
+ const tasks = views.map(async (view) => {
83
+ const { dot, svg } = await this.layouter.svg(view);
84
+ return {
85
+ id: view.id,
86
+ dot,
87
+ svg
88
+ };
89
+ });
90
+ const succeed = [];
91
+ const settledResult = await Promise.allSettled(tasks);
92
+ for (const result of settledResult) {
93
+ if (result.status === "fulfilled") {
94
+ succeed.push(result.value);
95
+ } else {
96
+ logWarnError(result.reason);
97
+ }
98
+ }
99
+ cache.set(KEY, succeed);
100
+ return succeed;
101
+ }
102
+ async overviewGraph() {
103
+ const KEY = "OverviewGraph";
104
+ const cache = this.services.WorkspaceCache;
105
+ if (cache.has(KEY)) {
106
+ return await Promise.resolve(cache.get(KEY));
107
+ }
108
+ const views = await this.computedViews();
109
+ const overviewGraph = await this.layouter.layoutOverviewGraph(views);
110
+ cache.set(KEY, overviewGraph);
111
+ return overviewGraph;
112
+ }
113
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "1.19.0",
4
+ "version": "1.19.1",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -24,7 +24,7 @@
24
24
  "sideEffects": false,
25
25
  "exports": {
26
26
  ".": {
27
- "development": "./src/index.ts",
27
+ "sources": "./src/index.ts",
28
28
  "node": {
29
29
  "types": "./dist/index.d.ts",
30
30
  "import": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  }
38
38
  },
39
39
  "./likec4lib": {
40
- "development": "./src/likec4lib.ts",
40
+ "sources": "./src/likec4lib.ts",
41
41
  "default": {
42
42
  "types": "./dist/likec4lib.d.ts",
43
43
  "import": "./dist/likec4lib.js",
@@ -45,7 +45,7 @@
45
45
  }
46
46
  },
47
47
  "./browser": {
48
- "development": "./src/browser.ts",
48
+ "sources": "./src/browser.ts",
49
49
  "default": {
50
50
  "types": "./dist/browser.d.ts",
51
51
  "import": "./dist/browser.js",
@@ -53,7 +53,7 @@
53
53
  }
54
54
  },
55
55
  "./protocol": {
56
- "development": "./src/protocol.ts",
56
+ "sources": "./src/protocol.ts",
57
57
  "default": {
58
58
  "types": "./dist/protocol.d.ts",
59
59
  "import": "./dist/protocol.js",
@@ -82,8 +82,9 @@
82
82
  "test:watch": "vitest"
83
83
  },
84
84
  "dependencies": {
85
- "@likec4/core": "1.19.0",
86
- "@likec4/log": "1.19.0",
85
+ "@likec4/core": "1.19.1",
86
+ "@likec4/layouts": "1.19.1",
87
+ "@likec4/log": "1.19.1",
87
88
  "@msgpack/msgpack": "^3.0.0-beta2",
88
89
  "@smithy/util-base64": "^3.0.0",
89
90
  "esm-env": "^1.2.1",
@@ -100,12 +101,14 @@
100
101
  "vscode-jsonrpc": "8.2.0",
101
102
  "vscode-languageserver": "9.0.1",
102
103
  "vscode-languageserver-types": "3.17.5",
103
- "vscode-uri": "3.0.8"
104
+ "vscode-uri": "3.0.8",
105
+ "which": "^4.0.0"
104
106
  },
105
107
  "devDependencies": {
106
- "@likec4/icons": "1.19.0",
107
- "@likec4/tsconfig": "1.19.0",
108
+ "@likec4/icons": "1.19.1",
109
+ "@likec4/tsconfig": "1.19.1",
108
110
  "@types/node": "^20.17.7",
111
+ "@types/which": "^3.0.4",
109
112
  "@vitest/coverage-v8": "^2.1.8",
110
113
  "execa": "^9.3.1",
111
114
  "langium-cli": "3.3.0",
package/src/Rpc.ts CHANGED
@@ -12,8 +12,9 @@ import {
12
12
  computeView,
13
13
  fetchComputedModel,
14
14
  fetchModel,
15
+ layoutView,
15
16
  locate,
16
- onDidChangeModel
17
+ onDidChangeModel,
17
18
  } from './protocol'
18
19
 
19
20
  export class Rpc implements Disposable {
@@ -25,6 +26,7 @@ export class Rpc implements Disposable {
25
26
  const modelBuilder = this.services.likec4.ModelBuilder
26
27
  const modelLocator = this.services.likec4.ModelLocator
27
28
  const modelEditor = this.services.likec4.ModelChanges
29
+ const views = this.services.likec4.Views
28
30
  const connection = this.services.shared.lsp.Connection
29
31
  if (!connection) {
30
32
  logger.info(`[ServerRpc] no connection, not initializing`)
@@ -44,8 +46,8 @@ export class Rpc implements Disposable {
44
46
  {
45
47
  timing: 'trailing',
46
48
  waitMs: 300,
47
- maxWaitMs: 1000
48
- }
49
+ maxWaitMs: 1000,
50
+ },
49
51
  )
50
52
 
51
53
  let isFirstBuild = true
@@ -70,6 +72,10 @@ export class Rpc implements Disposable {
70
72
  const view = await modelBuilder.computeView(viewId, cancelToken)
71
73
  return { view }
72
74
  }),
75
+ connection.onRequest(layoutView, async ({ viewId }, cancelToken) => {
76
+ const result = await views.layoutView(viewId, cancelToken)
77
+ return { result }
78
+ }),
73
79
  connection.onRequest(buildDocuments, async ({ docs }, cancelToken) => {
74
80
  const changed = docs.map(d => URI.parse(d))
75
81
  const notChanged = (uri: URI) => changed.every(c => !UriUtils.equals(c, uri))
@@ -80,7 +86,7 @@ export class Rpc implements Disposable {
80
86
  logger.debug(
81
87
  `[ServerRpc] received request to build:
82
88
  changed (total ${changed.length}):${docs.map(d => '\n - ' + d).join('')}
83
- deleted (total ${deleted.length}):${deleted.map(d => '\n - ' + d.toString()).join('\n')}`
89
+ deleted (total ${deleted.length}):${deleted.map(d => '\n - ' + d.toString()).join('\n')}`,
84
90
  )
85
91
 
86
92
  if (!isFirstBuild && (changed.length + deleted.length) > 0) {
@@ -91,13 +97,13 @@ export class Rpc implements Disposable {
91
97
  try {
92
98
  await connection.sendDiagnostics({
93
99
  uri,
94
- diagnostics: []
100
+ diagnostics: [],
95
101
  })
96
102
  } catch (e) {
97
103
  // Ignore
98
104
  logger.warn(`error clearing diagnostics for ${uri}: ${e}`)
99
105
  }
100
- })
106
+ }),
101
107
  )
102
108
  await interruptAndCheck(cancelToken)
103
109
  }
@@ -121,7 +127,7 @@ export class Rpc implements Disposable {
121
127
  }),
122
128
  connection.onRequest(changeView, async (request, _cancelToken) => {
123
129
  return await modelEditor.applyChange(request)
124
- })
130
+ }),
125
131
  )
126
132
  }
127
133
 
package/src/browser.ts CHANGED
@@ -7,6 +7,7 @@ export type { DocumentParser, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4Mode
7
7
 
8
8
  export { createCustomLanguageServices, createLanguageServices, LikeC4Module } from './module'
9
9
  export type { LikeC4Services, LikeC4SharedServices } from './module'
10
+ export type { LikeC4Views } from './views'
10
11
 
11
12
  // This is an example copied as is from here:
12
13
  // https://github.com/microsoft/vscode-extension-samples/blob/main/lsp-web-extension-sample/server/src/browserServerMain.ts