@likec4/language-server 0.29.0 → 0.30.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.
@@ -1,11 +1,10 @@
1
- import { failExpectedNever } from '@likec4/core';
2
- import { Fqn } from '@likec4/core/types';
1
+ import { failExpectedNever, AsFqn } from '@likec4/core';
3
2
  import { MultiMap } from 'langium';
4
3
  import { isEmpty, isNil } from 'remeda';
5
4
  import { ElementOps, ast } from '../ast';
6
5
  import { strictElementRefFqn } from '../elementRef';
7
6
  export function computeDocumentFqn(document, services) {
8
- const c4fqns = document.c4fqns = new MultiMap();
7
+ const c4fqns = (document.c4fqns = new MultiMap());
9
8
  const { model } = document.parseResult.value;
10
9
  if (!model?.elements) {
11
10
  return;
@@ -26,7 +25,7 @@ export function computeDocumentFqn(document, services) {
26
25
  continue;
27
26
  }
28
27
  if (ast.isElement(el)) {
29
- const fqn = Fqn(el.name, parent);
28
+ const fqn = AsFqn(el.name, parent);
30
29
  const path = locator.getAstNodePath(el);
31
30
  c4fqns.add(fqn, path);
32
31
  ElementOps.writeId(el, fqn);
@@ -1,6 +1,5 @@
1
- import type { Fqn } from '@likec4/core/types';
2
- import type { LangiumDocument, LangiumDocuments } from 'langium';
3
- import { StreamImpl } from 'langium';
1
+ import type { Fqn } from '@likec4/core';
2
+ import type { LangiumDocument, LangiumDocuments, Stream } from 'langium';
4
3
  import type { ast } from '../ast';
5
4
  import { type LikeC4LangiumDocument } from '../ast';
6
5
  import type { LikeC4Services } from '../module';
@@ -15,18 +14,20 @@ export interface FqnIndexEntry {
15
14
  path: string;
16
15
  }
17
16
  export declare class FqnIndex {
18
- private services;
19
17
  protected langiumDocuments: LangiumDocuments;
20
18
  constructor(services: LikeC4Services);
21
19
  private documents;
22
20
  private entries;
23
- get(el: ast.Element): Fqn | null;
24
- byFqn(fqn: Fqn): import("langium").Stream<{
21
+ getFqn(el: ast.Element): Fqn | null;
22
+ byFqn(fqn: Fqn): Stream<{
25
23
  path: string;
26
- doc: FqnIndexedDocument;
24
+ doc: LikeC4LangiumDocument;
27
25
  }>;
28
- directChildrenOf(parent: Fqn): StreamImpl<IterableIterator<FqnIndexEntry> | null, FqnIndexEntry>;
29
- uniqueDescedants(parent: Fqn): StreamImpl<IterableIterator<FqnIndexEntry> | null, FqnIndexEntry>;
26
+ directChildrenOf(parent: Fqn): Stream<FqnIndexEntry>;
27
+ /**
28
+ * Returns descedant elements with unique names in the scope
29
+ */
30
+ uniqueDescedants(parent: Fqn): Stream<FqnIndexEntry>;
30
31
  }
31
32
  export {};
32
33
  //# sourceMappingURL=fqn-index.d.ts.map
@@ -1,17 +1,15 @@
1
- import { nameFromFqn, parentFqn } from '@likec4/core/utils';
1
+ import { nameFromFqn, parentFqn } from '@likec4/core';
2
2
  import { DONE_RESULT, DocumentState, 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
7
  export function isFqnIndexedDocument(doc) {
8
- return isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns);
8
+ return (isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !isNil(doc.c4fqns));
9
9
  }
10
10
  export class FqnIndex {
11
- services;
12
11
  langiumDocuments;
13
12
  constructor(services) {
14
- this.services = services;
15
13
  this.langiumDocuments = services.shared.workspace.LangiumDocuments;
16
14
  services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, (docs, _cancelToken) => {
17
15
  for (const doc of docs) {
@@ -32,7 +30,7 @@ export class FqnIndex {
32
30
  entries() {
33
31
  return this.documents().flatMap(doc => doc.c4fqns.entries().map(([fqn, path]) => ({ fqn, path, doc })));
34
32
  }
35
- get(el) {
33
+ getFqn(el) {
36
34
  return ElementOps.readId(el) ?? null;
37
35
  // if (fqn) {
38
36
  // const doc = getDocument(el)
@@ -75,6 +73,9 @@ export class FqnIndex {
75
73
  return DONE_RESULT;
76
74
  });
77
75
  }
76
+ /**
77
+ * Returns descedant elements with unique names in the scope
78
+ */
78
79
  uniqueDescedants(parent) {
79
80
  return new StreamImpl(() => {
80
81
  const prefix = `${parent}.`;
@@ -86,6 +87,7 @@ export class FqnIndex {
86
87
  .forEach(e => {
87
88
  const name = nameFromFqn(e.fqn);
88
89
  const entry = { ...e, name };
90
+ // To keep direct children always
89
91
  if (parentFqn(e.fqn) === parent) {
90
92
  childrenNames.add(name);
91
93
  nested.add(name, entry);
@@ -67,27 +67,32 @@ export class LikeC4ModelBuilder {
67
67
  kinds: {}
68
68
  };
69
69
  R.forEach(R.map(docs, R.prop('c4Specification')), spec => Object.assign(c4Specification.kinds, spec.kinds));
70
- const toModelElement = (el) => {
71
- const kind = c4Specification.kinds[el.kind];
70
+ const toModelElement = ({ astPath, ...parsed }) => {
71
+ const kind = c4Specification.kinds[parsed.kind];
72
72
  if (kind) {
73
- const { astPath, ...model } = el;
74
73
  return {
75
- ...(kind.shape !== DefaultElementShape ? { shape: kind.shape } : {}),
76
- ...(kind.color !== DefaultThemeColor ? { color: kind.color } : {}),
77
- ...model
74
+ shape: kind.shape,
75
+ color: kind.color,
76
+ description: null,
77
+ technology: null,
78
+ tags: [],
79
+ ...parsed
78
80
  };
79
81
  }
80
82
  return null;
81
83
  };
82
84
  const elements = R.pipe(R.flatMap(docs, d => d.c4Elements), R.map(toModelElement), R.compact, R.sort(compareByFqnHierarchically), R.reduce((acc, el) => {
83
85
  const parent = parentFqn(el.id);
84
- if (!parent || parent in acc) {
85
- if (el.id in acc) {
86
- logger.warn(`Duplicate element id: ${el.id}`);
87
- return acc;
88
- }
89
- acc[el.id] = el;
86
+ if (parent && R.isNil(acc[parent])) {
87
+ logger.warn(`No parent found for ${el.id}`);
88
+ return acc;
89
+ }
90
+ if (el.id in acc) {
91
+ // should not happen, as validated
92
+ logger.warn(`Duplicate element id: ${el.id}`);
93
+ return acc;
90
94
  }
95
+ acc[el.id] = el;
91
96
  return acc;
92
97
  }, {}));
93
98
  const toModelRelation = ({ astPath, source, target, ...model }) => {
@@ -339,7 +344,7 @@ export class LikeC4ModelBuilder {
339
344
  if (ast.isExtendElement(node)) {
340
345
  return strictElementRefFqn(node.element);
341
346
  }
342
- const fqn = this.fqnIndex.get(node);
347
+ const fqn = this.fqnIndex.getFqn(node);
343
348
  invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
344
349
  return fqn;
345
350
  }
@@ -17,18 +17,15 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
17
17
  this.fqnIndex = services.likec4.FqnIndex;
18
18
  }
19
19
  directChildrenOf(parent) {
20
- return this.fqnIndex
21
- .directChildrenOf(parent)
22
- .map(toAstNodeDescription);
20
+ return this.fqnIndex.directChildrenOf(parent).map(toAstNodeDescription);
23
21
  }
22
+ // we need lazy resolving here
24
23
  uniqueDescedants(of) {
25
24
  return new StreamImpl(() => {
26
25
  const element = of();
27
- const fqn = element && this.fqnIndex.get(element);
26
+ const fqn = element && this.fqnIndex.getFqn(element);
28
27
  if (fqn) {
29
- return this.fqnIndex.uniqueDescedants(fqn)
30
- .map(toAstNodeDescription)
31
- .iterator();
28
+ return this.fqnIndex.uniqueDescedants(fqn).map(toAstNodeDescription).iterator();
32
29
  }
33
30
  return null;
34
31
  }, iterator => {
@@ -57,21 +54,21 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
57
54
  getScope(context) {
58
55
  const referenceType = this.reflection.getReferenceType(context);
59
56
  try {
60
- const node = context.container;
57
+ const container = context.container;
61
58
  // const path = this.services.workspace.AstNodeLocator.getAstNodePath(node)
62
59
  if (referenceType === ast.Element) {
63
- if (ast.isStrictElementRef(node)) {
64
- if (isElementRefHead(node)) {
60
+ if (ast.isStrictElementRef(container)) {
61
+ if (isElementRefHead(container)) {
65
62
  return this.getGlobalScope(referenceType);
66
63
  }
67
- const parent = parentStrictElementRef(node);
64
+ const parent = parentStrictElementRef(container);
68
65
  return new StreamScope(this.directChildrenOf(parent));
69
66
  }
70
- if (ast.isElementRef(node) && !isElementRefHead(node)) {
71
- return new StreamScope(this.scopeElementRef(node));
67
+ if (ast.isElementRef(container) && !isElementRefHead(container)) {
68
+ return new StreamScope(this.scopeElementRef(container));
72
69
  }
73
70
  }
74
- return this.computeScope(node, referenceType);
71
+ return this.computeScope(container, referenceType);
75
72
  }
76
73
  catch (e) {
77
74
  // console.error(e)
@@ -1,7 +1,7 @@
1
1
  export const elementChecks = (services) => {
2
2
  const fqnIndex = services.likec4.FqnIndex;
3
3
  return (el, accept) => {
4
- const fqn = fqnIndex.get(el);
4
+ const fqn = fqnIndex.getFqn(el);
5
5
  if (!fqn) {
6
6
  accept('error', 'Not indexed element', {
7
7
  node: el,
@@ -11,12 +11,6 @@ export const elementChecks = (services) => {
11
11
  }
12
12
  const withSameFqn = fqnIndex.byFqn(fqn).limit(2).count();
13
13
  if (withSameFqn > 1) {
14
- // console.error(withSameFqn.map(e => ({
15
- // fqn,
16
- // name: el.name,
17
- // path: e.path,
18
- // doc: e.doc.uri.toString()
19
- // })))
20
14
  accept('error', `Duplicate element name ${el.name !== fqn ? el.name + ' (' + fqn + ')' : el.name}`, {
21
15
  node: el,
22
16
  property: 'name'
@@ -6,8 +6,8 @@ export const relationChecks = (services) => {
6
6
  return (el, accept) => {
7
7
  try {
8
8
  const coupling = resolveRelationPoints(el);
9
- const target = fqnIndex.get(coupling.target);
10
- const source = fqnIndex.get(coupling.source);
9
+ const target = fqnIndex.getFqn(coupling.target);
10
+ const source = fqnIndex.getFqn(coupling.source);
11
11
  if (!target || !source) {
12
12
  if (!target) {
13
13
  accept('error', 'Target not found', {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "0.29.0",
4
+ "version": "0.30.0",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -59,7 +59,7 @@
59
59
  "test:watch": "run -T vitest"
60
60
  },
61
61
  "dependencies": {
62
- "@likec4/core": "0.29.0",
62
+ "@likec4/core": "0.30.0",
63
63
  "langium": "^1.2.1",
64
64
  "nanoid": "^4.0.2",
65
65
  "object-hash": "^3.0.0",