@likec4/language-server 1.1.0 → 1.2.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.
Files changed (42) hide show
  1. package/contrib/likec4.monarch.ts +4 -4
  2. package/contrib/likec4.tmLanguage.json +1 -1
  3. package/dist/ast.d.ts +28 -11
  4. package/dist/ast.js +14 -13
  5. package/dist/generated/ast.d.ts +75 -19
  6. package/dist/generated/ast.js +96 -7
  7. package/dist/generated/grammar.js +1 -1
  8. package/dist/lsp/CodeLensProvider.js +5 -2
  9. package/dist/lsp/DocumentSymbolProvider.d.ts +1 -1
  10. package/dist/lsp/DocumentSymbolProvider.js +14 -9
  11. package/dist/lsp/SemanticTokenProvider.js +1 -1
  12. package/dist/model/model-builder.js +68 -45
  13. package/dist/model/model-locator.d.ts +2 -2
  14. package/dist/model/model-locator.js +12 -16
  15. package/dist/model/model-parser.d.ts +2 -0
  16. package/dist/model/model-parser.js +140 -30
  17. package/dist/model-change/ModelChanges.d.ts +2 -1
  18. package/dist/model-change/ModelChanges.js +39 -35
  19. package/dist/model-change/changeElementStyle.d.ts +18 -0
  20. package/dist/model-change/changeElementStyle.js +141 -0
  21. package/dist/model-change/changeViewLayout.d.ts +4 -4
  22. package/dist/model-change/changeViewLayout.js +4 -5
  23. package/dist/protocol.d.ts +12 -12
  24. package/dist/references/scope-computation.js +36 -31
  25. package/dist/shared/NodeKindProvider.js +4 -2
  26. package/dist/validation/dynamic-view-rule.d.ts +5 -0
  27. package/dist/validation/dynamic-view-rule.js +32 -0
  28. package/dist/validation/dynamic-view-step.d.ts +5 -0
  29. package/dist/validation/dynamic-view-step.js +33 -0
  30. package/dist/validation/index.js +5 -1
  31. package/dist/validation/view-predicates/expanded-element.js +1 -0
  32. package/dist/validation/view-predicates/outgoing.js +2 -2
  33. package/dist/validation/view.d.ts +1 -1
  34. package/dist/validation/view.js +1 -3
  35. package/dist/view-utils/assignNavigateTo.d.ts +1 -1
  36. package/dist/view-utils/assignNavigateTo.js +2 -1
  37. package/dist/view-utils/resolve-extended-views.d.ts +2 -2
  38. package/dist/view-utils/resolve-relative-paths.d.ts +2 -2
  39. package/dist/view-utils/resolve-relative-paths.js +2 -3
  40. package/package.json +6 -6
  41. package/dist/model-change/changeViewStyle.d.ts +0 -15
  42. package/dist/model-change/changeViewStyle.js +0 -123
@@ -0,0 +1,18 @@
1
+ import { type Fqn, type NonEmptyArray } from '@likec4/core';
2
+ import { type Range, TextEdit } from 'vscode-languageserver-protocol';
3
+ import { ast, type ParsedAstView, type ParsedLikeC4LangiumDocument } from '../ast';
4
+ import type { LikeC4Services } from '../module';
5
+ import type { ChangeView } from '../protocol';
6
+ type ChangeElementStyleArg = {
7
+ view: ParsedAstView;
8
+ doc: ParsedLikeC4LangiumDocument;
9
+ viewAst: ast.LikeC4View;
10
+ targets: NonEmptyArray<Fqn>;
11
+ style: ChangeView.ChangeElementStyle['style'];
12
+ };
13
+ export declare function changeElementStyle(services: LikeC4Services, { view, viewAst, targets, style }: ChangeElementStyleArg): {
14
+ modifiedRange: Range;
15
+ edits: TextEdit[];
16
+ };
17
+ export {};
18
+ //# sourceMappingURL=changeElementStyle.d.ts.map
@@ -0,0 +1,141 @@
1
+ import { invariant, isAncestor, nonNullable } from "@likec4/core";
2
+ import { GrammarUtils } from "langium";
3
+ import { entries, filter, findLast, last } from "remeda";
4
+ import { TextEdit } from "vscode-languageserver-protocol";
5
+ import { ast } from "../ast.js";
6
+ const { findNodeForKeyword, findNodeForProperty } = GrammarUtils;
7
+ const asViewStyleRule = (target, style, indent = 0) => {
8
+ const indentStr = indent > 0 ? " ".repeat(indent) : "";
9
+ return [
10
+ indentStr + `style ${target} {`,
11
+ ...entries.strict(style).map(
12
+ ([key, value]) => indentStr + ` ${key} ${key === "opacity" ? value.toString() + "%" : value}`
13
+ ),
14
+ indentStr + `}`
15
+ ];
16
+ };
17
+ const isMatchingViewRule = (fqn, index) => (rule) => {
18
+ if (!ast.isViewRuleStyle(rule)) {
19
+ return false;
20
+ }
21
+ const [target, ...rest] = rule.targets;
22
+ if (!target || rest.length > 0 || !ast.isElementRef(target)) {
23
+ return false;
24
+ }
25
+ const ref = target.el.ref;
26
+ const _fqn = ref ? index.getFqn(ref) : null;
27
+ return _fqn === fqn;
28
+ };
29
+ export function changeElementStyle(services, {
30
+ view,
31
+ viewAst,
32
+ targets,
33
+ style
34
+ }) {
35
+ const viewCstNode = viewAst.$cstNode;
36
+ invariant(viewCstNode, "viewCstNode");
37
+ const insertPos = last(viewAst.body.rules)?.$cstNode?.range.end ?? viewAst.body.$cstNode?.range.end;
38
+ invariant(insertPos, "insertPos is not defined");
39
+ const indent = viewCstNode.range.start.character + 2;
40
+ const fqnIndex = services.likec4.FqnIndex;
41
+ const styleRules = filter(viewAst.body.rules, ast.isViewRuleStyle);
42
+ const viewOf = view.__ === "element" ? view.viewOf : null;
43
+ const existing = [];
44
+ const insert = [];
45
+ targets.forEach((target) => {
46
+ const rule = findLast(styleRules, isMatchingViewRule(target, fqnIndex));
47
+ const fqn = viewOf && isAncestor(viewOf, target) ? target.substring(viewOf.length + 1) : target;
48
+ if (rule) {
49
+ existing.push({ fqn, rule });
50
+ } else {
51
+ insert.push({ fqn });
52
+ }
53
+ });
54
+ const modifiedRange = {
55
+ start: insertPos,
56
+ end: insertPos
57
+ };
58
+ const includeRange = (range) => {
59
+ if (range.start.line <= modifiedRange.start.line) {
60
+ if (range.start.line == modifiedRange.start.line) {
61
+ modifiedRange.start.character = Math.min(range.start.character, modifiedRange.start.character);
62
+ } else {
63
+ modifiedRange.start = range.start;
64
+ }
65
+ }
66
+ if (range.end.line >= modifiedRange.end.line) {
67
+ if (range.end.line == modifiedRange.end.line) {
68
+ modifiedRange.end.character = Math.max(range.end.character, modifiedRange.end.character);
69
+ } else {
70
+ modifiedRange.end = range.end;
71
+ }
72
+ }
73
+ };
74
+ const edits = [];
75
+ if (insert.length > 0) {
76
+ const linesToInsert = insert.flatMap(({ fqn }) => asViewStyleRule(fqn, style, indent));
77
+ edits.push(
78
+ TextEdit.insert(
79
+ insertPos,
80
+ "\n" + linesToInsert.join("\n")
81
+ )
82
+ );
83
+ modifiedRange.start = {
84
+ line: insertPos.line + 1,
85
+ character: indent + 1
86
+ };
87
+ modifiedRange.end = {
88
+ line: insertPos.line + linesToInsert.length,
89
+ character: last(linesToInsert)?.length ?? 0
90
+ };
91
+ }
92
+ if (existing.length > 0) {
93
+ for (const { rule } of existing) {
94
+ const ruleCstNode = rule.$cstNode;
95
+ invariant(ruleCstNode, "RuleCstNode not found");
96
+ for (const [key, _value] of entries.strict(style)) {
97
+ const value = key === "opacity" ? _value.toString() + "%" : _value;
98
+ const ruleProp = rule.styleprops.find((p) => p.key === key);
99
+ if (ruleProp && ruleProp.$cstNode) {
100
+ const { range: { start, end } } = nonNullable(
101
+ findNodeForProperty(ruleProp.$cstNode, "value"),
102
+ "cant find value cst node"
103
+ );
104
+ includeRange({
105
+ start,
106
+ end: {
107
+ line: start.line,
108
+ character: start.character + value.length
109
+ }
110
+ });
111
+ edits.push(TextEdit.replace({ start, end }, value));
112
+ continue;
113
+ }
114
+ const insertPos2 = findNodeForKeyword(ruleCstNode, "{")?.range.end;
115
+ invariant(insertPos2, "Opening brace not found");
116
+ const indentStr = " ".repeat(2 + ruleCstNode.range.start.character);
117
+ const insertKeyValue = indentStr + key + " " + value;
118
+ edits.push(
119
+ TextEdit.insert(
120
+ insertPos2,
121
+ "\n" + insertKeyValue
122
+ )
123
+ );
124
+ includeRange({
125
+ start: {
126
+ line: insertPos2.line + 1,
127
+ character: indentStr.length
128
+ },
129
+ end: {
130
+ line: insertPos2.line + 1,
131
+ character: insertKeyValue.length
132
+ }
133
+ });
134
+ }
135
+ }
136
+ }
137
+ return {
138
+ modifiedRange,
139
+ edits
140
+ };
141
+ }
@@ -1,13 +1,13 @@
1
1
  import { type AutoLayoutDirection } from '@likec4/core';
2
2
  import { TextEdit } from 'vscode-languageserver-protocol';
3
- import { ast, type ParsedAstElementView, type ParsedLikeC4LangiumDocument } from '../ast';
3
+ import { ast, type ParsedAstView, type ParsedLikeC4LangiumDocument } from '../ast';
4
4
  import type { LikeC4Services } from '../module';
5
5
  type ChangeViewLayoutArg = {
6
- view: ParsedAstElementView;
6
+ view: ParsedAstView;
7
7
  doc: ParsedLikeC4LangiumDocument;
8
- viewAst: ast.ElementView;
8
+ viewAst: ast.LikeC4View;
9
9
  layout: AutoLayoutDirection;
10
10
  };
11
- export declare function changeViewLayout(services: LikeC4Services, { view, viewAst, layout }: ChangeViewLayoutArg): TextEdit[];
11
+ export declare function changeViewLayout(services: LikeC4Services, { viewAst, layout }: ChangeViewLayoutArg): TextEdit;
12
12
  export {};
13
13
  //# sourceMappingURL=changeViewLayout.d.ts.map
@@ -5,7 +5,6 @@ import { TextEdit } from "vscode-languageserver-protocol";
5
5
  import { ast, toAstViewLayoutDirection } from "../ast.js";
6
6
  const { findNodeForProperty } = GrammarUtils;
7
7
  export function changeViewLayout(services, {
8
- view,
9
8
  viewAst,
10
9
  layout
11
10
  }) {
@@ -16,15 +15,15 @@ export function changeViewLayout(services, {
16
15
  if (existingRule && existingRule.$cstNode) {
17
16
  const directionCstNode = findNodeForProperty(existingRule.$cstNode, "direction");
18
17
  if (directionCstNode) {
19
- return [TextEdit.replace(directionCstNode.range, newlayout)];
18
+ return TextEdit.replace(directionCstNode.range, newlayout);
20
19
  }
21
- return [TextEdit.replace(existingRule.$cstNode.range, `autoLayout ${newlayout}`)];
20
+ return TextEdit.replace(existingRule.$cstNode.range, `autoLayout ${newlayout}`);
22
21
  }
23
- const insertPos = last(viewAst.body.rules)?.$cstNode?.range.end ?? last(viewAst.body.props)?.$cstNode?.range.end ?? viewAst.body.$cstNode?.range.start;
22
+ const insertPos = last(viewAst.body.rules)?.$cstNode?.range.end ?? viewAst.body.$cstNode?.range.end;
24
23
  invariant(insertPos, "insertPos is not defined");
25
24
  const indent = " ".repeat(2 + viewCstNode.range.start.character);
26
25
  const insert = `
27
26
 
28
27
  ${indent}autoLayout ${newlayout}`;
29
- return [TextEdit.insert(insertPos, insert)];
28
+ return TextEdit.insert(insertPos, insert);
30
29
  }
@@ -1,4 +1,4 @@
1
- import type { AutoLayoutDirection, ComputedView, ElementShape, Fqn, LikeC4ComputedModel, LikeC4Model, NonEmptyArray, RelationID, ThemeColor, ViewID } from '@likec4/core';
1
+ import type { AutoLayoutDirection, BorderStyle, 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>;
@@ -33,22 +33,22 @@ export type LocateParams = {
33
33
  export declare const locate: RequestType<LocateParams, Location | null, void>;
34
34
  export type LocateRequest = typeof locate;
35
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
36
  interface ChangeAutoLayout {
47
37
  op: 'change-autolayout';
48
38
  layout: AutoLayoutDirection;
49
39
  }
40
+ interface ChangeElementStyle {
41
+ op: 'change-element-style';
42
+ style: {
43
+ border?: BorderStyle;
44
+ opacity?: number;
45
+ shape?: ElementShape;
46
+ color?: ThemeColor;
47
+ };
48
+ targets: NonEmptyArray<Fqn>;
49
+ }
50
50
  }
51
- export type ChangeView = ChangeView.ChangeColor | ChangeView.ChangeShape | ChangeView.ChangeAutoLayout;
51
+ export type ChangeView = ChangeView.ChangeAutoLayout | ChangeView.ChangeElementStyle;
52
52
  export interface ChangeViewRequestParams {
53
53
  viewId: ViewID;
54
54
  changes: NonEmptyArray<ChangeView>;
@@ -6,52 +6,55 @@ import { isTruthy } from "remeda";
6
6
  import { ast } from "../ast.js";
7
7
  import { logError } from "../logger.js";
8
8
  export class LikeC4ScopeComputation extends DefaultScopeComputation {
9
- computeExports(document, _cancelToken) {
10
- return new Promise((resolve) => {
11
- const docExports = [];
9
+ async computeExports(document, _cancelToken) {
10
+ const docExports = [];
11
+ try {
12
12
  const { specifications, models, views } = document.parseResult.value;
13
- try {
14
- for (const spec of specifications.flatMap((s) => [...s.elements, ...s.relationships])) {
13
+ for (const spec of specifications.flatMap((s) => [
14
+ ...s.elements,
15
+ ...s.relationships,
16
+ ...s.tags
17
+ ])) {
18
+ try {
19
+ if (ast.isSpecificationTag(spec)) {
20
+ if (spec.tag && isTruthy(spec.tag.name)) {
21
+ docExports.push(
22
+ this.descriptions.createDescription(spec.tag, "#" + spec.tag.name, document)
23
+ );
24
+ }
25
+ continue;
26
+ }
15
27
  if (spec.kind && isTruthy(spec.kind.name)) {
16
28
  docExports.push(
17
29
  this.descriptions.createDescription(spec.kind, spec.kind.name, document)
18
30
  );
19
31
  }
32
+ } catch (e) {
33
+ logError(e);
20
34
  }
21
- } catch (e) {
22
- logError(e);
23
- }
24
- try {
25
- for (const spec of specifications.flatMap((s) => s.tags)) {
26
- if (spec.tag && isTruthy(spec.tag.name)) {
27
- docExports.push(
28
- this.descriptions.createDescription(spec.tag, "#" + spec.tag.name, document)
29
- );
30
- }
31
- }
32
- } catch (e) {
33
- logError(e);
34
35
  }
35
- try {
36
- for (const elAst of models.flatMap((m) => m.elements)) {
36
+ for (const elAst of models.flatMap((m) => m.elements)) {
37
+ try {
37
38
  if (ast.isElement(elAst) && isTruthy(elAst.name)) {
38
39
  docExports.push(this.descriptions.createDescription(elAst, elAst.name, document));
39
40
  }
41
+ } catch (e) {
42
+ logError(e);
40
43
  }
41
- } catch (e) {
42
- logError(e);
43
44
  }
44
- try {
45
- for (const viewAst of views.flatMap((v) => v.views)) {
45
+ for (const viewAst of views.flatMap((v) => v.views)) {
46
+ try {
46
47
  if (isTruthy(viewAst.name)) {
47
48
  docExports.push(this.descriptions.createDescription(viewAst, viewAst.name, document));
48
49
  }
50
+ } catch (e) {
51
+ logError(e);
49
52
  }
50
- } catch (e) {
51
- logError(e);
52
53
  }
53
- resolve(docExports);
54
- });
54
+ } catch (e) {
55
+ logError(e);
56
+ }
57
+ return docExports;
55
58
  }
56
59
  computeLocalScopes(document, _cancelToken) {
57
60
  return new Promise((resolve) => {
@@ -95,9 +98,11 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
95
98
  }
96
99
  }
97
100
  }
98
- for (const [name, descriptions] of nestedScopes.entriesGroupedByKey()) {
99
- if (!localScope.has(name) && descriptions.length === 1) {
100
- localScope.add(name, descriptions[0]);
101
+ if (nestedScopes.size > 0) {
102
+ for (const [name, descriptions] of nestedScopes.entriesGroupedByKey()) {
103
+ if (!localScope.has(name) && descriptions.length === 1) {
104
+ localScope.add(name, descriptions[0]);
105
+ }
101
106
  }
102
107
  }
103
108
  scopes.addAll(container, localScope.values());
@@ -17,7 +17,7 @@ export class NodeKindProvider {
17
17
  case (ast.isModel(node) || ast.isModelViews(node) || ast.isSpecificationRule(node) || hasType(ast.Model) || hasType(ast.ModelViews) || hasType(ast.SpecificationRule)): {
18
18
  return SymbolKind.Namespace;
19
19
  }
20
- case (ast.isElementView(node) || hasType(ast.ElementView)): {
20
+ case (ast.isLikeC4View(node) || hasType(ast.LikeC4View)): {
21
21
  return SymbolKind.Class;
22
22
  }
23
23
  case (ast.isTag(node) || hasType(ast.Tag) || (ast.isSpecificationTag(node) || hasType(ast.SpecificationTag))): {
@@ -43,6 +43,8 @@ export class NodeKindProvider {
43
43
  return CompletionItemKind.Module;
44
44
  case SymbolKind.Class:
45
45
  return CompletionItemKind.Class;
46
+ case SymbolKind.Enum:
47
+ return CompletionItemKind.Enum;
46
48
  case SymbolKind.EnumMember:
47
49
  return CompletionItemKind.EnumMember;
48
50
  case SymbolKind.TypeParameter:
@@ -52,7 +54,7 @@ export class NodeKindProvider {
52
54
  case SymbolKind.Event:
53
55
  return CompletionItemKind.Event;
54
56
  default:
55
- return CompletionItemKind.Keyword;
57
+ return CompletionItemKind.Reference;
56
58
  }
57
59
  }
58
60
  }
@@ -0,0 +1,5 @@
1
+ import type { ValidationCheck } from 'langium';
2
+ import { ast } from '../ast';
3
+ import type { LikeC4Services } from '../module';
4
+ export declare const dynamicViewRulePredicate: (_services: LikeC4Services) => ValidationCheck<ast.DynamicViewRulePredicate>;
5
+ //# sourceMappingURL=dynamic-view-rule.d.ts.map
@@ -0,0 +1,32 @@
1
+ import { nonexhaustive } from "@likec4/core";
2
+ import { ast } from "../ast.js";
3
+ import { logError } from "../logger.js";
4
+ export const dynamicViewRulePredicate = (_services) => {
5
+ return (el, accept) => {
6
+ try {
7
+ for (const expr of el.expressions) {
8
+ switch (true) {
9
+ case ast.isElementRef(expr):
10
+ case ast.isDescedantsExpr(expr):
11
+ case ast.isCustomElementExpr(expr):
12
+ case ast.isExpandElementExpr(expr):
13
+ return;
14
+ case ast.isRelationExpr(expr):
15
+ case ast.isInOutExpr(expr):
16
+ case ast.isIncomingExpr(expr):
17
+ case ast.isOutgoingExpr(expr):
18
+ case ast.isElementKindExpr(expr):
19
+ case ast.isElementTagExpr(expr):
20
+ case ast.isWildcardExpr(expr):
21
+ return accept("warning", `Expression is not supported by dynamic views`, {
22
+ node: expr
23
+ });
24
+ default:
25
+ nonexhaustive(expr);
26
+ }
27
+ }
28
+ } catch (e) {
29
+ logError(e);
30
+ }
31
+ };
32
+ };
@@ -0,0 +1,5 @@
1
+ import type { ValidationCheck } from 'langium';
2
+ import { ast } from '../ast';
3
+ import type { LikeC4Services } from '../module';
4
+ export declare const dynamicViewStep: (services: LikeC4Services) => ValidationCheck<ast.DynamicViewStep>;
5
+ //# sourceMappingURL=dynamic-view-step.d.ts.map
@@ -0,0 +1,33 @@
1
+ import { isAncestor } from "@likec4/core";
2
+ import { elementRef } from "../elementRef.js";
3
+ import { logError } from "../logger.js";
4
+ export const dynamicViewStep = (services) => {
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);
31
+ }
32
+ };
33
+ };
@@ -1,4 +1,6 @@
1
1
  import { logger } from "../logger.js";
2
+ import { dynamicViewRulePredicate } from "./dynamic-view-rule.js";
3
+ import { dynamicViewStep } from "./dynamic-view-step.js";
2
4
  import { elementChecks } from "./element.js";
3
5
  import { opacityPropertyRuleChecks } from "./property-checks.js";
4
6
  import { relationChecks } from "./relation.js";
@@ -25,11 +27,13 @@ export function registerValidationChecks(services) {
25
27
  SpecificationRule: specificationRuleChecks(services),
26
28
  Model: modelRuleChecks(services),
27
29
  ModelViews: modelViewsChecks(services),
28
- ElementView: viewChecks(services),
30
+ DynamicViewStep: dynamicViewStep(services),
31
+ LikeC4View: viewChecks(services),
29
32
  Element: elementChecks(services),
30
33
  ElementKind: elementKindChecks(services),
31
34
  Relation: relationChecks(services),
32
35
  Tag: tagChecks(services),
36
+ DynamicViewRulePredicate: dynamicViewRulePredicate(services),
33
37
  CustomElementExpr: customElementExprChecks(services),
34
38
  ExpandElementExpr: expandElementExprChecks(services),
35
39
  RelationshipKind: relationshipChecks(services),
@@ -4,6 +4,7 @@ export const expandElementExprChecks = (_services) => {
4
4
  return (el, accept) => {
5
5
  switch (true) {
6
6
  case ast.isIncludePredicate(el.$container):
7
+ case ast.isDynamicViewRulePredicate(el.$container):
7
8
  case ast.isViewRuleStyle(el.$container):
8
9
  return;
9
10
  case ast.isCustomElementExpr(el.$container):
@@ -1,10 +1,10 @@
1
- import { isNil } from "remeda";
1
+ import { isNullish } from "remeda";
2
2
  import { ast } from "../../ast.js";
3
3
  export const outgoingExpressionChecks = (_services) => {
4
4
  return (el, accept) => {
5
5
  if (ast.isWildcardExpr(el.from)) {
6
6
  const view = el.$container.$container.$container;
7
- if (isNil(view.viewOf)) {
7
+ if (view.$type === "ElementView" && isNullish(view.viewOf)) {
8
8
  accept("warning", "Predicate is ignored as it concerns all relationships", {
9
9
  node: el
10
10
  });
@@ -1,5 +1,5 @@
1
1
  import type { ValidationCheck } from 'langium';
2
2
  import { ast } from '../ast';
3
3
  import type { LikeC4Services } from '../module';
4
- export declare const viewChecks: (services: LikeC4Services) => ValidationCheck<ast.ElementView>;
4
+ export declare const viewChecks: (services: LikeC4Services) => ValidationCheck<ast.LikeC4View>;
5
5
  //# sourceMappingURL=view.d.ts.map
@@ -2,12 +2,10 @@ import { ast } from "../ast.js";
2
2
  export const viewChecks = (services) => {
3
3
  const index = services.shared.workspace.IndexManager;
4
4
  return (el, accept) => {
5
- if (el.extends) {
6
- }
7
5
  if (!el.name) {
8
6
  return;
9
7
  }
10
- const anotherViews = index.allElements(ast.ElementView).filter((n) => n.name === el.name).limit(2).count();
8
+ const anotherViews = index.allElements(ast.LikeC4View).filter((n) => n.name === el.name).limit(2).count();
11
9
  if (anotherViews > 1) {
12
10
  accept("error", `Duplicate view '${el.name}'`, {
13
11
  node: el,
@@ -1,3 +1,3 @@
1
- import type { ComputedView } from '@likec4/core';
1
+ import { type ComputedView } from '@likec4/core';
2
2
  export declare function assignNavigateTo<R extends Iterable<ComputedView>>(views: R): R;
3
3
  //# sourceMappingURL=assignNavigateTo.d.ts.map
@@ -1,8 +1,9 @@
1
+ import { isComputedElementView } from "@likec4/core";
1
2
  import { find } from "remeda";
2
3
  export function assignNavigateTo(views) {
3
4
  const allElementViews = /* @__PURE__ */ new Map();
4
5
  for (const v of views) {
5
- if (v.viewOf && !v.extends) {
6
+ if (isComputedElementView(v) && v.viewOf && !v.extends) {
6
7
  const viewsOf = allElementViews.get(v.viewOf) ?? [];
7
8
  viewsOf.push(v.id);
8
9
  allElementViews.set(v.viewOf, viewsOf);
@@ -1,7 +1,7 @@
1
- import { type ElementView } from '@likec4/core';
1
+ import { type View } from '@likec4/core';
2
2
  /**
3
3
  * Resolve rules of extended views
4
4
  * (Removes invalid views)
5
5
  */
6
- export declare function resolveRulesExtendedViews<V extends Record<any, ElementView>>(unresolvedViews: V): V;
6
+ export declare function resolveRulesExtendedViews<V extends Record<any, View>>(unresolvedViews: V): V;
7
7
  //# sourceMappingURL=resolve-extended-views.d.ts.map
@@ -1,3 +1,3 @@
1
- import type { ElementView } from '@likec4/core';
2
- export declare function resolveRelativePaths(views: ElementView[]): ElementView[];
1
+ import type { View } from '@likec4/core';
2
+ export declare function resolveRelativePaths(views: View[]): View[];
3
3
  //# sourceMappingURL=resolve-relative-paths.d.ts.map
@@ -1,10 +1,9 @@
1
1
  import { invariant } from "@likec4/core";
2
- import { uniq, zip } from "rambdax";
3
- import { hasAtLeast } from "remeda";
2
+ import { hasAtLeast, unique, zip } from "remeda";
4
3
  function commonAncestorPath(views, sep = "/") {
5
4
  if (views.length <= 1)
6
5
  return "";
7
- const uniqURIs = uniq(views.flatMap(({ docUri }) => docUri ? [docUri] : []));
6
+ const uniqURIs = unique(views.flatMap(({ docUri }) => docUri ? [docUri] : []));
8
7
  if (uniqURIs.length === 0)
9
8
  return "";
10
9
  if (uniqURIs.length === 1) {
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.1.0",
4
+ "version": "1.2.0",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -61,7 +61,7 @@
61
61
  }
62
62
  },
63
63
  "scripts": {
64
- "typecheck": "tsc --noEmit",
64
+ "typecheck": "tsc -b",
65
65
  "watch:langium": "langium generate --watch",
66
66
  "watch:ts": "tsc --watch",
67
67
  "generate": "langium generate",
@@ -74,8 +74,8 @@
74
74
  "test": "vitest run"
75
75
  },
76
76
  "dependencies": {
77
- "@likec4/core": "1.1.0",
78
- "@likec4/graph": "1.1.0",
77
+ "@likec4/core": "1.2.0",
78
+ "@likec4/graph": "1.2.0",
79
79
  "@total-typescript/ts-reset": "^0.5.1",
80
80
  "fast-equals": "^5.0.1",
81
81
  "langium": "^3.0.0",
@@ -92,7 +92,7 @@
92
92
  "vscode-uri": "3.0.8"
93
93
  },
94
94
  "devDependencies": {
95
- "@types/node": "^20.12.7",
95
+ "@types/node": "^20.13.0",
96
96
  "@types/object-hash": "^3.0.6",
97
97
  "@types/string-hash": "^1",
98
98
  "execa": "^9.1.0",
@@ -102,5 +102,5 @@
102
102
  "unbuild": "^2.0.0",
103
103
  "vitest": "~1.5.2"
104
104
  },
105
- "packageManager": "yarn@4.2.2"
105
+ "packageManager": "yarn@4.3.0"
106
106
  }
@@ -1,15 +0,0 @@
1
- import { type Fqn, type NonEmptyArray } from '@likec4/core';
2
- import { TextEdit } from 'vscode-languageserver-protocol';
3
- import { ast, type ParsedAstElementView, type ParsedLikeC4LangiumDocument } from '../ast';
4
- import type { LikeC4Services } from '../module';
5
- 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