@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.
- package/dist/Rpc.js +6 -0
- package/dist/browser.d.ts +1 -0
- package/dist/documentation/documentation-provider.d.ts +8 -0
- package/dist/documentation/documentation-provider.js +46 -0
- package/dist/documentation/index.d.ts +1 -0
- package/dist/documentation/index.js +1 -0
- package/dist/generated/ast.d.ts +22 -9
- package/dist/generated/ast.js +23 -2
- package/dist/generated/grammar.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -2
- package/dist/like-c4.langium +4 -1
- package/dist/lsp/DocumentSymbolProvider.d.ts +11 -2
- package/dist/lsp/DocumentSymbolProvider.js +78 -6
- package/dist/model/fqn-computation.js +2 -2
- package/dist/model/model-builder.js +3 -3
- package/dist/model/model-parser.d.ts +5 -0
- package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
- package/dist/model/parser/DeploymentViewParser.d.ts +2 -1
- package/dist/model/parser/DeploymentViewParser.js +3 -0
- package/dist/model/parser/FqnRefParser.d.ts +1 -0
- package/dist/model/parser/FqnRefParser.js +13 -0
- package/dist/model/parser/GlobalsParser.d.ts +1 -0
- package/dist/model/parser/ViewsParser.d.ts +1 -0
- package/dist/module.d.ts +9 -1
- package/dist/module.js +47 -8
- package/dist/protocol.d.ts +19 -1
- package/dist/protocol.js +1 -0
- package/dist/references/scope-computation.d.ts +2 -2
- package/dist/references/scope-computation.js +9 -9
- package/dist/validation/dynamic-view-rule.js +3 -2
- package/dist/validation/dynamic-view-step.js +23 -27
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/property-checks.js +3 -2
- package/dist/validation/specification.js +14 -14
- package/dist/validation/view-predicates/element-with.js +3 -2
- package/dist/validation/view-predicates/expanded-element.js +3 -2
- package/dist/validation/view-predicates/incoming.js +3 -2
- package/dist/validation/view-predicates/outgoing.js +3 -2
- package/dist/validation/view-predicates/relation-with.js +3 -2
- package/dist/validation/view.js +3 -3
- package/dist/views/configurable-layouter.d.ts +7 -0
- package/dist/views/configurable-layouter.js +55 -0
- package/dist/views/index.d.ts +1 -0
- package/dist/views/index.js +1 -0
- package/dist/views/likec4-views.d.ts +26 -0
- package/dist/views/likec4-views.js +113 -0
- package/package.json +14 -11
- package/src/Rpc.ts +13 -7
- package/src/browser.ts +1 -0
- package/src/documentation/documentation-provider.ts +52 -0
- package/src/documentation/index.ts +1 -0
- package/src/generated/ast.ts +47 -11
- package/src/generated/grammar.ts +1 -1
- package/src/index.ts +4 -2
- package/src/like-c4.langium +4 -1
- package/src/lsp/DocumentSymbolProvider.ts +110 -28
- package/src/model/fqn-computation.ts +8 -8
- package/src/model/model-builder.ts +52 -52
- package/src/model/parser/DeploymentViewParser.ts +10 -7
- package/src/model/parser/FqnRefParser.ts +14 -0
- package/src/module.ts +56 -9
- package/src/protocol.ts +29 -4
- package/src/references/scope-computation.ts +35 -35
- package/src/validation/dynamic-view-rule.ts +5 -4
- package/src/validation/dynamic-view-step.ts +23 -26
- package/src/validation/property-checks.ts +11 -10
- package/src/validation/specification.ts +38 -38
- package/src/validation/view-predicates/element-with.ts +6 -5
- package/src/validation/view-predicates/expanded-element.ts +6 -5
- package/src/validation/view-predicates/incoming.ts +6 -5
- package/src/validation/view-predicates/outgoing.ts +6 -5
- package/src/validation/view-predicates/relation-with.ts +6 -5
- package/src/validation/view.ts +5 -5
- package/src/views/configurable-layouter.ts +65 -0
- package/src/views/index.ts +1 -0
- 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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
};
|
package/dist/validation/view.js
CHANGED
|
@@ -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,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.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
86
|
-
"@likec4/
|
|
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.
|
|
107
|
-
"@likec4/tsconfig": "1.19.
|
|
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.
|
|
123
|
+
"packageManager": "yarn@4.6.0"
|
|
121
124
|
}
|