@likec4/language-server 0.33.1 → 0.35.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 (51) hide show
  1. package/contrib/likec4.monarch.ts +17 -3
  2. package/dist/ast.d.ts +24 -11
  3. package/dist/ast.js +12 -9
  4. package/dist/elementRef.d.ts +1 -1
  5. package/dist/elementRef.js +2 -3
  6. package/dist/generated/ast.d.ts +29 -10
  7. package/dist/generated/ast.js +20 -1
  8. package/dist/generated/grammar.d.ts +1 -1
  9. package/dist/generated/grammar.js +11 -11
  10. package/dist/generated/module.d.ts +7 -3
  11. package/dist/generated/module.js +3 -3
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +1 -0
  14. package/dist/logger.d.ts +6 -4
  15. package/dist/logger.js +34 -6
  16. package/dist/{shared → lsp}/CodeLensProvider.d.ts +3 -3
  17. package/dist/{shared → lsp}/DocumentLinkProvider.d.ts +3 -2
  18. package/dist/{shared → lsp}/DocumentLinkProvider.js +3 -3
  19. package/dist/lsp/DocumentSymbolProvider.js +3 -3
  20. package/dist/lsp/index.d.ts +2 -0
  21. package/dist/lsp/index.js +2 -0
  22. package/dist/model/fqn-computation.js +6 -3
  23. package/dist/model/fqn-index.d.ts +4 -7
  24. package/dist/model/fqn-index.js +51 -21
  25. package/dist/model/index.d.ts +1 -0
  26. package/dist/model/index.js +1 -0
  27. package/dist/model/model-builder.d.ts +1 -18
  28. package/dist/model/model-builder.js +111 -340
  29. package/dist/model/model-locator.js +19 -24
  30. package/dist/model/model-parser.d.ts +27 -0
  31. package/dist/model/model-parser.js +293 -0
  32. package/dist/module.d.ts +2 -1
  33. package/dist/module.js +20 -29
  34. package/dist/protocol.d.ts +8 -16
  35. package/dist/protocol.js +2 -6
  36. package/dist/references/scope-computation.js +11 -5
  37. package/dist/references/scope-provider.js +10 -4
  38. package/dist/registerProtocolHandlers.js +33 -17
  39. package/dist/shared/index.d.ts +0 -2
  40. package/dist/shared/index.js +0 -2
  41. package/dist/test/testServices.d.ts +2 -2
  42. package/dist/test/testServices.js +16 -10
  43. package/dist/utils.d.ts +2 -2
  44. package/dist/utils.js +2 -7
  45. package/dist/validation/element.d.ts +1 -1
  46. package/dist/validation/element.js +23 -3
  47. package/dist/validation/index.js +26 -0
  48. package/dist/validation/relation.d.ts +1 -1
  49. package/dist/validation/relation.js +32 -26
  50. package/package.json +10 -8
  51. /package/dist/{shared → lsp}/CodeLensProvider.js +0 -0
@@ -0,0 +1,293 @@
1
+ import { InvalidModelError, invariant, isNonEmptyArray, nonexhaustive } from '@likec4/core';
2
+ import { DocumentState, getDocument, interruptAndCheck } from 'langium';
3
+ import objectHash from 'object-hash';
4
+ import stripIndent from 'strip-indent';
5
+ import { Disposable } from 'vscode-languageserver-protocol';
6
+ import { ElementViewOps, ast, cleanParsedModel, isLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle, toElementStyleExcludeDefaults } from '../ast';
7
+ import { elementRef, strictElementRefFqn } from '../elementRef';
8
+ import { logError, logWarnError, logger } from '../logger';
9
+ import { printDocs } from '../utils';
10
+ export class LikeC4ModelParser {
11
+ services;
12
+ fqnIndex;
13
+ listeners = [];
14
+ constructor(services) {
15
+ this.services = services;
16
+ this.fqnIndex = services.likec4.FqnIndex;
17
+ services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, async (docs, cancelToken) => await this.onValidated(docs, cancelToken));
18
+ }
19
+ onParsed(callback) {
20
+ this.listeners.push(callback);
21
+ return Disposable.create(() => {
22
+ const index = this.listeners.indexOf(callback);
23
+ if (index >= 0) {
24
+ this.listeners.splice(index, 1);
25
+ }
26
+ });
27
+ }
28
+ async onValidated(docs, cancelToken) {
29
+ let countOfChangedDocs = 0;
30
+ logger.debug(`[ModelParser] onValidated (${docs.length} docs)\n${printDocs(docs)}`);
31
+ for (const doc of docs) {
32
+ if (!isLikeC4LangiumDocument(doc)) {
33
+ continue;
34
+ }
35
+ countOfChangedDocs++;
36
+ try {
37
+ await this.parseDocument(doc, cancelToken);
38
+ }
39
+ catch (cause) {
40
+ logError(new InvalidModelError(`Error parsing document ${doc.uri.toString()}`, { cause }));
41
+ }
42
+ }
43
+ if (countOfChangedDocs > 0) {
44
+ this.notifyListeners();
45
+ }
46
+ }
47
+ async parseDocument(doc, cancelToken) {
48
+ const { elements, relations, views, specification } = cleanParsedModel(doc);
49
+ const specs = doc.parseResult.value.specification?.specs.filter(ast.isSpecificationElementKind);
50
+ if (specs) {
51
+ for (const { kind, style } of specs) {
52
+ if (kind.name in specification.kinds) {
53
+ logger.warn(`Duplicate specification for kind ${kind.name}`);
54
+ continue;
55
+ }
56
+ try {
57
+ specification.kinds[kind.name] = toElementStyleExcludeDefaults(style?.props);
58
+ }
59
+ catch (e) {
60
+ logWarnError(e);
61
+ }
62
+ }
63
+ }
64
+ await interruptAndCheck(cancelToken);
65
+ for (const el of streamModel(doc)) {
66
+ if (ast.isElement(el)) {
67
+ try {
68
+ elements.push(this.parseElement(el));
69
+ }
70
+ catch (e) {
71
+ logWarnError(e);
72
+ }
73
+ continue;
74
+ }
75
+ if (ast.isRelation(el)) {
76
+ try {
77
+ relations.push(this.parseRelation(el));
78
+ }
79
+ catch (e) {
80
+ logWarnError(e);
81
+ }
82
+ continue;
83
+ }
84
+ nonexhaustive(el);
85
+ }
86
+ await interruptAndCheck(cancelToken);
87
+ const docviews = doc.parseResult.value.views?.views;
88
+ if (docviews) {
89
+ for (const view of docviews) {
90
+ try {
91
+ const v = this.parseElementView(view);
92
+ ElementViewOps.writeId(view, v.id);
93
+ views.push(v);
94
+ }
95
+ catch (e) {
96
+ logWarnError(e);
97
+ }
98
+ }
99
+ }
100
+ // const prevHash = doc.c4hash ?? ''
101
+ // doc.c4hash = c4hash(doc)
102
+ // return prevHash !== doc.c4hash
103
+ }
104
+ parseElement(astNode) {
105
+ const id = this.resolveFqn(astNode);
106
+ invariant(astNode.kind.ref, 'Element kind is not resolved: ' + astNode.name);
107
+ const kind = astNode.kind.ref.name;
108
+ const tags = this.convertTags(astNode.body);
109
+ const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
110
+ const styleProps = toElementStyleExcludeDefaults(stylePropsAst);
111
+ const astPath = this.getAstNodePath(astNode);
112
+ let [title, description, technology] = astNode.props;
113
+ const bodyProps = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
114
+ title = title ?? bodyProps.find(p => p.key === 'title')?.value;
115
+ description = description ?? bodyProps.find(p => p.key === 'description')?.value;
116
+ technology = technology ?? bodyProps.find(p => p.key === 'technology')?.value;
117
+ const links = astNode.body?.props.filter(ast.isLinkProperty).map(p => p.value);
118
+ return {
119
+ id,
120
+ kind,
121
+ astPath,
122
+ title: title ? stripIndent(title).trim() : astNode.name,
123
+ ...(tags && { tags }),
124
+ ...(links && isNonEmptyArray(links) && { links }),
125
+ ...(technology && { technology }),
126
+ ...(description && { description: stripIndent(description).trim() }),
127
+ ...styleProps
128
+ };
129
+ }
130
+ parseRelation(astNode) {
131
+ const coupling = resolveRelationPoints(astNode);
132
+ const target = this.resolveFqn(coupling.target);
133
+ const source = this.resolveFqn(coupling.source);
134
+ const hashdata = {
135
+ astPath: this.getAstNodePath(astNode),
136
+ source,
137
+ target
138
+ };
139
+ const id = objectHash(hashdata);
140
+ const title = astNode.title ?? astNode.body?.props.find(p => p.key === 'title')?.value ?? '';
141
+ return {
142
+ id,
143
+ ...hashdata,
144
+ title
145
+ };
146
+ }
147
+ parseElementExpression(astNode) {
148
+ if (ast.isWildcardExpression(astNode)) {
149
+ return {
150
+ wildcard: true
151
+ };
152
+ }
153
+ if (ast.isElementKindExpression(astNode)) {
154
+ invariant(astNode.kind.ref, 'ElementKindExpression kind is not resolved: ' + astNode.$cstNode?.text);
155
+ return {
156
+ elementKind: astNode.kind.ref.name,
157
+ isEqual: astNode.isEqual
158
+ };
159
+ }
160
+ if (ast.isElementTagExpression(astNode)) {
161
+ invariant(astNode.tag.ref, 'ElementTagExpression tag is not resolved: ' + astNode.$cstNode?.text);
162
+ return {
163
+ elementTag: astNode.tag.ref.name,
164
+ isEqual: astNode.isEqual
165
+ };
166
+ }
167
+ if (ast.isElementRefExpression(astNode)) {
168
+ const element = elementRef(astNode.id);
169
+ invariant(element, 'Element not found ' + astNode.id.$cstNode?.text);
170
+ return {
171
+ element: this.resolveFqn(element),
172
+ isDescedants: astNode.isDescedants
173
+ };
174
+ }
175
+ nonexhaustive(astNode);
176
+ }
177
+ parseExpression(astNode) {
178
+ if (ast.isElementExpression(astNode)) {
179
+ return this.parseElementExpression(astNode);
180
+ }
181
+ if (ast.isIncomingExpression(astNode)) {
182
+ return {
183
+ incoming: this.parseElementExpression(astNode.target)
184
+ };
185
+ }
186
+ if (ast.isOutgoingExpression(astNode)) {
187
+ return {
188
+ outgoing: this.parseElementExpression(astNode.source)
189
+ };
190
+ }
191
+ if (ast.isInOutExpression(astNode)) {
192
+ return {
193
+ inout: this.parseElementExpression(astNode.inout.target)
194
+ };
195
+ }
196
+ if (ast.isRelationExpression(astNode)) {
197
+ return {
198
+ source: this.parseElementExpression(astNode.source),
199
+ target: this.parseElementExpression(astNode.target)
200
+ };
201
+ }
202
+ nonexhaustive(astNode);
203
+ }
204
+ parseViewRule(astRule) {
205
+ if (ast.isViewRuleExpression(astRule)) {
206
+ const exprs = astRule.expressions.map(n => this.parseExpression(n));
207
+ return {
208
+ isInclude: astRule.isInclude,
209
+ exprs
210
+ };
211
+ }
212
+ if (ast.isViewRuleStyle(astRule)) {
213
+ const styleProps = toElementStyle(astRule.props);
214
+ return {
215
+ targets: astRule.targets.map(n => this.parseElementExpression(n)),
216
+ style: {
217
+ ...styleProps
218
+ }
219
+ };
220
+ }
221
+ if (ast.isViewRuleAutoLayout(astRule)) {
222
+ return {
223
+ autoLayout: toAutoLayout(astRule.direction)
224
+ };
225
+ }
226
+ nonexhaustive(astRule);
227
+ }
228
+ parseElementView(astNode) {
229
+ const viewOfEl = astNode.viewOf && elementRef(astNode.viewOf);
230
+ const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
231
+ const astPath = this.getAstNodePath(astNode);
232
+ let id = astNode.name;
233
+ if (!id) {
234
+ const doc = getDocument(astNode).uri.toString();
235
+ id = objectHash({
236
+ doc,
237
+ astPath,
238
+ viewOf: viewOf ?? null
239
+ });
240
+ }
241
+ const title = astNode.props.find(p => p.key === 'title')?.value;
242
+ const description = astNode.props.find(p => p.key === 'description')?.value;
243
+ const tags = this.convertTags(astNode);
244
+ const links = astNode.props.filter(ast.isLinkProperty).map(p => p.value);
245
+ return {
246
+ id,
247
+ astPath,
248
+ ...(viewOf && { viewOf }),
249
+ ...(title && { title }),
250
+ ...(description && { description }),
251
+ ...(tags && { tags }),
252
+ ...(links && isNonEmptyArray(links) && { links }),
253
+ rules: astNode.rules.flatMap(n => {
254
+ try {
255
+ return this.parseViewRule(n);
256
+ }
257
+ catch (e) {
258
+ logWarnError(e);
259
+ return [];
260
+ }
261
+ })
262
+ };
263
+ }
264
+ resolveFqn(node) {
265
+ if (ast.isExtendElement(node)) {
266
+ return strictElementRefFqn(node.element);
267
+ }
268
+ const fqn = this.fqnIndex.getFqn(node);
269
+ invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
270
+ return fqn;
271
+ }
272
+ getAstNodePath(node) {
273
+ return this.services.workspace.AstNodeLocator.getAstNodePath(node);
274
+ }
275
+ convertTags(withTags) {
276
+ if (!withTags) {
277
+ return null;
278
+ }
279
+ const tags = withTags.tags?.value.flatMap(({ ref }) => (ref ? ref.name : []));
280
+ return tags && isNonEmptyArray(tags) ? tags : null;
281
+ }
282
+ notifyListeners() {
283
+ for (const listener of this.listeners) {
284
+ try {
285
+ listener();
286
+ }
287
+ catch (e) {
288
+ logError(e);
289
+ }
290
+ }
291
+ }
292
+ }
293
+ //# sourceMappingURL=model-parser.js.map
package/dist/module.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  import type { DefaultSharedModuleContext, LangiumServices, LangiumSharedServices, Module, PartialLangiumServices } from 'langium';
2
2
  import { LikeC4DocumentSymbolProvider } from './lsp';
3
- import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator } from './model';
3
+ import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model';
4
4
  /**
5
5
  * Declaration of custom services - add your own service classes here.
6
6
  */
7
7
  export interface LikeC4AddedServices {
8
8
  likec4: {
9
9
  FqnIndex: FqnIndex;
10
+ ModelParser: LikeC4ModelParser;
10
11
  ModelBuilder: LikeC4ModelBuilder;
11
12
  ModelLocator: LikeC4ModelLocator;
12
13
  };
package/dist/module.js CHANGED
@@ -1,25 +1,29 @@
1
1
  import { EmptyFileSystem, createDefaultModule, createDefaultSharedModule, inject } from 'langium';
2
2
  import { LikeC4GeneratedModule, LikeC4GeneratedSharedModule } from './generated/module';
3
- import { logger } from './logger';
4
- import { LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
5
- import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator } from './model';
3
+ import { LikeC4CodeLensProvider, LikeC4DocumentLinkProvider, LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
4
+ import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model';
6
5
  import { LikeC4ScopeComputation, LikeC4ScopeProvider } from './references';
7
6
  import { registerProtocolHandlers } from './registerProtocolHandlers';
8
- import { LikeC4CodeLensProvider, LikeC4DocumentLinkProvider, LikeC4WorkspaceManager } from './shared';
7
+ import { LikeC4WorkspaceManager } from './shared';
9
8
  import { registerValidationChecks } from './validation';
9
+ import { logger } from './logger';
10
+ import { serializeError } from '@likec4/core';
10
11
  function bind(Type) {
11
12
  return (services) => new Type(services);
12
13
  }
13
14
  export const LikeC4Module = {
14
15
  likec4: {
15
16
  FqnIndex: bind(FqnIndex),
17
+ ModelParser: bind(LikeC4ModelParser),
16
18
  ModelBuilder: bind(LikeC4ModelBuilder),
17
19
  ModelLocator: bind(LikeC4ModelLocator)
18
20
  },
19
21
  lsp: {
20
22
  DocumentSymbolProvider: bind(LikeC4DocumentSymbolProvider),
21
23
  SemanticTokenProvider: bind(LikeC4SemanticTokenProvider),
22
- HoverProvider: bind(LikeC4HoverProvider)
24
+ HoverProvider: bind(LikeC4HoverProvider),
25
+ CodeLensProvider: bind(LikeC4CodeLensProvider),
26
+ DocumentLinkProvider: bind(LikeC4DocumentLinkProvider)
23
27
  },
24
28
  //
25
29
  // // Formatter: bind(LikeC4Formatter),
@@ -31,44 +35,31 @@ export const LikeC4Module = {
31
35
  }
32
36
  };
33
37
  const LikeC4SharedModule = {
34
- ...LikeC4GeneratedSharedModule,
35
38
  workspace: {
36
39
  WorkspaceManager: services => new LikeC4WorkspaceManager(services)
37
- },
38
- lsp: {
39
- CodeLensProvider: services => new LikeC4CodeLensProvider(services),
40
- DocumentLinkProvider: services => new LikeC4DocumentLinkProvider(services)
41
40
  }
42
41
  };
43
42
  export function createLanguageServices(context) {
44
43
  const connection = context?.connection;
45
44
  if (connection) {
46
- const log = (method) => (message) => {
47
- try {
48
- console[method](message);
49
- connection.console[method](String(message));
50
- if (method === 'error') {
51
- connection.telemetry.logEvent({ eventName: 'error', message });
52
- }
53
- }
54
- catch (error) {
55
- console.error(error);
45
+ // eslint-disable-next-line @typescript-eslint/unbound-method
46
+ const original = logger.error.bind(logger);
47
+ logger.error = (arg) => {
48
+ if (typeof arg === 'string') {
49
+ original(arg);
50
+ connection.telemetry.logEvent({ eventName: 'error', error: arg });
51
+ return;
56
52
  }
57
- };
58
- logger.log = log('log');
59
- logger.info = log('info');
60
- logger.warn = log('warn');
61
- logger.error = log('error');
62
- logger.trace = logger.debug = (message) => {
63
- console.debug(message);
64
- connection.tracer.log(message);
53
+ const { message, error } = serializeError(arg);
54
+ original(error);
55
+ connection.telemetry.logEvent({ eventName: 'error', error: message });
65
56
  };
66
57
  }
67
58
  const moduleContext = {
68
59
  ...EmptyFileSystem,
69
60
  ...context
70
61
  };
71
- const shared = inject(createDefaultSharedModule(moduleContext), LikeC4SharedModule);
62
+ const shared = inject(createDefaultSharedModule(moduleContext), LikeC4GeneratedSharedModule, LikeC4SharedModule);
72
63
  const likec4 = inject(createDefaultModule({ shared }), LikeC4GeneratedModule, LikeC4Module);
73
64
  shared.ServiceRegistry.register(likec4);
74
65
  registerValidationChecks(likec4);
@@ -4,17 +4,15 @@ import { NotificationType, RequestType0, RequestType } from 'vscode-languageserv
4
4
  interface BuildDocumentsParams {
5
5
  docs: DocumentUri[];
6
6
  }
7
- export interface LocateElementParams {
7
+ export type LocateParams = {
8
8
  element: Fqn;
9
9
  property?: string;
10
- }
11
- export declare const locateElement: RequestType<LocateElementParams, Location | null, void>;
12
- export declare const locateRelation: RequestType<{
13
- id: RelationID;
14
- }, Location | null, void>;
15
- export declare const locateView: RequestType<{
16
- id: ViewID;
17
- }, Location | null, void>;
10
+ } | {
11
+ relation: RelationID;
12
+ } | {
13
+ view: ViewID;
14
+ };
15
+ export declare const locate: RequestType<LocateParams, Location | null, void>;
18
16
  export declare const Rpc: {
19
17
  readonly onDidChangeModel: NotificationType<string>;
20
18
  readonly fetchModel: RequestType0<{
@@ -24,13 +22,7 @@ export declare const Rpc: {
24
22
  docs: DocumentUri[];
25
23
  }, void>;
26
24
  readonly buildDocuments: RequestType<BuildDocumentsParams, void, void>;
27
- readonly locateElement: RequestType<LocateElementParams, Location | null, void>;
28
- readonly locateRelation: RequestType<{
29
- id: RelationID;
30
- }, Location | null, void>;
31
- readonly locateView: RequestType<{
32
- id: ViewID;
33
- }, Location | null, void>;
25
+ readonly locate: RequestType<LocateParams, Location | null, void>;
34
26
  };
35
27
  export {};
36
28
  //# sourceMappingURL=protocol.d.ts.map
package/dist/protocol.js CHANGED
@@ -6,17 +6,13 @@ const onDidChangeModel = new NotificationType('likec4/onDidChangeModel');
6
6
  const fetchModel = new RequestType0('likec4/fetchModel');
7
7
  const rebuild = new RequestType0('likec4/rebuildModel');
8
8
  const buildDocuments = new RequestType('likec4/buildDocuments');
9
- export const locateElement = new RequestType('likec4/locateElement');
10
- export const locateRelation = new RequestType('likec4/locateRelation');
11
- export const locateView = new RequestType('likec4/locateView');
9
+ export const locate = new RequestType('likec4/locate');
12
10
  //#endregion
13
11
  export const Rpc = {
14
12
  onDidChangeModel,
15
13
  fetchModel,
16
14
  rebuild,
17
15
  buildDocuments,
18
- locateElement,
19
- locateRelation,
20
- locateView
16
+ locate
21
17
  };
22
18
  //# sourceMappingURL=protocol.js.map
@@ -1,6 +1,7 @@
1
1
  import { DefaultScopeComputation, MultiMap } from 'langium';
2
2
  import { ast } from '../ast';
3
3
  import { isEmpty } from 'remeda';
4
+ import { nonexhaustive } from '@likec4/core';
4
5
  export class LikeC4ScopeComputation extends DefaultScopeComputation {
5
6
  services;
6
7
  constructor(services) {
@@ -12,15 +13,20 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
12
13
  const docExports = [];
13
14
  if (specification && specification.specs.length > 0) {
14
15
  for (const spec of specification.specs) {
15
- if (ast.isSpecificationElementKind(spec) && spec.kind && !isEmpty(spec.kind.name)) {
16
- docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
16
+ if (ast.isSpecificationElementKind(spec)) {
17
+ if (spec.kind && !isEmpty(spec.kind.name)) {
18
+ docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
19
+ }
17
20
  continue;
18
21
  }
19
- if (ast.isSpecificationTag(spec) && spec.tag && !isEmpty(spec.tag.name)) {
20
- docExports.push(this.descriptions.createDescription(spec.tag, spec.tag.name, document));
21
- docExports.push(this.descriptions.createDescription(spec.tag, '#' + spec.tag.name, document));
22
+ if (ast.isSpecificationTag(spec)) {
23
+ if (spec.tag && !isEmpty(spec.tag.name)) {
24
+ docExports.push(this.descriptions.createDescription(spec.tag, spec.tag.name, document));
25
+ docExports.push(this.descriptions.createDescription(spec.tag, '#' + spec.tag.name, document));
26
+ }
22
27
  continue;
23
28
  }
29
+ nonexhaustive(spec);
24
30
  }
25
31
  }
26
32
  // Only root model elements are exported
@@ -1,11 +1,18 @@
1
- import { DONE_RESULT, DefaultScopeProvider, EMPTY_STREAM, StreamImpl, StreamScope, getDocument, stream } from 'langium';
1
+ import { DONE_RESULT, DefaultScopeProvider, EMPTY_STREAM, StreamImpl, StreamScope, getDocument, stream, findNodeForProperty, toDocumentSegment } from 'langium';
2
2
  import { ast } from '../ast';
3
3
  import { elementRef, isElementRefHead, parentStrictElementRef } from '../elementRef';
4
- import { logger } from '../logger';
4
+ import { logError } from '../logger';
5
5
  function toAstNodeDescription(entry) {
6
+ const $cstNode = findNodeForProperty(entry.el.$cstNode, 'name');
6
7
  return {
7
8
  documentUri: entry.doc.uri,
8
9
  name: entry.name,
10
+ ...(entry.el.$cstNode && {
11
+ selectionSegment: toDocumentSegment(entry.el.$cstNode)
12
+ }),
13
+ ...($cstNode && {
14
+ nameSegment: toDocumentSegment($cstNode)
15
+ }),
9
16
  path: entry.path,
10
17
  type: ast.Element
11
18
  };
@@ -71,8 +78,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
71
78
  return this.computeScope(container, referenceType);
72
79
  }
73
80
  catch (e) {
74
- // console.error(e)
75
- logger.error(e);
81
+ logError(e);
76
82
  return this.getGlobalScope(referenceType);
77
83
  }
78
84
  }
@@ -1,6 +1,8 @@
1
1
  import { URI } from 'vscode-uri';
2
- import { logger } from './logger';
2
+ import { logger, logError } from './logger';
3
3
  import { Rpc } from './protocol';
4
+ import { nonexhaustive } from '@likec4/core';
5
+ import { isLikeC4LangiumDocument } from './ast';
4
6
  export function registerProtocolHandlers(services) {
5
7
  const connection = services.shared.lsp.Connection;
6
8
  if (!connection) {
@@ -16,13 +18,25 @@ export function registerProtocolHandlers(services) {
16
18
  }
17
19
  catch (e) {
18
20
  model = null;
19
- logger.error(e);
21
+ logError(e);
20
22
  }
21
23
  return Promise.resolve({ model });
22
24
  });
23
25
  connection.onRequest(Rpc.rebuild, async (cancelToken) => {
24
- const changed = LangiumDocuments.all.map(d => d.uri).toArray();
25
- logger.debug(`Rebuild all documents: [
26
+ const changed = LangiumDocuments.all
27
+ .map(d => {
28
+ // clean up any computed properties
29
+ if (isLikeC4LangiumDocument(d)) {
30
+ delete d.c4Specification;
31
+ delete d.c4Elements;
32
+ delete d.c4Relations;
33
+ delete d.c4Views;
34
+ delete d.c4fqns;
35
+ }
36
+ return d.uri;
37
+ })
38
+ .toArray();
39
+ logger.debug(`[ProtocolHandlers] rebuild all documents: [
26
40
  ${changed.map(d => d.toString()).join('\n ')}
27
41
  ]`);
28
42
  await services.shared.workspace.DocumentBuilder.update(changed, [], cancelToken);
@@ -32,12 +46,11 @@ export function registerProtocolHandlers(services) {
32
46
  });
33
47
  connection.onRequest(Rpc.buildDocuments, async ({ docs }, cancelToken) => {
34
48
  if (docs.length === 0) {
35
- logger.debug(`Received empty request to rebuild`);
49
+ logger.debug(`[ProtocolHandlers] received empty request to rebuild`);
36
50
  return;
37
51
  }
38
- logger.debug(`Received request to rebuild: [
39
- ${docs.join('\n ')}
40
- ]`);
52
+ logger.debug(`[ProtocolHandlers] received request to buildDocuments:
53
+ - ${docs.join('\n - ')}`);
41
54
  const changed = [];
42
55
  for (const d of docs) {
43
56
  try {
@@ -51,7 +64,7 @@ export function registerProtocolHandlers(services) {
51
64
  }
52
65
  }
53
66
  catch (e) {
54
- logger.error(e);
67
+ logError(e);
55
68
  }
56
69
  }
57
70
  if (changed.length !== docs.length) {
@@ -67,14 +80,17 @@ We rebuild: [
67
80
  }
68
81
  await services.shared.workspace.DocumentBuilder.update(changed, [], cancelToken);
69
82
  });
70
- connection.onRequest(Rpc.locateElement, ({ element, property }) => {
71
- return modelLocator.locateElement(element, property ?? 'name');
72
- });
73
- connection.onRequest(Rpc.locateRelation, ({ id }) => {
74
- return modelLocator.locateRelation(id);
75
- });
76
- connection.onRequest(Rpc.locateView, ({ id }) => {
77
- return modelLocator.locateView(id);
83
+ connection.onRequest(Rpc.locate, params => {
84
+ if ('element' in params) {
85
+ return modelLocator.locateElement(params.element, params.property ?? 'name');
86
+ }
87
+ if ('relation' in params) {
88
+ return modelLocator.locateRelation(params.relation);
89
+ }
90
+ if ('view' in params) {
91
+ return modelLocator.locateView(params.view);
92
+ }
93
+ nonexhaustive(params);
78
94
  });
79
95
  }
80
96
  //# sourceMappingURL=registerProtocolHandlers.js.map
@@ -1,4 +1,2 @@
1
- export * from './CodeLensProvider';
2
1
  export * from './WorkspaceManager';
3
- export * from './DocumentLinkProvider';
4
2
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,2 @@
1
- export * from './CodeLensProvider';
2
1
  export * from './WorkspaceManager';
3
- export * from './DocumentLinkProvider';
4
2
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,8 @@
1
1
  import type { LikeC4LangiumDocument } from '../ast';
2
- export declare function createTestServices(): {
2
+ export declare function createTestServices(workspace?: string): {
3
3
  services: import("../module").LikeC4Services;
4
4
  parse: (input: string, uri?: string) => Promise<LikeC4LangiumDocument>;
5
- validate: (input: string | LikeC4LangiumDocument) => Promise<{
5
+ validate: (input: string | LikeC4LangiumDocument, uri?: string) => Promise<{
6
6
  document: LikeC4LangiumDocument;
7
7
  diagnostics: import("vscode-languageserver-types").Diagnostic[];
8
8
  errors: string[];