@likec4/language-server 1.27.3 → 1.28.1

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 (113) hide show
  1. package/dist/LikeC4LanguageServices.js +6 -7
  2. package/dist/ast.d.ts +16 -9
  3. package/dist/ast.js +58 -79
  4. package/dist/bundled.mjs +2161 -2141
  5. package/dist/config/schema.d.ts +3 -3
  6. package/dist/config/schema.js +12 -5
  7. package/dist/documentation/documentation-provider.js +3 -1
  8. package/dist/formatting/LikeC4Formatter.d.ts +0 -2
  9. package/dist/formatting/LikeC4Formatter.js +24 -53
  10. package/dist/generated/ast.d.ts +128 -233
  11. package/dist/generated/ast.js +136 -308
  12. package/dist/generated/grammar.js +1 -1
  13. package/dist/lsp/CompletionProvider.d.ts +3 -0
  14. package/dist/lsp/CompletionProvider.js +128 -113
  15. package/dist/lsp/DocumentLinkProvider.js +6 -3
  16. package/dist/lsp/HoverProvider.js +3 -1
  17. package/dist/lsp/SemanticTokenProvider.js +33 -43
  18. package/dist/model/builder/MergedSpecification.d.ts +5 -3
  19. package/dist/model/builder/MergedSpecification.js +21 -7
  20. package/dist/model/builder/buildModel.d.ts +6 -1
  21. package/dist/model/builder/buildModel.js +20 -15
  22. package/dist/model/deployments-index.js +4 -2
  23. package/dist/model/fqn-index.d.ts +4 -2
  24. package/dist/model/fqn-index.js +28 -5
  25. package/dist/model/model-builder.d.ts +2 -2
  26. package/dist/model/model-builder.js +54 -16
  27. package/dist/model/model-locator.js +7 -4
  28. package/dist/model/model-parser.d.ts +215 -52
  29. package/dist/model/model-parser.js +6 -2
  30. package/dist/model/parser/Base.d.ts +11 -2
  31. package/dist/model/parser/Base.js +138 -3
  32. package/dist/model/parser/DeploymentModelParser.d.ts +19 -2
  33. package/dist/model/parser/DeploymentModelParser.js +19 -29
  34. package/dist/model/parser/DeploymentViewParser.d.ts +18 -2
  35. package/dist/model/parser/DeploymentViewParser.js +6 -24
  36. package/dist/model/parser/FqnRefParser.d.ts +18 -3
  37. package/dist/model/parser/FqnRefParser.js +264 -40
  38. package/dist/model/parser/GlobalsParser.d.ts +35 -18
  39. package/dist/model/parser/ImportsParser.d.ts +32 -0
  40. package/dist/model/parser/ImportsParser.js +26 -0
  41. package/dist/model/parser/ModelParser.d.ts +26 -2
  42. package/dist/model/parser/ModelParser.js +21 -41
  43. package/dist/model/parser/PredicatesParser.d.ts +35 -12
  44. package/dist/model/parser/PredicatesParser.js +20 -271
  45. package/dist/model/parser/SpecificationParser.d.ts +8 -0
  46. package/dist/model/parser/SpecificationParser.js +5 -9
  47. package/dist/model/parser/ViewsParser.d.ts +36 -19
  48. package/dist/model/parser/ViewsParser.js +16 -12
  49. package/dist/model-change/changeElementStyle.d.ts +2 -2
  50. package/dist/model-change/changeElementStyle.js +9 -6
  51. package/dist/references/name-provider.js +8 -2
  52. package/dist/references/scope-computation.d.ts +1 -1
  53. package/dist/references/scope-computation.js +33 -3
  54. package/dist/references/scope-provider.d.ts +7 -8
  55. package/dist/references/scope-provider.js +59 -41
  56. package/dist/shared/NodeKindProvider.js +4 -2
  57. package/dist/test/testServices.d.ts +2 -0
  58. package/dist/test/testServices.js +4 -1
  59. package/dist/utils/elementRef.d.ts +1 -1
  60. package/dist/utils/elementRef.js +6 -1
  61. package/dist/utils/fqnRef.d.ts +3 -0
  62. package/dist/utils/fqnRef.js +15 -4
  63. package/dist/utils/index.d.ts +1 -0
  64. package/dist/utils/index.js +9 -0
  65. package/dist/utils/projectId.d.ts +2 -1
  66. package/dist/utils/projectId.js +11 -1
  67. package/dist/validation/_shared.js +2 -2
  68. package/dist/validation/deployment-checks.js +24 -10
  69. package/dist/validation/element-ref.d.ts +4 -0
  70. package/dist/validation/element-ref.js +12 -0
  71. package/dist/validation/element.d.ts +1 -1
  72. package/dist/validation/element.js +1 -1
  73. package/dist/validation/imports.d.ts +5 -0
  74. package/dist/validation/imports.js +30 -0
  75. package/dist/validation/index.d.ts +1 -1
  76. package/dist/validation/index.js +47 -45
  77. package/dist/validation/relation.d.ts +2 -2
  78. package/dist/validation/relation.js +24 -27
  79. package/dist/validation/specification.d.ts +9 -9
  80. package/dist/validation/specification.js +9 -9
  81. package/dist/validation/view-predicates/{element-with.d.ts → fqn-expr-with.d.ts} +1 -1
  82. package/dist/validation/view-predicates/fqn-expr-with.js +42 -0
  83. package/dist/validation/view-predicates/fqn-ref-expr.d.ts +4 -0
  84. package/dist/validation/view-predicates/fqn-ref-expr.js +53 -0
  85. package/dist/validation/view-predicates/incoming.d.ts +1 -1
  86. package/dist/validation/view-predicates/incoming.js +2 -2
  87. package/dist/validation/view-predicates/index.d.ts +6 -6
  88. package/dist/validation/view-predicates/index.js +6 -6
  89. package/dist/validation/view-predicates/outgoing.d.ts +1 -1
  90. package/dist/validation/view-predicates/outgoing.js +8 -4
  91. package/dist/validation/view-predicates/{expanded-element.d.ts → relation-expr.d.ts} +1 -1
  92. package/dist/validation/view-predicates/relation-expr.js +39 -0
  93. package/dist/validation/view-predicates/relation-with.d.ts +1 -1
  94. package/dist/validation/view-predicates/relation-with.js +8 -5
  95. package/dist/views/likec4-views.d.ts +1 -0
  96. package/dist/views/likec4-views.js +23 -4
  97. package/dist/workspace/AstNodeDescriptionProvider.d.ts +1 -1
  98. package/dist/workspace/AstNodeDescriptionProvider.js +2 -3
  99. package/dist/workspace/IndexManager.d.ts +1 -1
  100. package/dist/workspace/IndexManager.js +5 -4
  101. package/dist/workspace/LangiumDocuments.d.ts +1 -1
  102. package/dist/workspace/LangiumDocuments.js +3 -5
  103. package/dist/workspace/ProjectsManager.d.ts +25 -7
  104. package/dist/workspace/ProjectsManager.js +76 -32
  105. package/dist/workspace/WorkspaceManager.d.ts +4 -5
  106. package/dist/workspace/WorkspaceManager.js +53 -20
  107. package/package.json +17 -13
  108. package/dist/validation/dynamic-view-rule.d.ts +0 -4
  109. package/dist/validation/dynamic-view-rule.js +0 -17
  110. package/dist/validation/view-predicates/element-with.js +0 -31
  111. package/dist/validation/view-predicates/expanded-element.js +0 -12
  112. package/dist/validation/view-predicates/expression-v2.d.ts +0 -5
  113. package/dist/validation/view-predicates/expression-v2.js +0 -83
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  computeColorValues,
3
3
  DeploymentElement,
4
+ isGlobalFqn,
4
5
  parentFqn,
5
6
  sortByFqnHierarchically
6
7
  } from "@likec4/core";
@@ -22,7 +23,7 @@ import { logger } from "../../logger.js";
22
23
  import { resolveRelativePaths } from "../../view-utils/index.js";
23
24
  import { MergedExtends } from "./MergedExtends.js";
24
25
  import { MergedSpecification } from "./MergedSpecification.js";
25
- export function buildModel(docs) {
26
+ export function buildModelData(docs) {
26
27
  const c4Specification = new MergedSpecification(docs);
27
28
  const customColorDefinitions = mapValues(
28
29
  c4Specification.specs.colors,
@@ -58,7 +59,7 @@ export function buildModel(docs) {
58
59
  flatMap((d) => map(d.c4Relations, c4Specification.toModelRelation)),
59
60
  filter((rel) => {
60
61
  if (!rel) return false;
61
- if (isNullish(elements[rel.source]) || isNullish(elements[rel.target])) {
62
+ if (isNullish(elements[rel.source]) && !isGlobalFqn(rel.source) || isNullish(elements[rel.target]) && !isGlobalFqn(rel.target)) {
62
63
  logger.debug`Invalid relation ${rel.id}
63
64
  source: ${rel.source} resolved: ${!!elements[rel.source]}
64
65
  target: ${rel.target} resolved: ${!!elements[rel.target]}\n`;
@@ -176,19 +177,23 @@ export function buildModel(docs) {
176
177
  resolveRulesExtendedViews
177
178
  );
178
179
  return {
179
- specification: {
180
- tags: Array.from(c4Specification.specs.tags),
181
- elements: c4Specification.specs.elements,
182
- relationships: c4Specification.specs.relationships,
183
- deployments: c4Specification.specs.deployments
180
+ data: {
181
+ specification: {
182
+ tags: Array.from(c4Specification.specs.tags),
183
+ elements: c4Specification.specs.elements,
184
+ relationships: c4Specification.specs.relationships,
185
+ deployments: c4Specification.specs.deployments
186
+ },
187
+ elements,
188
+ relations,
189
+ globals: c4Specification.globals,
190
+ views,
191
+ deployments: {
192
+ elements: deploymentElements,
193
+ relations: deploymentRelations
194
+ },
195
+ imports: {}
184
196
  },
185
- elements,
186
- relations,
187
- globals: c4Specification.globals,
188
- views,
189
- deployments: {
190
- elements: deploymentElements,
191
- relations: deploymentRelations
192
- }
197
+ imports: c4Specification.imports
193
198
  };
194
199
  }
@@ -20,6 +20,7 @@ export class DeploymentsIndex extends FqnIndex {
20
20
  if (rootNodes.length === 0) {
21
21
  return DocumentFqnIndex.EMPTY;
22
22
  }
23
+ const projectId = document.likec4ProjectId ??= this.projects.belongsTo(document);
23
24
  const root = new Array();
24
25
  const children = new MultiMap();
25
26
  const descendants = new MultiMap();
@@ -29,7 +30,8 @@ export class DeploymentsIndex extends FqnIndex {
29
30
  const createAndSaveDescription = (node, name, fqn) => {
30
31
  const desc = {
31
32
  ...Descriptions.createDescription(node, name, document),
32
- id: fqn
33
+ id: fqn,
34
+ likec4ProjectId: projectId
33
35
  };
34
36
  ElementOps.writeId(node, fqn);
35
37
  byfqn.set(fqn, desc);
@@ -93,6 +95,6 @@ export class DeploymentsIndex extends FqnIndex {
93
95
  logWarnError(e);
94
96
  }
95
97
  }
96
- return new DocumentFqnIndex(root, children, descendants, byfqn, this.projects.belongsTo(document));
98
+ return new DocumentFqnIndex(root, children, descendants, byfqn, projectId);
97
99
  }
98
100
  }
@@ -1,11 +1,11 @@
1
1
  import { type Fqn, type ProjectId } from '@likec4/core/types';
2
2
  import { DefaultWeakMap, MultiMap } from '@likec4/core/utils';
3
- import { type AstNode, type Stream, WorkspaceCache } from 'langium';
3
+ import { type Stream, WorkspaceCache } from 'langium';
4
4
  import { type AstNodeDescriptionWithFqn, type LikeC4LangiumDocument, ast } from '../ast';
5
5
  import type { LikeC4Services } from '../module';
6
6
  import { ADisposable } from '../utils';
7
7
  import { type LangiumDocuments, ProjectsManager } from '../workspace';
8
- export declare class FqnIndex<AstNd extends AstNode = ast.Element> extends ADisposable {
8
+ export declare class FqnIndex<AstNd = ast.Element> extends ADisposable {
9
9
  protected services: LikeC4Services;
10
10
  private cachePrefix;
11
11
  protected projects: ProjectsManager;
@@ -15,8 +15,10 @@ export declare class FqnIndex<AstNd extends AstNode = ast.Element> extends ADisp
15
15
  constructor(services: LikeC4Services, cachePrefix?: string);
16
16
  private documents;
17
17
  get(document: LikeC4LangiumDocument): DocumentFqnIndex;
18
+ resolve(reference: ast.Referenceable): Fqn;
18
19
  getFqn(el: AstNd): Fqn;
19
20
  byFqn(projectId: ProjectId, fqn: Fqn): Stream<AstNodeDescriptionWithFqn>;
21
+ rootElements(projectId: ProjectId): Stream<AstNodeDescriptionWithFqn>;
20
22
  directChildrenOf(projectId: ProjectId, parent: Fqn): Stream<AstNodeDescriptionWithFqn>;
21
23
  /**
22
24
  * Returns descedant elements with unique names in the scope
@@ -29,11 +29,10 @@ export class FqnIndex extends ADisposable {
29
29
  this.onDispose(
30
30
  services.shared.workspace.DocumentBuilder.onDocumentPhase(
31
31
  DocumentState.IndexedContent,
32
- async (doc, _cancelToken) => {
32
+ (doc) => {
33
33
  if (isLikeC4LangiumDocument(doc)) {
34
34
  this.documentCache.set(doc, this.createDocumentIndex(doc));
35
35
  }
36
- return await Promise.resolve();
37
36
  }
38
37
  )
39
38
  );
@@ -53,6 +52,15 @@ export class FqnIndex extends ADisposable {
53
52
  }
54
53
  return this.documentCache.get(document);
55
54
  }
55
+ resolve(reference) {
56
+ if (reference.$type === "Imported") {
57
+ return this.getFqn(reference.imported.ref);
58
+ }
59
+ if (reference.$type === "Element") {
60
+ return this.getFqn(reference);
61
+ }
62
+ return this.services.likec4.DeploymentsIndex.getFqn(reference);
63
+ }
56
64
  getFqn(el) {
57
65
  invariant(ast.isElement(el) || ast.isDeploymentElement(el));
58
66
  let id = ElementOps.readId(el);
@@ -66,12 +74,25 @@ export class FqnIndex extends ADisposable {
66
74
  return nonNullable(ElementOps.readId(el), "Element fqn must be set, invalid state");
67
75
  }
68
76
  byFqn(projectId, fqn) {
69
- return stream(this.workspaceCache.get(`${this.cachePrefix}:${projectId}:${fqn}`, () => {
77
+ return stream(this.workspaceCache.get(`${this.cachePrefix}:${projectId}:fqn:${fqn}`, () => {
70
78
  return this.documents(projectId).toArray().flatMap((doc) => {
71
79
  return this.get(doc).byFqn(fqn);
72
80
  });
73
81
  }));
74
82
  }
83
+ rootElements(projectId) {
84
+ return stream(
85
+ this.workspaceCache.get(`${this.cachePrefix}:${projectId}:rootElements`, () => {
86
+ const allchildren = this.documents(projectId).reduce((map, doc) => {
87
+ this.get(doc).rootElements().forEach((desc) => {
88
+ map.set(desc.name, desc);
89
+ });
90
+ return map;
91
+ }, new MultiMap());
92
+ return uniqueByName(allchildren).sort((a, b) => compareNatural(a.name, b.name));
93
+ })
94
+ );
95
+ }
75
96
  directChildrenOf(projectId, parent) {
76
97
  return stream(
77
98
  this.workspaceCache.get(`${this.cachePrefix}:${projectId}:directChildrenOf:${parent}`, () => {
@@ -118,6 +139,7 @@ export class FqnIndex extends ADisposable {
118
139
  if (rootElements.length === 0) {
119
140
  return DocumentFqnIndex.EMPTY;
120
141
  }
142
+ const projectId = document.likec4ProjectId ??= this.projects.belongsTo(document);
121
143
  const root = new Array();
122
144
  const children = new MultiMap();
123
145
  const descendants = new MultiMap();
@@ -126,7 +148,8 @@ export class FqnIndex extends ADisposable {
126
148
  const createAndSaveDescription = (node, name, fqn) => {
127
149
  const desc = {
128
150
  ...Descriptions.createDescription(node, name, document),
129
- id: fqn
151
+ id: fqn,
152
+ likec4ProjectId: projectId
130
153
  };
131
154
  ElementOps.writeId(node, fqn);
132
155
  byfqn.set(fqn, desc);
@@ -184,7 +207,7 @@ export class FqnIndex extends ADisposable {
184
207
  logWarnError(e);
185
208
  }
186
209
  }
187
- return new DocumentFqnIndex(root, children, descendants, byfqn, this.projects.belongsTo(document));
210
+ return new DocumentFqnIndex(root, children, descendants, byfqn, projectId);
188
211
  }
189
212
  }
190
213
  function uniqueByName(multimap) {
@@ -18,7 +18,6 @@ export declare class DefaultLikeC4ModelBuilder extends ADisposable implements Li
18
18
  private listeners;
19
19
  private cache;
20
20
  private DocumentBuilder;
21
- private LangiumDocuments;
22
21
  private mutex;
23
22
  constructor(services: LikeC4Services);
24
23
  /**
@@ -26,7 +25,8 @@ export declare class DefaultLikeC4ModelBuilder extends ADisposable implements Li
26
25
  * This method is internal and should to be called only when all documents are known to be parsed.
27
26
  * Otherwise, the model may be incomplete.
28
27
  */
29
- private unsafeSyncParseModel;
28
+ private unsafeSyncParseModelData;
29
+ private unsafeSyncJoinedModelData;
30
30
  parseModel(projectId?: c4.ProjectId | undefined, cancelToken?: CancellationToken): Promise<c4.ParsedLikeC4ModelData | null>;
31
31
  private previousViews;
32
32
  /**
@@ -12,7 +12,9 @@ import {
12
12
  import prettyMs from "pretty-ms";
13
13
  import {
14
14
  filter,
15
+ flatMap,
15
16
  groupBy,
17
+ hasAtLeast,
16
18
  isNot,
17
19
  mapToObj,
18
20
  pipe,
@@ -24,7 +26,8 @@ import { isLikeC4Builtin } from "../likec4lib.js";
24
26
  import { logger as mainLogger, logWarnError } from "../logger.js";
25
27
  import { ADisposable } from "../utils/index.js";
26
28
  import { assignNavigateTo } from "../view-utils/index.js";
27
- import { buildModel } from "./builder/buildModel.js";
29
+ import { buildModelData } from "./builder/buildModel.js";
30
+ const parsedWithoutImportsCacheKey = (projectId) => `parsed-without-imports-${projectId}`;
28
31
  const parsedModelCacheKey = (projectId) => `parsed-model-${projectId}`;
29
32
  const computedModelCacheKey = (projectId) => `computed-model-${projectId}`;
30
33
  const logger = mainLogger.getChild("ModelBuilder");
@@ -34,7 +37,6 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
34
37
  listeners = [];
35
38
  cache;
36
39
  DocumentBuilder;
37
- LangiumDocuments;
38
40
  mutex;
39
41
  constructor(services) {
40
42
  super();
@@ -42,7 +44,6 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
42
44
  this.parser = services.likec4.ModelParser;
43
45
  this.cache = services.ValidatedWorkspaceCache;
44
46
  this.DocumentBuilder = services.shared.workspace.DocumentBuilder;
45
- this.LangiumDocuments = services.shared.workspace.LangiumDocuments;
46
47
  this.mutex = services.shared.workspace.WorkspaceLock;
47
48
  this.onDispose(
48
49
  this.DocumentBuilder.onUpdate((_changed, deleted) => {
@@ -69,20 +70,57 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
69
70
  * This method is internal and should to be called only when all documents are known to be parsed.
70
71
  * Otherwise, the model may be incomplete.
71
72
  */
72
- unsafeSyncParseModel(projectId) {
73
+ unsafeSyncParseModelData(projectId) {
73
74
  const cache = this.cache;
74
75
  const log = logger.getChild(["project", projectId]);
75
- if (cache.has(parsedModelCacheKey(projectId))) {
76
- log.debug("unsafeSyncParseModel from cache");
76
+ const key = parsedWithoutImportsCacheKey(projectId);
77
+ if (cache.has(key)) {
78
+ log.debug`unsafeSyncBuildModelData from cache, project ${projectId}`;
77
79
  }
78
- return cache.get(parsedModelCacheKey(projectId), () => {
79
- const docs = this.documents(projectId);
80
- if (docs.length === 0) {
81
- logger.debug(`no documents to build model - project ${projectId}`);
80
+ return cache.get(key, () => {
81
+ try {
82
+ const docs = this.documents(projectId);
83
+ if (docs.length === 0) {
84
+ logger.debug`no documents to build model, project ${projectId}`;
85
+ return null;
86
+ }
87
+ log.debug`unsafeSyncBuildModelData, project ${projectId}`;
88
+ return buildModelData(docs);
89
+ } catch (e) {
90
+ logWarnError(e);
82
91
  return null;
83
92
  }
84
- log.debug("unsafeSyncParseModel from documents");
85
- return buildModel(docs);
93
+ });
94
+ }
95
+ unsafeSyncJoinedModelData(projectId) {
96
+ const cache = this.cache;
97
+ const key = parsedModelCacheKey(projectId);
98
+ return cache.get(key, () => {
99
+ const result = this.unsafeSyncParseModelData(projectId);
100
+ if (!result) {
101
+ return null;
102
+ }
103
+ if (result.imports.size > 0) {
104
+ logger.debug`processing imports of ${projectId}`;
105
+ const imports = [...result.imports.associations()].reduce((acc, [projectId2, fqns]) => {
106
+ const anotherProject = this.unsafeSyncParseModelData(projectId2);
107
+ if (anotherProject) {
108
+ const imported = pipe(
109
+ [...fqns],
110
+ flatMap((fqn) => anotherProject.data.elements[fqn] ?? [])
111
+ );
112
+ if (hasAtLeast(imported, 1)) {
113
+ acc[projectId2] = imported;
114
+ }
115
+ }
116
+ return acc;
117
+ }, {});
118
+ return {
119
+ ...result.data,
120
+ imports
121
+ };
122
+ }
123
+ return result.data;
86
124
  });
87
125
  }
88
126
  async parseModel(projectId, cancelToken = CancellationToken.None) {
@@ -91,14 +129,14 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
91
129
  const cache = this.cache;
92
130
  const cached = cache.get(parsedModelCacheKey(project));
93
131
  if (cached) {
94
- log.debug("parseModel from cache");
132
+ log.debug`parseModel from cache, project ${project}`;
95
133
  return cached;
96
134
  }
97
135
  const t0 = performance.now();
98
136
  return await this.mutex.read(async () => {
99
137
  await interruptAndCheck(cancelToken);
100
- const result = this.unsafeSyncParseModel(project);
101
- log.debug(`parseModel in ${prettyMs(performance.now() - t0)}`);
138
+ const result = this.unsafeSyncJoinedModelData(project);
139
+ log.debug(`parseModel, project ${project} in ${prettyMs(performance.now() - t0)}`);
102
140
  return result;
103
141
  });
104
142
  }
@@ -112,7 +150,7 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
112
150
  const cache = this.cache;
113
151
  const viewsCache = this.cache;
114
152
  return cache.get(computedModelCacheKey(projectId), () => {
115
- const parsed = this.unsafeSyncParseModel(projectId);
153
+ const parsed = this.unsafeSyncJoinedModelData(projectId);
116
154
  if (!parsed) {
117
155
  return LikeC4Model.EMPTY;
118
156
  }
@@ -1,3 +1,4 @@
1
+ import { splitGlobalFqn } from "@likec4/core";
1
2
  import { AstUtils, GrammarUtils } from "langium";
2
3
  import { isString } from "remeda";
3
4
  import { ast } from "../ast.js";
@@ -50,8 +51,9 @@ export class LikeC4ModelLocator {
50
51
  return doc.c4Elements.find((e) => e.id === fqn) ?? null;
51
52
  }
52
53
  locateElement(fqn, projectId) {
53
- const _projectId = this.projects.ensureProjectId(projectId);
54
- const entry = this.fqnIndex.byFqn(_projectId, fqn).head();
54
+ let [_projectId, _fqn] = splitGlobalFqn(fqn);
55
+ _projectId ??= this.projects.ensureProjectId(projectId);
56
+ const entry = this.fqnIndex.byFqn(_projectId, _fqn).head();
55
57
  const docsegment = entry?.nameSegment ?? entry?.selectionSegment;
56
58
  if (!entry || !docsegment) {
57
59
  return null;
@@ -62,8 +64,9 @@ export class LikeC4ModelLocator {
62
64
  };
63
65
  }
64
66
  locateDeploymentElement(fqn, projectId) {
65
- const _projectId = this.projects.ensureProjectId(projectId);
66
- const entry = this.deploymentsIndex.byFqn(_projectId, fqn).head();
67
+ let [_projectId, _fqn] = splitGlobalFqn(fqn);
68
+ _projectId ??= this.projects.ensureProjectId(projectId);
69
+ const entry = this.deploymentsIndex.byFqn(_projectId, _fqn).head();
67
70
  const docsegment = entry?.nameSegment ?? entry?.selectionSegment;
68
71
  if (!entry || !docsegment) {
69
72
  return null;