@likec4/language-server 0.24.0 → 0.26.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.
@@ -5,7 +5,7 @@
5
5
  ******************************************************************************/
6
6
  /// <reference types="react" />
7
7
  import { type DocumentSymbolProvider, type MaybePromise } from 'langium';
8
- import { type DocumentSymbol } from 'vscode-languageserver';
8
+ import { type DocumentSymbol } from 'vscode-languageserver-protocol';
9
9
  import { type LikeC4LangiumDocument, ast } from '../ast';
10
10
  import type { LikeC4Services } from '../module';
11
11
  export declare class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
@@ -5,7 +5,7 @@
5
5
  ******************************************************************************/
6
6
  import invariant from 'tiny-invariant';
7
7
  import { findNodeForProperty } from 'langium';
8
- import { SymbolKind } from 'vscode-languageserver';
8
+ import { SymbolKind } from 'vscode-languageserver-protocol';
9
9
  import { ast } from '../ast';
10
10
  import { logger } from '../logger';
11
11
  export class LikeC4DocumentSymbolProvider {
@@ -1,5 +1,5 @@
1
1
  import { type AstNode, AstNodeHoverProvider, type MaybePromise } from 'langium';
2
- import type { Hover } from 'vscode-languageserver';
2
+ import type { Hover } from 'vscode-languageserver-protocol';
3
3
  import type { LikeC4Services } from '../module';
4
4
  export declare class LikeC4HoverProvider extends AstNodeHoverProvider {
5
5
  private locator;
@@ -1,5 +1,5 @@
1
1
  import { AbstractSemanticTokenProvider } from 'langium';
2
- import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver';
2
+ import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver-protocol';
3
3
  import { ast } from '../ast';
4
4
  import { isElementRefHead } from '../elementRef';
5
5
  export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
@@ -7,7 +7,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
7
7
  const keyword = (keyword, _index) => acceptor({
8
8
  node,
9
9
  keyword,
10
- type: SemanticTokenTypes.keyword
10
+ type: SemanticTokenTypes.keyword,
11
+ modifier: [SemanticTokenModifiers.defaultLibrary]
11
12
  });
12
13
  if (ast.isElementRef(node) || ast.isStrictElementRef(node)) {
13
14
  acceptor({
@@ -25,11 +26,17 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
25
26
  });
26
27
  return;
27
28
  }
28
- if (ast.isRelationExpression(node) ||
29
+ if (ast.isRelation(node) ||
30
+ ast.isRelationExpression(node) ||
29
31
  ast.isIncomingExpression(node) ||
32
+ ast.isInOutExpression(node) ||
30
33
  ast.isOutgoingExpression(node)) {
31
- keyword('->');
32
- return;
34
+ acceptor({
35
+ node,
36
+ property: 'arr',
37
+ type: SemanticTokenTypes.keyword,
38
+ modifier: [SemanticTokenModifiers.defaultLibrary]
39
+ });
33
40
  }
34
41
  if (ast.isElementKindExpression(node) || ast.isElementTagExpression(node)) {
35
42
  keyword('element');
@@ -54,13 +61,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
54
61
  return;
55
62
  }
56
63
  }
57
- if (ast.isInOutExpression(node)) {
58
- keyword('->', 0);
59
- keyword('->', 1);
60
- return;
61
- }
62
64
  if (ast.isRelation(node)) {
63
- keyword('->');
64
65
  if ('title' in node) {
65
66
  acceptor({
66
67
  node,
@@ -102,11 +103,16 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
102
103
  node,
103
104
  property: 'key',
104
105
  type: SemanticTokenTypes.keyword
106
+ // type: SemanticTokenTypes.property,
107
+ // modifier: [
108
+ // SemanticTokenModifiers.readonly,
109
+ // SemanticTokenModifiers.declaration
110
+ // ]
105
111
  });
106
112
  acceptor({
107
113
  node,
108
114
  property: 'value',
109
- type: SemanticTokenTypes.enumMember
115
+ type: SemanticTokenTypes.enum
110
116
  });
111
117
  return;
112
118
  }
@@ -115,6 +121,11 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
115
121
  node,
116
122
  property: 'key',
117
123
  type: SemanticTokenTypes.keyword
124
+ // type: SemanticTokenTypes.property,
125
+ // modifier: [
126
+ // SemanticTokenModifiers.readonly,
127
+ // SemanticTokenModifiers.declaration
128
+ // ]
118
129
  });
119
130
  acceptor({
120
131
  node,
@@ -151,20 +162,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
151
162
  type: SemanticTokenTypes.keyword,
152
163
  modifier: []
153
164
  });
154
- if ('title' in node) {
155
- acceptor({
156
- node,
157
- property: 'title',
158
- type: SemanticTokenTypes.string
159
- });
160
- }
161
165
  }
162
166
  highlightView(node, acceptor) {
163
- acceptor({
164
- node,
165
- keyword: 'view',
166
- type: SemanticTokenTypes.keyword
167
- });
168
167
  if (node.name) {
169
168
  acceptor({
170
169
  node,
@@ -1,5 +1,5 @@
1
1
  import type { Fqn } from '@likec4/core/types';
2
- import type { LangiumDocuments } from 'langium';
2
+ import type { LangiumDocument, LangiumDocuments } from 'langium';
3
3
  import { StreamImpl } from 'langium';
4
4
  import type { ast } from '../ast';
5
5
  import { type LikeC4LangiumDocument } from '../ast';
@@ -7,6 +7,7 @@ import type { LikeC4Services } from '../module';
7
7
  type FqnIndexedDocument = Omit<LikeC4LangiumDocument, 'c4fqns'> & {
8
8
  c4fqns: NonNullable<LikeC4LangiumDocument['c4fqns']>;
9
9
  };
10
+ export declare function isFqnIndexedDocument(doc: LangiumDocument): doc is FqnIndexedDocument;
10
11
  export interface FqnIndexEntry {
11
12
  fqn: Fqn;
12
13
  name: string;
@@ -24,7 +25,7 @@ export declare class FqnIndex {
24
25
  path: string;
25
26
  doc: FqnIndexedDocument;
26
27
  }>;
27
- directChildrenOf(parent: Fqn): import("langium").Stream<FqnIndexEntry>;
28
+ directChildrenOf(parent: Fqn): StreamImpl<IterableIterator<FqnIndexEntry> | null, FqnIndexEntry>;
28
29
  uniqueDescedants(parent: Fqn): StreamImpl<IterableIterator<FqnIndexEntry> | null, FqnIndexEntry>;
29
30
  }
30
31
  export {};
@@ -1,10 +1,12 @@
1
1
  import { nameFromFqn, parentFqn } from '@likec4/core/utils';
2
- import { DocumentState, DONE_RESULT, getDocument, MultiMap, StreamImpl } from 'langium';
2
+ import { DocumentState, DONE_RESULT, MultiMap, StreamImpl } from 'langium';
3
3
  import { isNil } from 'remeda';
4
4
  import { ElementOps, isLikeC4LangiumDocument } from '../ast';
5
5
  import { logger } from '../logger';
6
6
  import { computeDocumentFqn } from './fqn-computation';
7
- const isFqnIndexedDocument = (doc) => isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns);
7
+ export function isFqnIndexedDocument(doc) {
8
+ return isLikeC4LangiumDocument(doc) && !isNil(doc.c4fqns);
9
+ }
8
10
  export class FqnIndex {
9
11
  services;
10
12
  langiumDocuments;
@@ -31,54 +33,70 @@ export class FqnIndex {
31
33
  return this.documents().flatMap(doc => doc.c4fqns.entries().map(([fqn, path]) => ({ fqn, path, doc })));
32
34
  }
33
35
  get(el) {
34
- let fqn = ElementOps.readId(el) ?? null;
35
- if (fqn) {
36
- const doc = getDocument(el);
37
- if (isFqnIndexedDocument(doc) && doc.c4fqns.has(fqn)) {
38
- return fqn;
39
- }
40
- const path = this.services.workspace.AstNodeLocator.getAstNodePath(el);
41
- logger.error(`Clean cached FQN ${fqn} at ${path}`);
42
- ElementOps.writeId(el, null);
43
- fqn = null;
44
- }
45
- return fqn;
36
+ return ElementOps.readId(el) ?? null;
37
+ // if (fqn) {
38
+ // const doc = getDocument(el)
39
+ // if (isFqnIndexedDocument(doc) && doc.c4fqns.has(fqn)) {
40
+ // return fqn
41
+ // }
42
+ // const path = this.services.workspace.AstNodeLocator.getAstNodePath(el)
43
+ // logger.error(`Clean cached FQN ${fqn} at ${path}`)
44
+ // ElementOps.writeId(el, null)
45
+ // fqn = null
46
+ // }
47
+ // return fqn
46
48
  }
47
49
  byFqn(fqn) {
48
- return this.documents()
49
- .flatMap(doc => {
50
+ return this.documents().flatMap(doc => {
50
51
  return doc.c4fqns.get(fqn).map(path => ({ path, doc }));
51
52
  });
52
53
  }
53
54
  directChildrenOf(parent) {
54
- return this
55
- .entries()
56
- .filter(e => parentFqn(e.fqn) === parent)
57
- .map((e) => ({ ...e, name: nameFromFqn(e.fqn) }));
55
+ return new StreamImpl(() => {
56
+ const children = new MultiMap(this.entries()
57
+ .filter(e => parentFqn(e.fqn) === parent)
58
+ .map((e) => {
59
+ const name = nameFromFqn(e.fqn);
60
+ const entry = { ...e, name };
61
+ return [name, entry];
62
+ })
63
+ .toArray());
64
+ if (children.size === 0) {
65
+ return null;
66
+ }
67
+ return children
68
+ .entriesGroupedByKey()
69
+ .flatMap(([_name, descrs]) => (descrs.length === 1 ? descrs : []))
70
+ .iterator();
71
+ }, iterator => {
72
+ if (iterator) {
73
+ return iterator.next();
74
+ }
75
+ return DONE_RESULT;
76
+ });
58
77
  }
59
78
  uniqueDescedants(parent) {
60
79
  return new StreamImpl(() => {
61
80
  const prefix = `${parent}.`;
62
- const children = [];
63
81
  const childrenNames = new Set();
64
82
  const descedants = [];
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);
72
- }
73
- else {
74
- descedants.push(entry);
75
- }
83
+ const nested = new MultiMap();
84
+ this.entries()
85
+ .filter(e => e.fqn.startsWith(prefix))
86
+ .forEach(e => {
87
+ const name = nameFromFqn(e.fqn);
88
+ const entry = { ...e, name };
89
+ if (parentFqn(e.fqn) === parent) {
90
+ childrenNames.add(name);
91
+ nested.add(name, entry);
92
+ }
93
+ else {
94
+ descedants.push(entry);
76
95
  }
77
96
  });
78
- if (children.length + descedants.length === 0) {
97
+ if (nested.size + descedants.length === 0) {
79
98
  return null;
80
99
  }
81
- const nested = new MultiMap(children.map(entry => [entry.name, entry]));
82
100
  for (const descedant of descedants) {
83
101
  if (!childrenNames.has(descedant.name)) {
84
102
  nested.add(descedant.name, descedant);
@@ -1,9 +1,9 @@
1
- import { computeViews } from '@likec4/core';
1
+ import { ModelIndex, assignNavigateTo, computeView } from '@likec4/core';
2
2
  import { DefaultElementShape, DefaultThemeColor } from '@likec4/core/types';
3
3
  import { compareByFqnHierarchically, parentFqn } from '@likec4/core/utils';
4
4
  import { DocumentState, getDocument } from 'langium';
5
5
  import objectHash from 'object-hash';
6
- import { clone, isNil } from 'rambdax';
6
+ import { clone } from 'rambdax';
7
7
  import * as R from 'remeda';
8
8
  import invariant from 'tiny-invariant';
9
9
  import { ElementViewOps, ast, cleanParsedModel, isLikeC4LangiumDocument, isValidLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle } from '../ast';
@@ -11,6 +11,7 @@ import { elementRef, strictElementRefFqn } from '../elementRef';
11
11
  import { logger } from '../logger';
12
12
  import { Rpc } from '../protocol';
13
13
  import { failExpectedNever } from '../utils';
14
+ import stripIndent from 'strip-indent';
14
15
  export class LikeC4ModelBuilder {
15
16
  services;
16
17
  fqnIndex;
@@ -46,7 +47,7 @@ export class LikeC4ModelBuilder {
46
47
  return this.services.shared.lsp.Connection;
47
48
  }
48
49
  documents() {
49
- return this.langiumDocuments.all.toArray().filter(isValidLikeC4LangiumDocument);
50
+ return this.langiumDocuments.all.filter(isValidLikeC4LangiumDocument).toArray();
50
51
  }
51
52
  buildModel() {
52
53
  const docs = this.documents();
@@ -72,10 +73,6 @@ export class LikeC4ModelBuilder {
72
73
  }
73
74
  return null;
74
75
  };
75
- const toModelRelation = (rel) => {
76
- const { astPath, ...model } = rel;
77
- return model;
78
- };
79
76
  const elements = R.pipe(R.flatMap(docs, d => d.c4Elements), R.map(toModelElement), R.compact, R.sort(compareByFqnHierarchically), R.reduce((acc, el) => {
80
77
  const parent = parentFqn(el.id);
81
78
  if (!parent || parent in acc) {
@@ -87,7 +84,18 @@ export class LikeC4ModelBuilder {
87
84
  }
88
85
  return acc;
89
86
  }, {}));
90
- const relations = R.pipe(R.flatMap(docs, d => d.c4Relations), R.map(toModelRelation), R.filter(({ source, target }) => source in elements && target in elements), R.mapToObj(r => [r.id, r]));
87
+ const toModelRelation = ({ astPath, source, target, ...model }) => {
88
+ if (source in elements && target in elements) {
89
+ return {
90
+ source,
91
+ target,
92
+ ...model
93
+ };
94
+ }
95
+ return null;
96
+ };
97
+ const relations = R.pipe(R.flatMap(docs, d => d.c4Relations), R.map(toModelRelation), R.compact, R.mapToObj(r => [r.id, r]));
98
+ const modelIndex = ModelIndex.from({ elements, relations });
91
99
  const toModelView = (view) => {
92
100
  // eslint-disable-next-line prefer-const
93
101
  let { astPath, rules, title, ...model } = view;
@@ -97,18 +105,19 @@ export class LikeC4ModelBuilder {
97
105
  if (!title && view.id === 'index') {
98
106
  title = 'Landscape view';
99
107
  }
100
- return {
108
+ return computeView({
101
109
  ...model,
102
110
  ...(title && { title }),
103
111
  rules: clone(rules)
104
- };
112
+ }, modelIndex);
105
113
  };
106
- const views = R.pipe(docs, R.flatMap(d => d.c4Views), R.map(toModelView), R.filter(v => isNil(v.viewOf) || v.viewOf in elements), R.mapToObj(v => [v.id, v]));
107
- return computeViews({
114
+ const views = R.pipe(R.flatMap(docs, d => d.c4Views), R.map(toModelView), R.compact);
115
+ assignNavigateTo(views);
116
+ return {
108
117
  elements,
109
118
  relations,
110
- views
111
- });
119
+ views: R.mapToObj(views, v => [v.id, v])
120
+ };
112
121
  }
113
122
  catch (e) {
114
123
  logger.error(e);
@@ -192,7 +201,7 @@ export class LikeC4ModelBuilder {
192
201
  astPath,
193
202
  title: title ?? astNode.name,
194
203
  ...(technology && { technology }),
195
- ...(description && { description }),
204
+ ...(description && { description: stripIndent(description).trim() }),
196
205
  ...(tags.length > 0 ? { tags } : {}),
197
206
  ...(shape && shape !== DefaultElementShape ? { shape } : {}),
198
207
  ...(color && color !== DefaultThemeColor ? { color } : {})
@@ -345,10 +354,10 @@ export class LikeC4ModelBuilder {
345
354
  clearTimeout(this.scheduledCb);
346
355
  }
347
356
  this.scheduledCb = setTimeout(() => {
348
- this.scheduledCb = null;
349
357
  logger.debug('send onDidChangeModel');
350
- void connection.sendNotification(Rpc.onDidChangeModel);
351
- }, 300);
358
+ this.scheduledCb = null;
359
+ void connection.sendNotification(Rpc.onDidChangeModel, '');
360
+ }, 350);
352
361
  }
353
362
  }
354
363
  //# sourceMappingURL=model-builder.js.map
@@ -1,5 +1,5 @@
1
- import { findNodeForKeyword, findNodeForProperty, getDocument } from 'langium';
2
- import { ast, isParsedLikeC4LangiumDocument } from '../ast';
1
+ import { findNodeForProperty, getDocument } from 'langium';
2
+ import { ElementOps, ast, isParsedLikeC4LangiumDocument } from '../ast';
3
3
  export class LikeC4ModelLocator {
4
4
  services;
5
5
  fqnIndex;
@@ -10,21 +10,21 @@ export class LikeC4ModelLocator {
10
10
  this.langiumDocuments = services.shared.workspace.LangiumDocuments;
11
11
  }
12
12
  documents() {
13
- return this.langiumDocuments.all.toArray().filter(isParsedLikeC4LangiumDocument);
13
+ return this.langiumDocuments.all.filter(isParsedLikeC4LangiumDocument);
14
14
  }
15
15
  getParsedElement(astNode) {
16
+ const fqn = ElementOps.readId(astNode) ?? null;
17
+ if (!fqn)
18
+ return null;
16
19
  const doc = getDocument(astNode);
17
20
  if (!isParsedLikeC4LangiumDocument(doc)) {
18
21
  return null;
19
22
  }
20
- const fqn = this.fqnIndex.get(astNode);
21
- if (!fqn)
22
- return null;
23
23
  return doc.c4Elements.find(e => e.id === fqn) ?? null;
24
24
  }
25
25
  locateElement(fqn, property = 'name') {
26
26
  for (const doc of this.documents()) {
27
- if (doc.c4fqns && !doc.c4fqns.has(fqn)) {
27
+ if (!doc.c4fqns?.has(fqn)) {
28
28
  continue;
29
29
  }
30
30
  const element = doc.c4Elements.find(e => e.id === fqn);
@@ -68,7 +68,7 @@ export class LikeC4ModelLocator {
68
68
  };
69
69
  }
70
70
  }
71
- const targetNode = findNodeForKeyword(node.$cstNode, '->');
71
+ const targetNode = findNodeForProperty(node.$cstNode, 'arr');
72
72
  if (!targetNode) {
73
73
  return null;
74
74
  }
package/dist/module.js CHANGED
@@ -46,6 +46,9 @@ export function createLanguageServices(context) {
46
46
  try {
47
47
  console[method](message);
48
48
  connection.console[method](String(message));
49
+ if (method === 'error') {
50
+ connection.telemetry.logEvent({ eventName: 'error', message });
51
+ }
49
52
  }
50
53
  catch (error) {
51
54
  console.error(error);
@@ -1,6 +1,6 @@
1
1
  import type { Fqn, LikeC4Model, RelationID, ViewID } from '@likec4/core';
2
2
  import type { DocumentUri, Location } from 'vscode-languageserver-protocol';
3
- import { NotificationType0, RequestType0, RequestType } from 'vscode-languageserver-protocol';
3
+ import { NotificationType, RequestType0, RequestType } from 'vscode-languageserver-protocol';
4
4
  interface BuildDocumentsParams {
5
5
  docs: DocumentUri[];
6
6
  }
@@ -16,7 +16,7 @@ export declare const locateView: RequestType<{
16
16
  id: ViewID;
17
17
  }, Location | null, void>;
18
18
  export declare const Rpc: {
19
- readonly onDidChangeModel: NotificationType0;
19
+ readonly onDidChangeModel: NotificationType<string>;
20
20
  readonly fetchModel: RequestType0<{
21
21
  model: LikeC4Model | null;
22
22
  }, void>;
package/dist/protocol.js CHANGED
@@ -1,6 +1,6 @@
1
- import { NotificationType0, RequestType0, RequestType } from 'vscode-languageserver-protocol';
1
+ import { NotificationType, RequestType0, RequestType } from 'vscode-languageserver-protocol';
2
2
  //#region From server
3
- const onDidChangeModel = new NotificationType0('likec4/onDidChangeModel');
3
+ const onDidChangeModel = new NotificationType('likec4/onDidChangeModel');
4
4
  //#endregion
5
5
  //#region To server
6
6
  const fetchModel = new RequestType0('likec4/fetchModel');
@@ -67,29 +67,14 @@ We rebuild: [
67
67
  }
68
68
  await services.shared.workspace.DocumentBuilder.update(changed, [], cancelToken);
69
69
  });
70
- connection.onRequest(Rpc.locateElement, async ({ element, property }, _cancelToken) => {
71
- try {
72
- return modelLocator.locateElement(element, property ?? 'name');
73
- }
74
- catch (e) {
75
- return Promise.reject(e);
76
- }
70
+ connection.onRequest(Rpc.locateElement, ({ element, property }) => {
71
+ return modelLocator.locateElement(element, property ?? 'name');
77
72
  });
78
- connection.onRequest(Rpc.locateRelation, async ({ id }, _cancelToken) => {
79
- try {
80
- return modelLocator.locateRelation(id);
81
- }
82
- catch (e) {
83
- return Promise.reject(e);
84
- }
73
+ connection.onRequest(Rpc.locateRelation, ({ id }) => {
74
+ return modelLocator.locateRelation(id);
85
75
  });
86
- connection.onRequest(Rpc.locateView, async ({ id }, _cancelToken) => {
87
- try {
88
- return modelLocator.locateView(id);
89
- }
90
- catch (e) {
91
- return Promise.reject(e);
92
- }
76
+ connection.onRequest(Rpc.locateView, ({ id }) => {
77
+ return modelLocator.locateView(id);
93
78
  });
94
79
  }
95
80
  //# sourceMappingURL=registerProtocolHandlers.js.map