@likec4/language-server 0.6.3 → 0.18.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 (71) hide show
  1. package/contrib/likec4.monarch.ts +1 -1
  2. package/contrib/likec4.tmLanguage.json +1 -1
  3. package/dist/ast.d.ts +4 -2
  4. package/dist/ast.js +13 -3
  5. package/dist/builtin.d.ts +1 -0
  6. package/dist/builtin.js +1 -0
  7. package/dist/elementRef.d.ts +2 -1
  8. package/dist/elementRef.js +1 -0
  9. package/dist/generated/ast.d.ts +12 -6
  10. package/dist/generated/ast.js +14 -2
  11. package/dist/generated/grammar.d.ts +3 -2
  12. package/dist/generated/grammar.js +79 -37
  13. package/dist/generated/module.d.ts +3 -2
  14. package/dist/generated/module.js +2 -2
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +1 -0
  17. package/dist/logger.d.ts +1 -0
  18. package/dist/logger.js +1 -0
  19. package/dist/lsp/DocumentSymbolProvider.d.ts +2 -1
  20. package/dist/lsp/DocumentSymbolProvider.js +29 -11
  21. package/dist/lsp/HoverProvider.d.ts +2 -1
  22. package/dist/lsp/HoverProvider.js +1 -0
  23. package/dist/lsp/SemanticTokenProvider.d.ts +1 -0
  24. package/dist/lsp/SemanticTokenProvider.js +9 -44
  25. package/dist/lsp/index.d.ts +1 -0
  26. package/dist/lsp/index.js +1 -0
  27. package/dist/model/fqn-computation.d.ts +4 -0
  28. package/dist/model/fqn-computation.js +41 -0
  29. package/dist/model/fqn-index.d.ts +36 -11
  30. package/dist/model/fqn-index.js +50 -89
  31. package/dist/model/index.d.ts +2 -0
  32. package/dist/model/index.js +2 -0
  33. package/dist/model/model-builder.d.ts +1 -0
  34. package/dist/model/model-builder.js +44 -30
  35. package/dist/model/model-locator.d.ts +1 -0
  36. package/dist/model/model-locator.js +24 -16
  37. package/dist/module.d.ts +1 -0
  38. package/dist/module.js +1 -4
  39. package/dist/protocol.d.ts +36 -0
  40. package/dist/protocol.js +22 -0
  41. package/dist/references/index.d.ts +1 -0
  42. package/dist/references/index.js +1 -0
  43. package/dist/references/scope-computation.d.ts +5 -1
  44. package/dist/references/scope-computation.js +11 -3
  45. package/dist/references/scope-provider.d.ts +4 -0
  46. package/dist/references/scope-provider.js +40 -30
  47. package/dist/registerProtocolHandlers.d.ts +1 -0
  48. package/dist/registerProtocolHandlers.js +48 -17
  49. package/dist/shared/CodeLensProvider.d.ts +2 -1
  50. package/dist/shared/CodeLensProvider.js +1 -0
  51. package/dist/shared/WorkspaceManager.d.ts +3 -2
  52. package/dist/shared/WorkspaceManager.js +3 -4
  53. package/dist/shared/index.d.ts +1 -0
  54. package/dist/shared/index.js +1 -0
  55. package/dist/test/index.d.ts +2 -0
  56. package/dist/test/index.js +2 -0
  57. package/dist/test/testServices.d.ts +16 -0
  58. package/dist/test/testServices.js +58 -0
  59. package/dist/utils.d.ts +1 -0
  60. package/dist/utils.js +1 -0
  61. package/dist/validation/element.d.ts +1 -0
  62. package/dist/validation/element.js +9 -2
  63. package/dist/validation/index.d.ts +1 -0
  64. package/dist/validation/index.js +1 -0
  65. package/dist/validation/relation.d.ts +1 -0
  66. package/dist/validation/relation.js +1 -0
  67. package/dist/validation/specification.d.ts +1 -0
  68. package/dist/validation/specification.js +1 -0
  69. package/dist/validation/view.d.ts +1 -0
  70. package/dist/validation/view.js +1 -0
  71. package/package.json +24 -42
@@ -1,5 +1,5 @@
1
1
  import { AbstractSemanticTokenProvider } from 'langium';
2
- import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver-protocol';
2
+ import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver';
3
3
  import { ast } from '../ast';
4
4
  import { isElementRefHead } from '../elementRef';
5
5
  export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
@@ -135,46 +135,9 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
135
135
  if (ast.isElement(node)) {
136
136
  return this.highlightAstElement(node, acceptor);
137
137
  }
138
- // if (ast.isExtendElement(node)) {
139
- // keyword('extend')
140
- // return
141
- // }
142
- // if (ast.isElementProperty(node) || ast.isRelationProperty(node) || ast.isViewProperty(node)) {
143
- // acceptor({
144
- // node,
145
- // property: 'key',
146
- // type: SemanticTokenTypes.property,
147
- // modifier: [SemanticTokenModifiers.definition]
148
- // })
149
- // if ('value' in node) {
150
- // acceptor({
151
- // node,
152
- // property: 'value',
153
- // type: SemanticTokenTypes.string
154
- // })
155
- // }
156
- // return
157
- // }
158
138
  if (ast.isView(node)) {
159
139
  return this.highlightView(node, acceptor);
160
140
  }
161
- // if (ast.isDynamicViewSteps(node)) {
162
- // keyword('steps')
163
- // return
164
- // }
165
- // if (ast.isViewRuleAutoLayout(node)) {
166
- // keyword('autoLayout')
167
- // return
168
- // }
169
- // if (ast.isViewRuleStyle(node)) {
170
- // keyword('style')
171
- // return
172
- // }
173
- // if (ast.isViewRuleExpression(node)) {
174
- // keyword(node.isInclude ? 'include' : 'exclude')
175
- // return
176
- // }
177
- // //
178
141
  }
179
142
  highlightAstElement(node, acceptor) {
180
143
  acceptor({
@@ -198,11 +161,17 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
198
161
  }
199
162
  }
200
163
  highlightView(node, acceptor) {
164
+ acceptor({
165
+ node,
166
+ keyword: 'view',
167
+ type: SemanticTokenTypes.keyword
168
+ });
201
169
  if (node.name) {
202
170
  acceptor({
203
171
  node,
204
172
  property: 'name',
205
- type: SemanticTokenTypes.variable
173
+ type: SemanticTokenTypes.variable,
174
+ modifier: [SemanticTokenModifiers.declaration]
206
175
  });
207
176
  }
208
177
  if (node.viewOf) {
@@ -212,10 +181,6 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
212
181
  type: SemanticTokenTypes.keyword
213
182
  });
214
183
  }
215
- acceptor({
216
- node,
217
- keyword: 'view',
218
- type: SemanticTokenTypes.keyword
219
- });
220
184
  }
221
185
  }
186
+ //# sourceMappingURL=SemanticTokenProvider.js.map
@@ -1,3 +1,4 @@
1
1
  export * from './DocumentSymbolProvider';
2
2
  export * from './HoverProvider';
3
3
  export * from './SemanticTokenProvider';
4
+ //# sourceMappingURL=index.d.ts.map
package/dist/lsp/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './DocumentSymbolProvider';
2
2
  export * from './HoverProvider';
3
3
  export * from './SemanticTokenProvider';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import { type LikeC4LangiumDocument } from '../ast';
2
+ import type { LikeC4Services } from '../module';
3
+ export declare function computeDocumentFqn(document: LikeC4LangiumDocument, services: LikeC4Services): void;
4
+ //# sourceMappingURL=fqn-computation.d.ts.map
@@ -0,0 +1,41 @@
1
+ import { failExpectedNever } from '@likec4/core';
2
+ import { Fqn } from '@likec4/core/types';
3
+ import { MultiMap } from 'langium';
4
+ import { isEmpty, isNil } from 'rambdax';
5
+ import { ElementOps, ast } from '../ast';
6
+ import { strictElementRefFqn } from '../elementRef';
7
+ export function computeDocumentFqn(document, services) {
8
+ const c4fqns = document.c4fqns = new MultiMap();
9
+ const { model } = document.parseResult.value;
10
+ if (!model?.elements) {
11
+ return;
12
+ }
13
+ const locator = services.workspace.AstNodeLocator;
14
+ const traverseStack = model.elements.map(el => [el, null]);
15
+ let pair;
16
+ while ((pair = traverseStack.shift())) {
17
+ const [el, parent] = pair;
18
+ if (ast.isRelation(el)) {
19
+ continue;
20
+ }
21
+ if (ast.isExtendElement(el)) {
22
+ if (!isNil(el.body) && !isEmpty(el.body.elements)) {
23
+ const fqn = strictElementRefFqn(el.element);
24
+ el.body.elements.forEach(child => traverseStack.push([child, fqn]));
25
+ }
26
+ continue;
27
+ }
28
+ if (ast.isElement(el)) {
29
+ const fqn = Fqn(el.name, parent);
30
+ const path = locator.getAstNodePath(el);
31
+ c4fqns.add(fqn, path);
32
+ ElementOps.writeId(el, fqn);
33
+ if (!isNil(el.body) && !isEmpty(el.body.elements)) {
34
+ el.body.elements.forEach(child => traverseStack.push([child, fqn]));
35
+ }
36
+ continue;
37
+ }
38
+ failExpectedNever(el);
39
+ }
40
+ }
41
+ //# sourceMappingURL=fqn-computation.js.map
@@ -1,17 +1,42 @@
1
- import { StreamImpl } from 'langium';
2
- import type { AstNodeDescription, AstNodeDescriptionProvider } from 'langium';
3
- import { ast } from '../ast';
1
+ import type { Fqn } from '@likec4/core/types';
2
+ import type { LangiumDocuments } from 'langium';
3
+ import { DocumentState, MultiMap, StreamImpl } from 'langium';
4
+ import type { SetNonNullable } from 'type-fest';
5
+ import type { ast } from '../ast';
6
+ import { type LikeC4LangiumDocument } from '../ast';
4
7
  import type { LikeC4Services } from '../module';
5
- import { Fqn } from '@likec4/core/types';
8
+ export interface FqnIndexEntry {
9
+ fqn: Fqn;
10
+ name: string;
11
+ doc: LikeC4LangiumDocument;
12
+ path: string;
13
+ }
6
14
  export declare class FqnIndex {
7
- #private;
8
15
  private services;
9
- protected readonly descriptions: AstNodeDescriptionProvider;
16
+ protected langiumDocuments: LangiumDocuments;
10
17
  constructor(services: LikeC4Services);
18
+ private documents;
19
+ private entries;
11
20
  get(el: ast.Element): Fqn | null;
12
- byFqn(fqn: Fqn): readonly AstNodeDescription[];
13
- directChildrenOf(parent: Fqn): import("langium").Stream<AstNodeDescription>;
14
- uniqueDescedants(parent: Fqn): StreamImpl<IterableIterator<AstNodeDescription> | null, AstNodeDescription>;
15
- private doIndexElements;
16
- private cleanIndexedElements;
21
+ byFqn(fqn: Fqn): import("langium").Stream<{
22
+ path: string;
23
+ doc: SetNonNullable<{
24
+ c4hash?: string;
25
+ c4Specification: import("../ast").ParsedAstSpecification;
26
+ c4Elements: import("../ast").ParsedAstElement[];
27
+ c4Relations: import("../ast").ParsedAstRelation[];
28
+ c4Views: import("../ast").ParsedAstElementView[];
29
+ readonly uri: import("vscode-uri").URI;
30
+ readonly textDocument: import("vscode-languageserver-textdocument").TextDocument;
31
+ state: DocumentState;
32
+ parseResult: import("langium").ParseResult<ast.LikeC4Document>;
33
+ precomputedScopes?: import("langium").PrecomputedScopes;
34
+ references: import("langium").Reference<import("langium").AstNode>[];
35
+ diagnostics?: import("vscode-languageserver-types").Diagnostic[];
36
+ c4fqns: MultiMap<Fqn, string> | undefined;
37
+ }, "c4fqns">;
38
+ }>;
39
+ directChildrenOf(parent: Fqn): import("langium").Stream<FqnIndexEntry>;
40
+ uniqueDescedants(parent: Fqn): StreamImpl<IterableIterator<FqnIndexEntry> | null, FqnIndexEntry>;
17
41
  }
42
+ //# sourceMappingURL=fqn-index.d.ts.map
@@ -1,48 +1,60 @@
1
- import { DocumentState, DONE_RESULT, MultiMap, StreamImpl } from 'langium';
2
- import { ast, ElementOps } from '../ast';
1
+ import { nameFromFqn, parentFqn } from '@likec4/core/utils';
2
+ import { DocumentState, DONE_RESULT, getDocument, MultiMap, StreamImpl } from 'langium';
3
+ import { isNil } from 'rambdax';
4
+ import { ElementOps, isLikeC4LangiumDocument } from '../ast';
3
5
  import { logger } from '../logger';
4
- import { parentFqn } from '@likec4/core/utils';
5
- import { Fqn } from '@likec4/core/types';
6
- import { strictElementRefFqn } from '../elementRef';
6
+ import { computeDocumentFqn } from './fqn-computation';
7
+ const isFqnIndexedDocument = (doc) => isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns);
7
8
  export class FqnIndex {
8
9
  services;
9
- // #fqnMap = new WeakMap<ast.Element, Fqn>()
10
- #index = new MultiMap();
11
- descriptions;
10
+ langiumDocuments;
12
11
  constructor(services) {
13
12
  this.services = services;
14
- this.descriptions = services.workspace.AstNodeDescriptionProvider;
15
- services.shared.workspace.DocumentBuilder.onUpdate((_changed, removed) => {
16
- for (const uri of [...removed]) {
17
- this.cleanIndexedElements(uri);
18
- }
19
- });
20
- services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.ComputedScopes, (docs, _cancelToken) => {
13
+ this.langiumDocuments = services.shared.workspace.LangiumDocuments;
14
+ services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, (docs, _cancelToken) => {
21
15
  for (const doc of docs) {
22
- this.cleanIndexedElements(doc.uri);
23
- this.doIndexElements(doc);
16
+ if (isLikeC4LangiumDocument(doc)) {
17
+ try {
18
+ computeDocumentFqn(doc, services);
19
+ }
20
+ catch (e) {
21
+ logger.error(e);
22
+ }
23
+ }
24
24
  }
25
25
  });
26
26
  }
27
+ documents() {
28
+ return this.langiumDocuments.all.filter(isFqnIndexedDocument);
29
+ }
30
+ entries() {
31
+ return this.documents().flatMap(doc => doc.c4fqns.entries().map(([fqn, path]) => ({ fqn, path, doc })));
32
+ }
27
33
  get(el) {
28
34
  let fqn = ElementOps.readId(el) ?? null;
29
- // if ()
30
- // let fqn = this.#fqnMap.get(el) ?? null
31
- if (fqn && !this.#index.has(fqn)) {
35
+ if (fqn) {
36
+ const doc = getDocument(el);
37
+ if (isFqnIndexedDocument(doc) && doc.c4fqns.has(fqn)) {
38
+ return fqn;
39
+ }
32
40
  const path = this.services.workspace.AstNodeLocator.getAstNodePath(el);
33
41
  logger.error(`Clean cached FQN ${fqn} at ${path}`);
34
- // this.#fqnMap.delete(el)
42
+ ElementOps.writeId(el, null);
35
43
  fqn = null;
36
44
  }
37
45
  return fqn;
38
46
  }
39
47
  byFqn(fqn) {
40
- return this.#index.get(fqn);
48
+ return this.documents()
49
+ .flatMap(doc => {
50
+ return doc.c4fqns.get(fqn).map(path => ({ path, doc }));
51
+ });
41
52
  }
42
53
  directChildrenOf(parent) {
43
- return this.#index
44
- .entriesGroupedByKey()
45
- .flatMap(([fqn, descrs]) => (descrs.length === 1 && parentFqn(fqn) === parent ? descrs : []));
54
+ return this
55
+ .entries()
56
+ .filter(e => parentFqn(e.fqn) === parent)
57
+ .map((e) => ({ ...e, name: nameFromFqn(e.fqn) }));
46
58
  }
47
59
  uniqueDescedants(parent) {
48
60
  return new StreamImpl(() => {
@@ -50,24 +62,26 @@ export class FqnIndex {
50
62
  const children = [];
51
63
  const childrenNames = new Set();
52
64
  const descedants = [];
53
- this.#index.entries().forEach(([fqn, desc]) => {
54
- if (fqn.startsWith(prefix)) {
55
- if (parentFqn(fqn) === parent) {
56
- childrenNames.add(desc.name);
57
- children.push([fqn, desc]);
65
+ this.entries().forEach(e => {
66
+ if (e.fqn.startsWith(prefix)) {
67
+ const name = nameFromFqn(e.fqn);
68
+ const entry = { ...e, name };
69
+ if (parentFqn(e.fqn) === parent) {
70
+ childrenNames.add(name);
71
+ children.push(entry);
58
72
  }
59
73
  else {
60
- descedants.push([fqn, desc]);
74
+ descedants.push(entry);
61
75
  }
62
76
  }
63
77
  });
64
78
  if (children.length + descedants.length === 0) {
65
79
  return null;
66
80
  }
67
- const nested = new MultiMap(children.map(([_fqn, desc]) => [desc.name, desc]));
68
- for (const [_, indexed] of descedants) {
69
- if (!childrenNames.has(indexed.name)) {
70
- nested.add(indexed.name, indexed);
81
+ const nested = new MultiMap(children.map(entry => [entry.name, entry]));
82
+ for (const descedant of descedants) {
83
+ if (!childrenNames.has(descedant.name)) {
84
+ nested.add(descedant.name, descedant);
71
85
  }
72
86
  }
73
87
  return nested
@@ -81,58 +95,5 @@ export class FqnIndex {
81
95
  return DONE_RESULT;
82
96
  });
83
97
  }
84
- doIndexElements(doc) {
85
- const visitElement = (element, parent = null) => {
86
- try {
87
- const name = element.name;
88
- const fqn = Fqn(name, parent);
89
- this.#index.add(fqn, this.descriptions.createDescription(element, name, doc));
90
- ElementOps.writeId(element, fqn);
91
- // this.#fqnMap.set(element, fqn)
92
- if (element.body) {
93
- for (const nested of element.body.elements) {
94
- if (ast.isElement(nested)) {
95
- visitElement(nested, fqn);
96
- }
97
- }
98
- }
99
- }
100
- catch (e) {
101
- logger.warn(e);
102
- }
103
- };
104
- const visitExtendElement = (extendElement) => {
105
- try {
106
- const fqn = strictElementRefFqn(extendElement.element);
107
- for (const nested of extendElement.body.elements) {
108
- if (ast.isElement(nested)) {
109
- visitElement(nested, fqn);
110
- }
111
- }
112
- }
113
- catch (e) {
114
- logger.warn(e);
115
- }
116
- };
117
- const elements = doc.parseResult.value.model?.elements ?? [];
118
- for (const modelElement of elements) {
119
- if (ast.isExtendElement(modelElement)) {
120
- visitExtendElement(modelElement);
121
- continue;
122
- }
123
- if (ast.isElement(modelElement)) {
124
- visitElement(modelElement);
125
- continue;
126
- }
127
- }
128
- }
129
- cleanIndexedElements(docUri) {
130
- const docUriAsString = docUri.toString();
131
- const toDelete = this.#index
132
- .entries()
133
- .filter(([, indexed]) => indexed.documentUri.toString() === docUriAsString);
134
- for (const [fqn, indexed] of toDelete.toArray()) {
135
- this.#index.delete(fqn, indexed);
136
- }
137
- }
138
98
  }
99
+ //# sourceMappingURL=fqn-index.js.map
@@ -1,3 +1,5 @@
1
+ export * from './fqn-computation';
1
2
  export * from './fqn-index';
2
3
  export * from './model-builder';
3
4
  export * from './model-locator';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,5 @@
1
+ export * from './fqn-computation';
1
2
  export * from './fqn-index';
2
3
  export * from './model-builder';
3
4
  export * from './model-locator';
5
+ //# sourceMappingURL=index.js.map
@@ -24,3 +24,4 @@ export declare class LikeC4ModelBuilder {
24
24
  private convertTags;
25
25
  private notifyClient;
26
26
  }
27
+ //# sourceMappingURL=model-builder.d.ts.map
@@ -2,15 +2,14 @@ import { computeViews } from '@likec4/core/compute-view';
2
2
  import { DefaultElementShape, DefaultThemeColor } from '@likec4/core/types';
3
3
  import { compareByFqnHierarchically, parentFqn } from '@likec4/core/utils';
4
4
  import { A, O, flow, pipe } from '@mobily/ts-belt';
5
- import { DocumentState, getDocument, interruptAndCheck } from 'langium';
5
+ import { DocumentState, getDocument } from 'langium';
6
6
  import objectHash from 'object-hash';
7
- import { clone, isNil, mergeDeepRight, omit, reduce } from 'rambdax';
7
+ import { clone, isNil, mergeDeepRight, reduce } from 'rambdax';
8
8
  import invariant from 'tiny-invariant';
9
- import { toAutoLayout } from '../ast';
10
- import { ElementViewOps, ast, c4hash, cleanParsedModel, isLikeC4LangiumDocument, isParsedLikeC4LangiumDocument, resolveRelationPoints, streamModel, toElementStyle } from '../ast';
9
+ import { ElementViewOps, ast, c4hash, cleanParsedModel, isLikeC4LangiumDocument, isParsedLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle } from '../ast';
11
10
  import { elementRef, strictElementRefFqn } from '../elementRef';
12
11
  import { logger } from '../logger';
13
- import { Rpc } from '@likec4/language-protocol';
12
+ import { Rpc } from '../protocol';
14
13
  import { failExpectedNever } from '../utils';
15
14
  export class LikeC4ModelBuilder {
16
15
  services;
@@ -20,26 +19,24 @@ export class LikeC4ModelBuilder {
20
19
  this.services = services;
21
20
  this.fqnIndex = services.likec4.FqnIndex;
22
21
  this.langiumDocuments = services.shared.workspace.LangiumDocuments;
23
- services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, async (docs, cancelToken) => {
22
+ services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, (docs, cancelToken) => {
24
23
  let countOfChangedDocs = 0;
25
- try {
26
- for (const doc of docs) {
27
- await interruptAndCheck(cancelToken);
28
- try {
29
- if (isLikeC4LangiumDocument(doc) && this.parseDocument(doc)) {
30
- countOfChangedDocs++;
31
- }
32
- }
33
- catch (e) {
34
- logger.warn(`Error parsing document ${doc.uri.toString()}`);
24
+ for (const doc of docs) {
25
+ if (cancelToken.isCancellationRequested) {
26
+ break;
27
+ }
28
+ try {
29
+ if (isLikeC4LangiumDocument(doc) && this.parseDocument(doc)) {
30
+ countOfChangedDocs++;
35
31
  }
36
32
  }
37
- }
38
- finally {
39
- if (countOfChangedDocs > 0 && !cancelToken.isCancellationRequested) {
40
- await this.notifyClient();
33
+ catch (e) {
34
+ logger.warn(`Error parsing document ${doc.uri.toString()}`);
41
35
  }
42
36
  }
37
+ if (countOfChangedDocs > 0 && !cancelToken.isCancellationRequested) {
38
+ void this.notifyClient();
39
+ }
43
40
  });
44
41
  }
45
42
  get connection() {
@@ -51,8 +48,10 @@ export class LikeC4ModelBuilder {
51
48
  buildModel() {
52
49
  const docs = this.documents();
53
50
  if (docs.length === 0) {
51
+ logger.debug('No documents to build model from');
54
52
  return;
55
53
  }
54
+ // TODO:
56
55
  try {
57
56
  const c4Specification = reduce((acc, doc) => mergeDeepRight(acc, doc.c4Specification), {
58
57
  kinds: {}
@@ -65,43 +64,58 @@ export class LikeC4ModelBuilder {
65
64
  const toModelElement = (el) => {
66
65
  const kind = c4Specification.kinds[el.kind];
67
66
  if (kind) {
67
+ const { astPath, ...model } = el;
68
68
  return {
69
69
  ...(kind.shape !== DefaultElementShape ? { shape: kind.shape } : {}),
70
70
  ...(kind.color !== DefaultThemeColor ? { color: kind.color } : {}),
71
- ...omit(['astPath'], el)
71
+ ...model
72
72
  };
73
73
  }
74
74
  return null;
75
75
  };
76
76
  const toModelRelation = (rel) => {
77
- return omit(['astPath'], rel);
77
+ const { astPath, ...model } = rel;
78
+ return model;
78
79
  };
79
80
  const elements = pipe(docs.flatMap(d => d.c4Elements), A.filterMap(flow(toModelElement, O.fromNullable)), A.sort(compareByFqnHierarchically), A.reduce({}, (acc, el) => {
80
81
  const parent = parentFqn(el.id);
81
82
  if (!parent || parent in acc) {
82
- invariant(!(el.id in acc), 'Duplicate element id: ' + el.id);
83
+ if (el.id in acc) {
84
+ logger.warn(`Duplicate element id: ${el.id}`);
85
+ return acc;
86
+ }
83
87
  acc[el.id] = el;
84
88
  }
85
89
  return acc;
86
90
  }));
87
91
  const relations = pipe(docs.flatMap(d => d.c4Relations), A.filterMap(flow(toModelRelation, O.fromPredicate(({ source, target }) => source in elements && target in elements))), A.reduce({}, (acc, el) => {
88
- invariant(!(el.id in acc), 'Duplicate relation id: ' + el.id);
92
+ if (el.id in acc) {
93
+ logger.warn(`Duplicate relation id: ${el.id}`);
94
+ return acc;
95
+ }
89
96
  acc[el.id] = el;
90
97
  return acc;
91
98
  }));
92
99
  const toModelView = (view) => {
93
- let title = view.title;
100
+ // eslint-disable-next-line prefer-const
101
+ let { astPath, rules, title, ...model } = view;
94
102
  if (!title && view.viewOf) {
95
103
  title = elements[view.viewOf]?.title;
96
104
  }
105
+ if (!title && view.id === 'index') {
106
+ title = 'Landscape view';
107
+ }
97
108
  return {
98
- ...omit(['astPath', 'rules', 'tite'], view),
99
- ...(!!title ? { title } : {}),
100
- rules: clone(view.rules)
109
+ ...model,
110
+ ...(title && { title }),
111
+ rules: clone(rules)
101
112
  };
102
113
  };
103
114
  const views = pipe(docs.flatMap(d => d.c4Views), A.filterMap(flow(toModelView, O.fromPredicate(v => isNil(v.viewOf) || v.viewOf in elements))), A.reduce({}, (acc, v) => {
104
- invariant(!(v.id in acc), 'Duplicate view id: ' + v.id);
115
+ if (v.id in acc) {
116
+ logger.warn(`Duplicate view id: ${v.id}`);
117
+ return acc;
118
+ }
105
119
  acc[v.id] = v;
106
120
  return acc;
107
121
  }));
@@ -326,7 +340,7 @@ export class LikeC4ModelBuilder {
326
340
  if (!connection) {
327
341
  return;
328
342
  }
329
- logger.debug('Send onDidChangeModel');
330
343
  await connection.sendNotification(Rpc.onDidChangeModel);
331
344
  }
332
345
  }
346
+ //# sourceMappingURL=model-builder.js.map
@@ -14,3 +14,4 @@ export declare class LikeC4ModelLocator {
14
14
  locateRelation(relationId: c4.RelationID): Location | null;
15
15
  locateView(viewId: c4.ViewID): Location | null;
16
16
  }
17
+ //# sourceMappingURL=model-locator.d.ts.map
@@ -1,6 +1,4 @@
1
- import { getDocument } from 'langium';
2
- import { findNodeForKeyword, findNodeForProperty } from 'langium';
3
- import { head } from 'rambdax';
1
+ import { findNodeForKeyword, findNodeForProperty, getDocument } from 'langium';
4
2
  import { ast, isParsedLikeC4LangiumDocument } from '../ast';
5
3
  export class LikeC4ModelLocator {
6
4
  services;
@@ -25,19 +23,28 @@ export class LikeC4ModelLocator {
25
23
  return doc.c4Elements.find(e => e.id === fqn) ?? null;
26
24
  }
27
25
  locateElement(fqn, property = 'name') {
28
- const descr = head(this.fqnIndex.byFqn(fqn));
29
- if (!descr)
30
- return null;
31
- const docUri = descr.documentUri.toString();
32
- const doc = this.documents().find(d => d.uri.toString() === docUri);
33
- const node = doc && this.services.workspace.AstNodeLocator.getAstNode(doc.parseResult.value, descr.path);
34
- if (!ast.isElement(node) || !node.$cstNode)
35
- return null;
36
- const propertyNode = findNodeForProperty(node.$cstNode, property);
37
- return {
38
- uri: docUri,
39
- range: propertyNode?.range ?? node.$cstNode.range
40
- };
26
+ for (const doc of this.documents()) {
27
+ if (doc.c4fqns && !doc.c4fqns.has(fqn)) {
28
+ continue;
29
+ }
30
+ const element = doc.c4Elements.find(e => e.id === fqn);
31
+ if (!element) {
32
+ continue;
33
+ }
34
+ const node = this.services.workspace.AstNodeLocator.getAstNode(doc.parseResult.value, element.astPath);
35
+ if (!ast.isElement(node)) {
36
+ continue;
37
+ }
38
+ const propertyNode = findNodeForProperty(node.$cstNode, property) ?? node.$cstNode;
39
+ if (!propertyNode) {
40
+ return null;
41
+ }
42
+ return {
43
+ uri: doc.uri.toString(),
44
+ range: propertyNode.range
45
+ };
46
+ }
47
+ return null;
41
48
  }
42
49
  locateRelation(relationId) {
43
50
  for (const doc of this.documents()) {
@@ -106,3 +113,4 @@ export class LikeC4ModelLocator {
106
113
  return null;
107
114
  }
108
115
  }
116
+ //# sourceMappingURL=model-locator.js.map
package/dist/module.d.ts CHANGED
@@ -18,3 +18,4 @@ export declare function createLanguageServices(context?: LanguageServicesContext
18
18
  likec4: LikeC4Services;
19
19
  };
20
20
  export {};
21
+ //# sourceMappingURL=module.d.ts.map
package/dist/module.js CHANGED
@@ -14,10 +14,6 @@ export const LikeC4Module = {
14
14
  FqnIndex: bind(FqnIndex),
15
15
  ModelBuilder: bind(LikeC4ModelBuilder),
16
16
  ModelLocator: bind(LikeC4ModelLocator)
17
- // Model: bind(LikeC4Model),
18
- // Model: bind(LikeC4Model),
19
- // SpecIndex: bind(LikeC4SpecIndex),
20
- // Validator: bind(LikeC4Validator)
21
17
  },
22
18
  lsp: {
23
19
  DocumentSymbolProvider: bind(LikeC4DocumentSymbolProvider),
@@ -63,3 +59,4 @@ export function createLanguageServices(context) {
63
59
  registerProtocolHandlers(likec4);
64
60
  return { shared, likec4 };
65
61
  }
62
+ //# sourceMappingURL=module.js.map