@likec4/language-server 1.19.0 → 1.19.2

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 (77) 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/generated/ast.d.ts +22 -9
  8. package/dist/generated/ast.js +23 -2
  9. package/dist/generated/grammar.js +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +3 -2
  12. package/dist/like-c4.langium +4 -1
  13. package/dist/lsp/DocumentSymbolProvider.d.ts +11 -2
  14. package/dist/lsp/DocumentSymbolProvider.js +78 -6
  15. package/dist/model/fqn-computation.js +2 -2
  16. package/dist/model/model-builder.js +3 -3
  17. package/dist/model/model-parser.d.ts +5 -0
  18. package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
  19. package/dist/model/parser/DeploymentViewParser.d.ts +2 -1
  20. package/dist/model/parser/DeploymentViewParser.js +3 -0
  21. package/dist/model/parser/FqnRefParser.d.ts +1 -0
  22. package/dist/model/parser/FqnRefParser.js +13 -0
  23. package/dist/model/parser/GlobalsParser.d.ts +1 -0
  24. package/dist/model/parser/ViewsParser.d.ts +1 -0
  25. package/dist/module.d.ts +9 -1
  26. package/dist/module.js +47 -8
  27. package/dist/protocol.d.ts +19 -1
  28. package/dist/protocol.js +1 -0
  29. package/dist/references/scope-computation.d.ts +2 -2
  30. package/dist/references/scope-computation.js +9 -9
  31. package/dist/validation/dynamic-view-rule.js +3 -2
  32. package/dist/validation/dynamic-view-step.js +23 -27
  33. package/dist/validation/index.d.ts +1 -1
  34. package/dist/validation/property-checks.js +3 -2
  35. package/dist/validation/specification.js +14 -14
  36. package/dist/validation/view-predicates/element-with.js +3 -2
  37. package/dist/validation/view-predicates/expanded-element.js +3 -2
  38. package/dist/validation/view-predicates/incoming.js +3 -2
  39. package/dist/validation/view-predicates/outgoing.js +3 -2
  40. package/dist/validation/view-predicates/relation-with.js +3 -2
  41. package/dist/validation/view.js +3 -3
  42. package/dist/views/configurable-layouter.d.ts +7 -0
  43. package/dist/views/configurable-layouter.js +55 -0
  44. package/dist/views/index.d.ts +1 -0
  45. package/dist/views/index.js +1 -0
  46. package/dist/views/likec4-views.d.ts +26 -0
  47. package/dist/views/likec4-views.js +113 -0
  48. package/package.json +14 -11
  49. package/src/Rpc.ts +13 -7
  50. package/src/browser.ts +1 -0
  51. package/src/documentation/documentation-provider.ts +52 -0
  52. package/src/documentation/index.ts +1 -0
  53. package/src/generated/ast.ts +47 -11
  54. package/src/generated/grammar.ts +1 -1
  55. package/src/index.ts +4 -2
  56. package/src/like-c4.langium +4 -1
  57. package/src/lsp/DocumentSymbolProvider.ts +110 -28
  58. package/src/model/fqn-computation.ts +8 -8
  59. package/src/model/model-builder.ts +52 -52
  60. package/src/model/parser/DeploymentViewParser.ts +10 -7
  61. package/src/model/parser/FqnRefParser.ts +14 -0
  62. package/src/module.ts +56 -9
  63. package/src/protocol.ts +29 -4
  64. package/src/references/scope-computation.ts +35 -35
  65. package/src/validation/dynamic-view-rule.ts +5 -4
  66. package/src/validation/dynamic-view-step.ts +23 -26
  67. package/src/validation/property-checks.ts +11 -10
  68. package/src/validation/specification.ts +38 -38
  69. package/src/validation/view-predicates/element-with.ts +6 -5
  70. package/src/validation/view-predicates/expanded-element.ts +6 -5
  71. package/src/validation/view-predicates/incoming.ts +6 -5
  72. package/src/validation/view-predicates/outgoing.ts +6 -5
  73. package/src/validation/view-predicates/relation-with.ts +6 -5
  74. package/src/validation/view.ts +5 -5
  75. package/src/views/configurable-layouter.ts +65 -0
  76. package/src/views/index.ts +1 -0
  77. package/src/views/likec4-views.ts +139 -0
@@ -1,6 +1,7 @@
1
1
  import { ast, elementExpressionFromPredicate } from "../ast.js";
2
+ import { tryOrLog } from "./_shared.js";
2
3
  export const dynamicViewRulePredicate = (_services) => {
3
- return (predicate, accept) => {
4
+ return tryOrLog((predicate, accept) => {
4
5
  const expr = elementExpressionFromPredicate(predicate.value);
5
6
  switch (true) {
6
7
  case ast.isElementKindExpression(expr):
@@ -12,5 +13,5 @@ export const dynamicViewRulePredicate = (_services) => {
12
13
  return;
13
14
  }
14
15
  }
15
- };
16
+ });
16
17
  };
@@ -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
  };
@@ -3,7 +3,7 @@ import { type LikeC4LangiumDocument, ast } from '../ast';
3
3
  import type { LikeC4Services } from '../module';
4
4
  type Guard<N extends AstNode> = (n: AstNode) => n is N;
5
5
  type Guarded<G> = G extends Guard<infer N> ? N : never;
6
- declare const isValidatableAstNode: (n: AstNode) => n is ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentViewRulePredicate | ast.DeploymentViewRuleStyle | ast.ViewRuleAutoLayout | ast.DynamicViewGlobalPredicateRef | ast.DynamicViewIncludePredicate | ast.ViewRuleGlobalStyle | ast.ViewRuleStyle | ast.ElementDescedantsExpression | ast.ElementKindExpression | ast.ElementRef | ast.ElementTagExpression | ast.ExpandElementExpression | ast.WildcardExpression | ast.ElementPredicateWhere | ast.ElementPredicateWith | ast.ElementStringProperty | ast.ElementStyleProperty | ast.IconProperty | ast.LinkProperty | ast.MetadataBody | ast.FqnRefExpr | ast.DirectedRelationExpr | ast.InOutRelationExpr | ast.IncomingRelationExpr | ast.OutgoingRelationExpr | ast.Element | ast.ExtendElement | ast.DeploymentView | ast.DynamicView | ast.ElementView | ast.DirectedRelationExpression | ast.InOutRelationExpression | ast.IncomingRelationExpression | ast.OutgoingRelationExpression | ast.RelationPredicateWhere | ast.RelationPredicateWith | ast.RelationStringProperty | ast.ArrowProperty | ast.ColorProperty | ast.LineProperty | ast.MetadataAttribute | ast.NotationProperty | ast.NotesProperty | ast.SpecificationElementStringProperty | ast.SpecificationRelationshipStringProperty | ast.ViewStringProperty | ast.BorderProperty | ast.OpacityProperty | ast.ShapeProperty | ast.ViewRuleGlobalPredicateRef | ast.ViewRuleGroup | ast.ExcludePredicate | ast.IncludePredicate | ast.SpecificationRelationshipKind | ast.GlobalStyle | ast.SpecificationColor | ast.NavigateToProperty | ast.DynamicViewStep | ast.Tags | ast.DeploymentRelation | ast.SpecificationDeploymentNodeKind | ast.DynamicViewParallelSteps | ast.GlobalDynamicPredicateGroup | ast.DynamicViewPredicateIterator | ast.Relation | ast.SpecificationElementKind | ast.GlobalPredicateGroup | ast.Globals | ast.GlobalStyleGroup | ast.SpecificationRule | ast.SpecificationTag;
6
+ declare const isValidatableAstNode: (n: AstNode) => n is ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentViewRulePredicate | ast.DeploymentViewRuleStyle | ast.ViewRuleAutoLayout | ast.DynamicViewGlobalPredicateRef | ast.DynamicViewIncludePredicate | ast.ViewRuleGlobalStyle | ast.ViewRuleStyle | ast.ElementDescedantsExpression | ast.ElementKindExpression | ast.ElementRef | ast.ElementTagExpression | ast.ExpandElementExpression | ast.WildcardExpression | ast.ElementPredicateWhere | ast.ElementPredicateWith | ast.ElementStringProperty | ast.ElementStyleProperty | ast.IconProperty | ast.LinkProperty | ast.MetadataBody | ast.FqnRefExpr | ast.DirectedRelationExpr | ast.InOutRelationExpr | ast.IncomingRelationExpr | ast.OutgoingRelationExpr | ast.RelationPredicateWhereV2 | ast.Element | ast.ExtendElement | ast.DeploymentView | ast.DynamicView | ast.ElementView | ast.DirectedRelationExpression | ast.InOutRelationExpression | ast.IncomingRelationExpression | ast.OutgoingRelationExpression | ast.RelationPredicateWhere | ast.RelationPredicateWith | ast.RelationStringProperty | ast.ArrowProperty | ast.ColorProperty | ast.LineProperty | ast.MetadataAttribute | ast.NotationProperty | ast.NotesProperty | ast.SpecificationElementStringProperty | ast.SpecificationRelationshipStringProperty | ast.ViewStringProperty | ast.BorderProperty | ast.OpacityProperty | ast.ShapeProperty | ast.ViewRuleGlobalPredicateRef | ast.ViewRuleGroup | ast.ExcludePredicate | ast.IncludePredicate | ast.SpecificationRelationshipKind | ast.GlobalStyle | ast.SpecificationColor | ast.NavigateToProperty | ast.DynamicViewStep | ast.Tags | ast.DeploymentRelation | ast.SpecificationDeploymentNodeKind | ast.DynamicViewParallelSteps | ast.GlobalDynamicPredicateGroup | ast.DynamicViewPredicateIterator | ast.Relation | ast.SpecificationElementKind | ast.GlobalPredicateGroup | ast.Globals | ast.GlobalStyleGroup | ast.SpecificationRule | ast.SpecificationTag;
7
7
  type ValidatableAstNode = Guarded<typeof isValidatableAstNode>;
8
8
  export declare function checksFromDiagnostics(doc: LikeC4LangiumDocument): {
9
9
  isValid: (n: ValidatableAstNode) => boolean;
@@ -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.2",
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.2",
86
+ "@likec4/layouts": "1.19.2",
87
+ "@likec4/log": "1.19.2",
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.2",
109
+ "@likec4/tsconfig": "1.19.2",
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",
@@ -117,5 +120,5 @@
117
120
  "unbuild": "^3.1.0",
118
121
  "vitest": "^2.1.8"
119
122
  },
120
- "packageManager": "yarn@4.5.3"
123
+ "packageManager": "yarn@4.6.0"
121
124
  }