@likec4/language-server 1.45.0 → 1.46.2
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.
- package/dist/LikeC4LanguageServices.d.ts +3 -0
- package/dist/LikeC4LanguageServices.js +2 -0
- package/dist/Rpc.js +13 -2
- package/dist/ast.d.ts +10 -0
- package/dist/ast.js +7 -0
- package/dist/bundled.mjs +3826 -4066
- package/dist/filesystem/ChokidarWatcher.js +2 -2
- package/dist/filesystem/LikeC4FileSystem.js +5 -1
- package/dist/filesystem/index.d.ts +2 -0
- package/dist/generated/ast.d.ts +46 -9
- package/dist/generated/ast.js +56 -4
- package/dist/generated/grammar.js +1 -1
- package/dist/generated-lib/icons.js +1 -1
- package/dist/lsp/DocumentSymbolProvider.js +12 -1
- package/dist/lsp/SemanticTokenProvider.js +36 -29
- package/dist/mcp/server/WithMCPServer.js +0 -2
- package/dist/mcp/tools/read-deployment.js +18 -0
- package/dist/mcp/tools/read-element.js +24 -0
- package/dist/model/builder/buildModel.js +70 -1
- package/dist/model/fqn-index.js +8 -2
- package/dist/model/model-parser.d.ts +3 -0
- package/dist/model/model-parser.js +7 -0
- package/dist/model/parser/Base.js +8 -3
- package/dist/model/parser/GlobalsParser.d.ts +1 -0
- package/dist/model/parser/ModelParser.d.ts +2 -1
- package/dist/model/parser/ModelParser.js +45 -1
- package/dist/model/parser/SpecificationParser.js +1 -1
- package/dist/model/parser/ViewsParser.d.ts +1 -0
- package/dist/model/parser/ViewsParser.js +13 -0
- package/dist/model-change/ModelChanges.js +6 -3
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +11 -1
- package/dist/validation/relation.d.ts +1 -0
- package/dist/validation/relation.js +87 -1
- package/dist/validation/view-checks.d.ts +4 -0
- package/dist/validation/view-checks.js +46 -0
- package/dist/views/LikeC4ManualLayouts.js +2 -2
- package/dist/views/LikeC4Views.js +2 -2
- package/dist/workspace/IndexManager.js +10 -1
- package/dist/workspace/LangiumDocuments.js +19 -1
- package/dist/workspace/ProjectsManager.d.ts +26 -1
- package/dist/workspace/ProjectsManager.js +98 -12
- package/dist/workspace/WorkspaceManager.js +38 -0
- package/lib/package.json +159 -0
- package/package.json +22 -22
package/dist/model/fqn-index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Copyright (c) 2023-2025 Denis Davydkov
|
|
4
|
+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
5
|
+
//
|
|
6
|
+
// Portions of this file have been modified by NVIDIA CORPORATION & AFFILIATES.
|
|
1
7
|
import { invariant, nonNullable } from '@likec4/core';
|
|
2
8
|
import { Fqn } from '@likec4/core/types';
|
|
3
9
|
import { ancestorsFqn, compareNatural, DefaultWeakMap, MultiMap, sortNaturalByFqn } from '@likec4/core/utils';
|
|
@@ -160,7 +166,7 @@ export class FqnIndex extends ADisposable {
|
|
|
160
166
|
let _nested = [];
|
|
161
167
|
if (isDefined(el.body) && !isEmpty(el.body.elements)) {
|
|
162
168
|
for (const child of el.body.elements) {
|
|
163
|
-
if (!ast.isRelation(child)) {
|
|
169
|
+
if (!ast.isRelation(child) && !ast.isExtendRelation(child)) {
|
|
164
170
|
try {
|
|
165
171
|
_nested.push(...traverseNode(child, thisFqn));
|
|
166
172
|
}
|
|
@@ -189,7 +195,7 @@ export class FqnIndex extends ADisposable {
|
|
|
189
195
|
};
|
|
190
196
|
for (const node of rootElements) {
|
|
191
197
|
try {
|
|
192
|
-
if (ast.isRelation(node)) {
|
|
198
|
+
if (ast.isRelation(node) || ast.isExtendRelation(node)) {
|
|
193
199
|
continue;
|
|
194
200
|
}
|
|
195
201
|
traverseNode(node, null);
|
|
@@ -19,6 +19,7 @@ declare const DocumentParserFromMixins: {
|
|
|
19
19
|
parseViewRuleGlobalPredicateRef(astRule: import("../generated/ast").ViewRuleGlobalPredicateRef | import("../generated/ast").DynamicViewGlobalPredicateRef): import("@likec4/core").ViewRuleGlobalPredicateRef;
|
|
20
20
|
parseViewRuleStyleOrGlobalRef(astRule: import("../generated/ast").ViewRuleStyleOrGlobalRef): import("@likec4/core").ViewRuleGlobalStyle | import("@likec4/core").ElementViewRuleStyle<import("@likec4/core").Any>;
|
|
21
21
|
parseViewRuleGroup(astNode: import("../generated/ast").ViewRuleGroup): import("@likec4/core").ElementViewRuleGroup;
|
|
22
|
+
parseViewRuleRank(astRule: import("../generated/ast").ViewRuleRank): import("@likec4/core").ElementViewRuleRank;
|
|
22
23
|
parseViewRuleStyle(astRule: import("../generated/ast").ViewRuleStyle | import("../generated/ast").GlobalStyle): import("@likec4/core").ElementViewRuleStyle;
|
|
23
24
|
parseViewRuleGlobalStyle(astRule: import("../generated/ast").ViewRuleGlobalStyle): import("@likec4/core").ViewRuleGlobalStyle;
|
|
24
25
|
parseDynamicElementView(astNode: import("../generated/ast").DynamicView, additionalStyles: (import("@likec4/core").ViewRuleGlobalStyle | import("@likec4/core").ElementViewRuleStyle<import("@likec4/core").Any>)[]): import("../ast").ParsedAstDynamicView;
|
|
@@ -118,6 +119,7 @@ declare const DocumentParserFromMixins: {
|
|
|
118
119
|
parseViewRuleGlobalPredicateRef(astRule: import("../generated/ast").ViewRuleGlobalPredicateRef | import("../generated/ast").DynamicViewGlobalPredicateRef): import("@likec4/core").ViewRuleGlobalPredicateRef;
|
|
119
120
|
parseViewRuleStyleOrGlobalRef(astRule: import("../generated/ast").ViewRuleStyleOrGlobalRef): import("@likec4/core").ViewRuleGlobalStyle | import("@likec4/core").ElementViewRuleStyle<import("@likec4/core").Any>;
|
|
120
121
|
parseViewRuleGroup(astNode: import("../generated/ast").ViewRuleGroup): import("@likec4/core").ElementViewRuleGroup;
|
|
122
|
+
parseViewRuleRank(astRule: import("../generated/ast").ViewRuleRank): import("@likec4/core").ElementViewRuleRank;
|
|
121
123
|
parseViewRuleStyle(astRule: import("../generated/ast").ViewRuleStyle | import("../generated/ast").GlobalStyle): import("@likec4/core").ElementViewRuleStyle;
|
|
122
124
|
parseViewRuleGlobalStyle(astRule: import("../generated/ast").ViewRuleGlobalStyle): import("@likec4/core").ViewRuleGlobalStyle;
|
|
123
125
|
parseDynamicElementView(astNode: import("../generated/ast").DynamicView, additionalStyles: (import("@likec4/core").ViewRuleGlobalStyle | import("@likec4/core").ElementViewRuleStyle<import("@likec4/core").Any>)[]): import("../ast").ParsedAstDynamicView;
|
|
@@ -466,6 +468,7 @@ declare const DocumentParserFromMixins: {
|
|
|
466
468
|
parseModel(): void;
|
|
467
469
|
parseElement(astNode: import("../generated/ast").Element): import("../ast").ParsedAstElement;
|
|
468
470
|
parseExtendElement(astNode: import("../generated/ast").ExtendElement): import("../ast").ParsedAstExtend | null;
|
|
471
|
+
parseExtendRelation(astNode: import("../generated/ast").ExtendRelation): import("../ast").ParsedAstExtendRelation | null;
|
|
469
472
|
_resolveRelationSource(node: import("../generated/ast").Relation): import("@likec4/core").FqnRef.ModelRef | import("@likec4/core").FqnRef.ImportRef;
|
|
470
473
|
parseRelation(astNode: import("../generated/ast").Relation): import("../ast").ParsedAstRelation;
|
|
471
474
|
parseFqnRef(astNode: import("../generated/ast").FqnRef): import("@likec4/core").FqnRef;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Copyright (c) 2023-2025 Denis Davydkov
|
|
4
|
+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
5
|
+
//
|
|
6
|
+
// Portions of this file have been modified by NVIDIA CORPORATION & AFFILIATES.
|
|
1
7
|
import { DefaultWeakMap, invariant, MultiMap } from '@likec4/core/utils';
|
|
2
8
|
import { DocumentState, UriUtils } from 'langium';
|
|
3
9
|
import { pipe } from 'remeda';
|
|
@@ -97,6 +103,7 @@ export class LikeC4ModelParser {
|
|
|
97
103
|
c4Elements: [],
|
|
98
104
|
c4ExtendElements: [],
|
|
99
105
|
c4ExtendDeployments: [],
|
|
106
|
+
c4ExtendRelations: [],
|
|
100
107
|
c4Relations: [],
|
|
101
108
|
c4Deployments: [],
|
|
102
109
|
c4DeploymentRelations: [],
|
|
@@ -264,9 +264,14 @@ export class BaseParser {
|
|
|
264
264
|
if (isArray(elementProps)) {
|
|
265
265
|
const style = this.parseStyleProps(elementProps.find(ast.isElementStyleProperty)?.props);
|
|
266
266
|
// Property on element has higher priority than from style
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
try {
|
|
268
|
+
const iconProp = this.parseIconProperty(elementProps.find(ast.isIconProperty));
|
|
269
|
+
if (iconProp) {
|
|
270
|
+
style.icon = iconProp;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
logger.warn('Failed to parse icon property on element', { err });
|
|
270
275
|
}
|
|
271
276
|
return style;
|
|
272
277
|
}
|
|
@@ -15,6 +15,7 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
|
|
|
15
15
|
parseViewRuleGlobalPredicateRef(astRule: ast.ViewRuleGlobalPredicateRef | ast.DynamicViewGlobalPredicateRef): c4.ViewRuleGlobalPredicateRef;
|
|
16
16
|
parseViewRuleStyleOrGlobalRef(astRule: ast.ViewRuleStyleOrGlobalRef): c4.ViewRuleGlobalStyle | c4.ElementViewRuleStyle<c4.aux.Any>;
|
|
17
17
|
parseViewRuleGroup(astNode: ast.ViewRuleGroup): c4.ElementViewRuleGroup;
|
|
18
|
+
parseViewRuleRank(astRule: ast.ViewRuleRank): c4.ElementViewRuleRank;
|
|
18
19
|
parseViewRuleStyle(astRule: ast.ViewRuleStyle | ast.GlobalStyle): c4.ElementViewRuleStyle;
|
|
19
20
|
parseViewRuleGlobalStyle(astRule: ast.ViewRuleGlobalStyle): c4.ViewRuleGlobalStyle;
|
|
20
21
|
parseDynamicElementView(astNode: ast.DynamicView, additionalStyles: (c4.ViewRuleGlobalStyle | c4.ElementViewRuleStyle<c4.aux.Any>)[]): import("../../ast").ParsedAstDynamicView;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as c4 from '@likec4/core';
|
|
2
2
|
import { FqnRef } from '@likec4/core/types';
|
|
3
|
-
import { type ParsedAstElement, type ParsedAstExtend, type ParsedAstRelation, ast } from '../../ast';
|
|
3
|
+
import { type ParsedAstElement, type ParsedAstExtend, type ParsedAstExtendRelation, type ParsedAstRelation, ast } from '../../ast';
|
|
4
4
|
import type { WithExpressionV2 } from './FqnRefParser';
|
|
5
5
|
export type WithModel = ReturnType<typeof ModelParser>;
|
|
6
6
|
export declare function ModelParser<TBase extends WithExpressionV2>(B: TBase): {
|
|
@@ -8,6 +8,7 @@ export declare function ModelParser<TBase extends WithExpressionV2>(B: TBase): {
|
|
|
8
8
|
parseModel(): void;
|
|
9
9
|
parseElement(astNode: ast.Element): ParsedAstElement;
|
|
10
10
|
parseExtendElement(astNode: ast.ExtendElement): ParsedAstExtend | null;
|
|
11
|
+
parseExtendRelation(astNode: ast.ExtendRelation): ParsedAstExtendRelation | null;
|
|
11
12
|
_resolveRelationSource(node: ast.Relation): FqnRef.ModelRef | FqnRef.ImportRef;
|
|
12
13
|
parseRelation(astNode: ast.Relation): ParsedAstRelation;
|
|
13
14
|
parseFqnRef(astNode: ast.FqnRef): c4.FqnRef;
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Copyright (c) 2023-2025 Denis Davydkov
|
|
4
|
+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
5
|
+
//
|
|
6
|
+
// Portions of this file have been modified by NVIDIA CORPORATION & AFFILIATES.
|
|
1
7
|
import { invariant, isNonEmptyArray, LinkedList, nonexhaustive, nonNullable } from '@likec4/core';
|
|
2
8
|
import { exact, FqnRef } from '@likec4/core/types';
|
|
3
9
|
import { loggable } from '@likec4/log';
|
|
@@ -15,7 +21,12 @@ function* streamModel(doc) {
|
|
|
15
21
|
relations.push(el);
|
|
16
22
|
continue;
|
|
17
23
|
}
|
|
18
|
-
|
|
24
|
+
// Skip ExtendRelation as it doesn't have child elements
|
|
25
|
+
if (ast.isExtendRelation(el)) {
|
|
26
|
+
yield el;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (el.body && 'elements' in el.body && el.body.elements && el.body.elements.length > 0) {
|
|
19
30
|
for (const child of el.body.elements) {
|
|
20
31
|
traverseStack.push(child);
|
|
21
32
|
}
|
|
@@ -48,6 +59,13 @@ export function ModelParser(B) {
|
|
|
48
59
|
}
|
|
49
60
|
continue;
|
|
50
61
|
}
|
|
62
|
+
if (ast.isExtendRelation(el)) {
|
|
63
|
+
const parsed = this.parseExtendRelation(el);
|
|
64
|
+
if (parsed) {
|
|
65
|
+
doc.c4ExtendRelations.push(parsed);
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
51
69
|
nonexhaustive(el);
|
|
52
70
|
}
|
|
53
71
|
catch (e) {
|
|
@@ -107,6 +125,32 @@ export function ModelParser(B) {
|
|
|
107
125
|
links: isNonEmptyArray(links) ? links : null,
|
|
108
126
|
});
|
|
109
127
|
}
|
|
128
|
+
parseExtendRelation(astNode) {
|
|
129
|
+
const source = this.parseFqnRef(astNode.source);
|
|
130
|
+
const target = this.parseFqnRef(astNode.target);
|
|
131
|
+
invariant(FqnRef.isModelRef(source) || FqnRef.isImportRef(source), 'Source must be a model reference');
|
|
132
|
+
invariant(FqnRef.isModelRef(target) || FqnRef.isImportRef(target), 'Target must be a model reference');
|
|
133
|
+
const tags = this.parseTags(astNode.body);
|
|
134
|
+
const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty));
|
|
135
|
+
const astPath = this.getAstNodePath(astNode);
|
|
136
|
+
const links = this.parseLinks(astNode.body) ?? [];
|
|
137
|
+
if (!tags && isEmpty(metadata ?? {}) && isEmpty(links)) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
// Generate a stable relation ID based on source, target, kind, and title
|
|
141
|
+
// This allows extends to match specific relations between elements
|
|
142
|
+
const kind = (astNode.kind ?? astNode.dotKind?.kind)?.ref?.name;
|
|
143
|
+
// Normalize title the same way as parseRelation does
|
|
144
|
+
const { title = '' } = this.parseBaseProps({}, { title: astNode.title });
|
|
145
|
+
const id = stringHash('extend-relation', FqnRef.flatten(source), FqnRef.flatten(target), kind ?? 'default', title);
|
|
146
|
+
return exact({
|
|
147
|
+
id,
|
|
148
|
+
astPath,
|
|
149
|
+
metadata,
|
|
150
|
+
tags,
|
|
151
|
+
links: isNonEmptyArray(links) ? links : null,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
110
154
|
_resolveRelationSource(node) {
|
|
111
155
|
if (isDefined(node.source)) {
|
|
112
156
|
const source = this.parseFqnRef(node.source);
|
|
@@ -79,7 +79,7 @@ export function SpecificationParser(B) {
|
|
|
79
79
|
continue;
|
|
80
80
|
}
|
|
81
81
|
c4Specification.colors[colorName] = {
|
|
82
|
-
color: nonNullable(this.parseColorLiteral(color), `Color "${colorName}" is not valid
|
|
82
|
+
color: nonNullable(this.parseColorLiteral(color), `Color "${colorName}" is not valid`),
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
catch (e) {
|
|
@@ -14,6 +14,7 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
|
|
|
14
14
|
parseViewRuleGlobalPredicateRef(astRule: ast.ViewRuleGlobalPredicateRef | ast.DynamicViewGlobalPredicateRef): c4.ViewRuleGlobalPredicateRef;
|
|
15
15
|
parseViewRuleStyleOrGlobalRef(astRule: ast.ViewRuleStyleOrGlobalRef): ViewRuleStyleOrGlobalRef;
|
|
16
16
|
parseViewRuleGroup(astNode: ast.ViewRuleGroup): c4.ElementViewRuleGroup;
|
|
17
|
+
parseViewRuleRank(astRule: ast.ViewRuleRank): c4.ElementViewRuleRank;
|
|
17
18
|
parseViewRuleStyle(astRule: ast.ViewRuleStyle | ast.GlobalStyle): c4.ElementViewRuleStyle;
|
|
18
19
|
parseViewRuleGlobalStyle(astRule: ast.ViewRuleGlobalStyle): c4.ViewRuleGlobalStyle;
|
|
19
20
|
parseDynamicElementView(astNode: ast.DynamicView, additionalStyles: ViewRuleStyleOrGlobalRef[]): ParsedAstDynamicView;
|
|
@@ -9,6 +9,7 @@ import { elementRef } from '../../utils/elementRef';
|
|
|
9
9
|
import { parseViewManualLayout } from '../../view-utils/manual-layout';
|
|
10
10
|
import { removeIndent, toSingleLine } from './Base';
|
|
11
11
|
const logger = mainLogger.getChild('ViewsParser');
|
|
12
|
+
const rankLogger = logger.getChild('rank');
|
|
12
13
|
export function ViewsParser(B) {
|
|
13
14
|
return class ViewsParser extends B {
|
|
14
15
|
parseViews() {
|
|
@@ -128,6 +129,9 @@ export function ViewsParser(B) {
|
|
|
128
129
|
if (ast.isViewRuleGroup(astRule)) {
|
|
129
130
|
return this.parseViewRuleGroup(astRule);
|
|
130
131
|
}
|
|
132
|
+
if (ast.isViewRuleRank(astRule)) {
|
|
133
|
+
return this.parseViewRuleRank(astRule);
|
|
134
|
+
}
|
|
131
135
|
nonexhaustive(astRule);
|
|
132
136
|
}
|
|
133
137
|
parseViewRulePredicate(astNode) {
|
|
@@ -192,6 +196,15 @@ export function ViewsParser(B) {
|
|
|
192
196
|
...this.parseStyleProps(astNode.props),
|
|
193
197
|
};
|
|
194
198
|
}
|
|
199
|
+
parseViewRuleRank(astRule) {
|
|
200
|
+
const targets = this.parseFqnExpressions(astRule.targets).filter((e) => c4.ModelExpression.isFqnExpr(e));
|
|
201
|
+
const rank = astRule.value ?? 'same';
|
|
202
|
+
rankLogger.debug `Parsed rank constraint ${rank} with ${targets.length} target(s)`;
|
|
203
|
+
return {
|
|
204
|
+
rank,
|
|
205
|
+
targets,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
195
208
|
parseViewRuleStyle(astRule) {
|
|
196
209
|
const targets = this.parseFqnExpressions(astRule.targets).filter((e) => c4.ModelExpression.isFqnExpr(e));
|
|
197
210
|
const style = this.parseStyleProps(astRule.props.filter(ast.isStyleProperty));
|
|
@@ -15,7 +15,6 @@ export class LikeC4ModelChanges {
|
|
|
15
15
|
}
|
|
16
16
|
async applyChange(changeView) {
|
|
17
17
|
const lspConnection = this.services.shared.lsp.Connection;
|
|
18
|
-
invariant(lspConnection, 'LSP Connection not available');
|
|
19
18
|
let result = null;
|
|
20
19
|
try {
|
|
21
20
|
await this.services.shared.workspace.WorkspaceLock.write(async () => {
|
|
@@ -32,7 +31,7 @@ export class LikeC4ModelChanges {
|
|
|
32
31
|
};
|
|
33
32
|
// TODO refactor to use separate methods for save/reset operations
|
|
34
33
|
if (change.op === 'save-view-snapshot') {
|
|
35
|
-
invariant(viewId === change.layout.id, 'View ID does not match');
|
|
34
|
+
invariant(viewId === change.layout.id, 'View ID does not match, expected ' + viewId + ', got ' + change.layout.id);
|
|
36
35
|
// If there is an existing manual layout v1
|
|
37
36
|
if (lookup.view.manualLayout) {
|
|
38
37
|
// We clean it up
|
|
@@ -62,6 +61,7 @@ export class LikeC4ModelChanges {
|
|
|
62
61
|
};
|
|
63
62
|
return;
|
|
64
63
|
}
|
|
64
|
+
invariant(lspConnection, 'This change only supported in IDE (running as Extension)');
|
|
65
65
|
const { edits, modifiedRange } = this.convertToTextEdit({
|
|
66
66
|
lookup,
|
|
67
67
|
change,
|
|
@@ -91,13 +91,16 @@ export class LikeC4ModelChanges {
|
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
93
|
catch (err) {
|
|
94
|
-
const error = loggable(wrapError(err, `Failed to apply change ${changeView.change.op} ${changeView.viewId}
|
|
94
|
+
const error = loggable(wrapError(err, `Failed to apply change ${changeView.change.op} ${changeView.viewId}`));
|
|
95
95
|
logger.error(error);
|
|
96
96
|
result = {
|
|
97
97
|
success: false,
|
|
98
98
|
error,
|
|
99
99
|
};
|
|
100
100
|
}
|
|
101
|
+
finally {
|
|
102
|
+
this.services.likec4.ModelBuilder.clearCache();
|
|
103
|
+
}
|
|
101
104
|
return result ?? {
|
|
102
105
|
success: false,
|
|
103
106
|
error: 'Unknown error applying model change',
|
|
@@ -4,7 +4,7 @@ import type { LikeC4Services } from '../module';
|
|
|
4
4
|
export { LikeC4DocumentValidator } from './DocumentValidator';
|
|
5
5
|
type Guard<N extends AstNode> = (n: AstNode) => n is N;
|
|
6
6
|
type Guarded<G> = G extends Guard<infer N> ? N : never;
|
|
7
|
-
declare const isValidatableAstNode: (n: AstNode) => n is ast.DynamicViewDisplayVariantProperty | ast.LinkProperty | ast.ViewStringProperty | ast.ElementStringProperty | ast.ElementStyleProperty | ast.IconProperty | ast.MetadataBody | ast.RelationStringProperty | ast.MetadataAttribute | ast.NotationProperty | ast.NotesProperty | ast.SpecificationElementStringProperty | ast.SpecificationRelationshipStringProperty | ast.HexColor | ast.RGBAColor | ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentViewRulePredicate | ast.DeploymentViewRuleStyle | ast.ViewRuleAutoLayout | ast.DynamicViewGlobalPredicateRef | ast.DynamicViewIncludePredicate | ast.ViewRuleGlobalStyle | ast.ViewRuleStyle | ast.DynamicStepChain | ast.DynamicStepSingle | ast.ElementKindExpression | ast.ElementTagExpression | ast.FqnRefExpr | ast.WildcardExpression | ast.FqnExprWhere | ast.FqnExprWith | ast.DirectedRelationExpr | ast.InOutRelationExpr | ast.IncomingRelationExpr | ast.OutgoingRelationExpr | ast.RelationExprWhere | ast.RelationExprWith | ast.Element | ast.ExtendDeployment | ast.ExtendElement | ast.Imported | ast.DeploymentView | ast.DynamicView | ast.ElementView | ast.ArrowProperty | ast.ColorProperty | ast.LineProperty | ast.PaddingSizeProperty | ast.ShapeSizeProperty | ast.TextSizeProperty | ast.BorderProperty | ast.MultipleProperty | ast.OpacityProperty | ast.ShapeProperty | ast.ViewRuleGlobalPredicateRef | ast.ViewRuleGroup | ast.ViewRulePredicate | ast.DynamicViewParallelSteps | ast.SpecificationRelationshipKind | ast.SpecificationRule | ast.ElementRef | ast.DeploymentRelation | ast.Relation | ast.GlobalDynamicPredicateGroup | ast.Globals | ast.GlobalPredicateGroup | ast.GlobalStyle | ast.SpecificationDeploymentNodeKind | ast.SpecificationElementKind | ast.GlobalStyleGroup | ast.SpecificationColor | ast.NavigateToProperty | ast.Tags | ast.ImportsFromPoject | ast.SpecificationTag;
|
|
7
|
+
declare const isValidatableAstNode: (n: AstNode) => n is ast.DynamicViewDisplayVariantProperty | ast.LinkProperty | ast.ViewStringProperty | ast.ElementStringProperty | ast.ElementStyleProperty | ast.IconProperty | ast.MetadataBody | ast.RelationStringProperty | ast.MetadataAttribute | ast.NotationProperty | ast.NotesProperty | ast.SpecificationElementStringProperty | ast.SpecificationRelationshipStringProperty | ast.HexColor | ast.RGBAColor | ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentViewRulePredicate | ast.DeploymentViewRuleStyle | ast.ViewRuleAutoLayout | ast.DynamicViewGlobalPredicateRef | ast.DynamicViewIncludePredicate | ast.ViewRuleGlobalStyle | ast.ViewRuleStyle | ast.DynamicStepChain | ast.DynamicStepSingle | ast.ElementKindExpression | ast.ElementTagExpression | ast.FqnRefExpr | ast.WildcardExpression | ast.FqnExprWhere | ast.FqnExprWith | ast.DirectedRelationExpr | ast.InOutRelationExpr | ast.IncomingRelationExpr | ast.OutgoingRelationExpr | ast.RelationExprWhere | ast.RelationExprWith | ast.Element | ast.ExtendDeployment | ast.ExtendElement | ast.Imported | ast.DeploymentView | ast.DynamicView | ast.ElementView | ast.ArrowProperty | ast.ColorProperty | ast.LineProperty | ast.PaddingSizeProperty | ast.ShapeSizeProperty | ast.TextSizeProperty | ast.BorderProperty | ast.MultipleProperty | ast.OpacityProperty | ast.ShapeProperty | ast.ViewRuleGlobalPredicateRef | ast.ViewRuleGroup | ast.ViewRulePredicate | ast.ViewRuleRank | ast.DynamicViewParallelSteps | ast.SpecificationRelationshipKind | ast.SpecificationRule | ast.ElementRef | ast.DeploymentRelation | ast.Relation | ast.GlobalDynamicPredicateGroup | ast.Globals | ast.GlobalPredicateGroup | ast.GlobalStyle | ast.SpecificationDeploymentNodeKind | ast.SpecificationElementKind | ast.GlobalStyleGroup | ast.SpecificationColor | ast.NavigateToProperty | ast.Tags | ast.ExtendRelation | ast.ImportsFromPoject | ast.SpecificationTag;
|
|
8
8
|
type ValidatableAstNode = Guarded<typeof isValidatableAstNode>;
|
|
9
9
|
export declare function checksFromDiagnostics(doc: LikeC4LangiumDocument): {
|
|
10
10
|
isValid: (n: ValidatableAstNode) => boolean;
|
package/dist/validation/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Copyright (c) 2023-2025 Denis Davydkov
|
|
4
|
+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
5
|
+
//
|
|
6
|
+
// Portions of this file have been modified by NVIDIA CORPORATION & AFFILIATES.
|
|
1
7
|
import { onNextTick } from '@likec4/core/utils';
|
|
2
8
|
import { loggable } from '@likec4/log';
|
|
3
9
|
import { DocumentState } from 'langium';
|
|
@@ -11,9 +17,10 @@ import { checkElement } from './element';
|
|
|
11
17
|
import { checkElementRef } from './element-ref';
|
|
12
18
|
import { checkImportsFromPoject } from './imports';
|
|
13
19
|
import { colorLiteralRuleChecks, iconPropertyRuleChecks, notesPropertyRuleChecks, opacityPropertyRuleChecks, } from './property-checks';
|
|
14
|
-
import { checkRelationBody, relationChecks } from './relation';
|
|
20
|
+
import { checkRelationBody, extendRelationChecks, relationChecks } from './relation';
|
|
15
21
|
import { checkDeploymentNodeKind, checkElementKind, checkGlobalPredicate, checkGlobals, checkGlobalStyleId, checkModel, checkRelationshipKind, checkSpecificationRule, checkTag, } from './specification';
|
|
16
22
|
import { viewChecks } from './view';
|
|
23
|
+
import { viewRuleRankChecks } from './view-checks';
|
|
17
24
|
import { checkFqnExprWith, checkFqnRefExpr, checkIncomingRelationExpr, checkOutgoingRelationExpr, checkRelationExpr, checkRelationExprWith, } from './view-predicates';
|
|
18
25
|
export { LikeC4DocumentValidator } from './DocumentValidator';
|
|
19
26
|
function validatableAstNodeGuards(predicates) {
|
|
@@ -60,6 +67,7 @@ const isValidatableAstNode = validatableAstNodeGuards([
|
|
|
60
67
|
ast.isElementRef,
|
|
61
68
|
ast.isExtendElement,
|
|
62
69
|
ast.isExtendDeployment,
|
|
70
|
+
ast.isExtendRelation,
|
|
63
71
|
ast.isSpecificationElementKind,
|
|
64
72
|
ast.isSpecificationRelationshipKind,
|
|
65
73
|
ast.isSpecificationDeploymentNodeKind,
|
|
@@ -108,6 +116,7 @@ export function registerValidationChecks(services) {
|
|
|
108
116
|
DeploymentNode: deploymentNodeChecks(services),
|
|
109
117
|
DeploymentRelation: deploymentRelationChecks(services),
|
|
110
118
|
ExtendDeployment: extendDeploymentChecks(services),
|
|
119
|
+
ExtendRelation: extendRelationChecks(services),
|
|
111
120
|
FqnRefExpr: checkFqnRefExpr(services),
|
|
112
121
|
RelationExpr: checkRelationExpr(services),
|
|
113
122
|
NotesProperty: notesPropertyRuleChecks(services),
|
|
@@ -137,6 +146,7 @@ export function registerValidationChecks(services) {
|
|
|
137
146
|
// Imported: checkImported(services),
|
|
138
147
|
ColorLiteral: colorLiteralRuleChecks(services),
|
|
139
148
|
DynamicViewDisplayVariantProperty: dynamicViewDisplayVariant(services),
|
|
149
|
+
ViewRuleRank: viewRuleRankChecks(services),
|
|
140
150
|
});
|
|
141
151
|
const connection = services.shared.lsp.Connection;
|
|
142
152
|
if (connection) {
|
|
@@ -3,3 +3,4 @@ import { ast } from '../ast';
|
|
|
3
3
|
import type { LikeC4Services } from '../module';
|
|
4
4
|
export declare const relationChecks: (services: LikeC4Services) => ValidationCheck<ast.Relation>;
|
|
5
5
|
export declare const checkRelationBody: (_services: LikeC4Services) => ValidationCheck<ast.RelationBody>;
|
|
6
|
+
export declare const extendRelationChecks: (services: LikeC4Services) => ValidationCheck<ast.ExtendRelation>;
|
|
@@ -1,8 +1,29 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Copyright (c) 2023-2025 Denis Davydkov
|
|
4
|
+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
5
|
+
//
|
|
6
|
+
// Portions of this file have been modified by NVIDIA CORPORATION & AFFILIATES.
|
|
1
7
|
import { FqnRef, isSameHierarchy } from '@likec4/core';
|
|
2
8
|
import { AstUtils } from 'langium';
|
|
3
|
-
import { ast } from '../ast';
|
|
9
|
+
import { ast, isParsedLikeC4LangiumDocument } from '../ast';
|
|
4
10
|
import { safeCall } from '../utils';
|
|
11
|
+
import { stringHash } from '../utils/stringHash';
|
|
5
12
|
import { tryOrLog } from './_shared';
|
|
13
|
+
// Cache of relation match keys to avoid recomputing for every extend validation
|
|
14
|
+
let cachedRelationKeys = null;
|
|
15
|
+
let cachedDocsFingerprint = null;
|
|
16
|
+
const computeDocsFingerprint = (docs) => {
|
|
17
|
+
return docs
|
|
18
|
+
.map(doc => {
|
|
19
|
+
const relationHashes = (doc.c4Relations ?? [])
|
|
20
|
+
.map(rel => stringHash('extend-relation', FqnRef.flatten(rel.source), FqnRef.flatten(rel.target), rel.kind ?? 'default', rel.title ?? ''))
|
|
21
|
+
.sort()
|
|
22
|
+
.join(',');
|
|
23
|
+
return `${doc.uri.toString()}:${relationHashes}`;
|
|
24
|
+
})
|
|
25
|
+
.join('|');
|
|
26
|
+
};
|
|
6
27
|
export const relationChecks = (services) => {
|
|
7
28
|
const modelParser = services.likec4.ModelParser;
|
|
8
29
|
return tryOrLog((el, accept) => {
|
|
@@ -53,3 +74,68 @@ export const checkRelationBody = (_services) => {
|
|
|
53
74
|
}
|
|
54
75
|
});
|
|
55
76
|
};
|
|
77
|
+
export const extendRelationChecks = (services) => {
|
|
78
|
+
const modelParser = services.likec4.ModelParser;
|
|
79
|
+
return tryOrLog((el, accept) => {
|
|
80
|
+
const parser = modelParser.forDocument(AstUtils.getDocument(el));
|
|
81
|
+
const source = safeCall(() => parser.parseFqnRef(el.source));
|
|
82
|
+
if (!source) {
|
|
83
|
+
accept('error', 'Source not resolved', {
|
|
84
|
+
node: el,
|
|
85
|
+
property: 'source',
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const target = safeCall(() => parser.parseFqnRef(el.target));
|
|
90
|
+
if (!target) {
|
|
91
|
+
accept('error', 'Target not resolved', {
|
|
92
|
+
node: el,
|
|
93
|
+
property: 'target',
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!FqnRef.isModelRef(source) && !FqnRef.isImportRef(source)) {
|
|
98
|
+
accept('error', 'Source must reference a model element', {
|
|
99
|
+
node: el,
|
|
100
|
+
property: 'source',
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (!FqnRef.isModelRef(target) && !FqnRef.isImportRef(target)) {
|
|
105
|
+
accept('error', 'Target must reference a model element', {
|
|
106
|
+
node: el,
|
|
107
|
+
property: 'target',
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Warn if this extend does not match any relation in the workspace
|
|
112
|
+
// Build a match key identical to buildModel.ts
|
|
113
|
+
const kind = (el.kind ?? el.dotKind?.kind)?.ref?.name ?? 'default';
|
|
114
|
+
// Normalize title using the same parser helper
|
|
115
|
+
const { title = '' } = parser.parseBaseProps({}, { title: el.title });
|
|
116
|
+
const extendKey = stringHash('extend-relation', FqnRef.flatten(source), FqnRef.flatten(target), kind, title);
|
|
117
|
+
// Build (or reuse) a Set of all relation match keys across the workspace.
|
|
118
|
+
// This avoids O(E x D x R) scans on large workspaces.
|
|
119
|
+
const docs = services.shared.workspace.LangiumDocuments.all
|
|
120
|
+
.toArray()
|
|
121
|
+
.filter(isParsedLikeC4LangiumDocument);
|
|
122
|
+
const fingerprint = computeDocsFingerprint(docs);
|
|
123
|
+
if (fingerprint !== cachedDocsFingerprint) {
|
|
124
|
+
const keys = new Set();
|
|
125
|
+
for (const d of docs) {
|
|
126
|
+
for (const rel of d.c4Relations ?? []) {
|
|
127
|
+
const key = stringHash('extend-relation', FqnRef.flatten(rel.source), FqnRef.flatten(rel.target), rel.kind ?? 'default', rel.title ?? '');
|
|
128
|
+
keys.add(key);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
cachedRelationKeys = keys;
|
|
132
|
+
cachedDocsFingerprint = fingerprint;
|
|
133
|
+
}
|
|
134
|
+
const hasMatch = cachedRelationKeys?.has(extendKey) ?? false;
|
|
135
|
+
if (!hasMatch) {
|
|
136
|
+
accept('warning', 'This extend does not match any relation (by source, kind, target, title)', {
|
|
137
|
+
node: el,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ast } from '../ast';
|
|
2
|
+
import { tryOrLog } from './_shared';
|
|
3
|
+
// Helper to collect FqnExpr values from FqnExpressions linked list
|
|
4
|
+
function collectFqnExprs(exprs) {
|
|
5
|
+
const result = [];
|
|
6
|
+
let iter = exprs;
|
|
7
|
+
while (iter) {
|
|
8
|
+
if (iter.value) {
|
|
9
|
+
result.push(iter.value);
|
|
10
|
+
}
|
|
11
|
+
iter = iter.prev;
|
|
12
|
+
}
|
|
13
|
+
return result.reverse();
|
|
14
|
+
}
|
|
15
|
+
export const viewRuleRankChecks = (_services) => {
|
|
16
|
+
return tryOrLog((el, accept) => {
|
|
17
|
+
const targetExprs = collectFqnExprs(el.targets);
|
|
18
|
+
if (targetExprs.length < 2 && el.value === 'same') {
|
|
19
|
+
accept('warning', 'Rank rule should have at least 2 targets', {
|
|
20
|
+
node: el,
|
|
21
|
+
property: 'targets',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
// Filter to only FqnRefExpr for parent comparison
|
|
25
|
+
const fqnRefExprs = targetExprs.filter(ast.isFqnRefExpr);
|
|
26
|
+
const firstParent = fqnRefExprs[0]?.ref?.parent;
|
|
27
|
+
for (let i = 1; i < fqnRefExprs.length; i++) {
|
|
28
|
+
const target = fqnRefExprs[i];
|
|
29
|
+
if (el.value === 'same' && !areSame(firstParent, target?.ref?.parent)) {
|
|
30
|
+
accept('error', 'All targets must have the same parent rank same', {
|
|
31
|
+
node: el,
|
|
32
|
+
property: 'targets',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
function areSame(a, b) {
|
|
39
|
+
if (!a && !b)
|
|
40
|
+
return true;
|
|
41
|
+
if (!a || !b)
|
|
42
|
+
return false;
|
|
43
|
+
if (a.value.ref !== b.value.ref)
|
|
44
|
+
return false;
|
|
45
|
+
return areSame(a.parent, b.parent);
|
|
46
|
+
}
|
|
@@ -75,9 +75,9 @@ export class DefaultLikeC4ManualLayouts {
|
|
|
75
75
|
const { manualLayout: _, ...rest } = layouted;
|
|
76
76
|
layouted = rest;
|
|
77
77
|
}
|
|
78
|
+
const content = JSON5.stringify(
|
|
78
79
|
// Normalize icon paths before writing
|
|
79
|
-
|
|
80
|
-
const content = JSON5.stringify(layouted, {
|
|
80
|
+
this.normalizeIconPathsForWrite(layouted, project.folderUri), {
|
|
81
81
|
space: 2,
|
|
82
82
|
quote: '\'',
|
|
83
83
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { _layout, applyManualLayout, calcDriftsFromSnapshot
|
|
1
|
+
import { _layout, applyManualLayout, calcDriftsFromSnapshot } from '@likec4/core';
|
|
2
2
|
import { GraphvizLayouter } from '@likec4/layouts';
|
|
3
3
|
import { loggable } from '@likec4/log';
|
|
4
4
|
import { interruptAndCheck } from 'langium';
|
|
5
|
-
import { isTruthy,
|
|
5
|
+
import { isTruthy, values } from 'remeda';
|
|
6
6
|
import { logger as rootLogger, logWarnError } from '../logger';
|
|
7
7
|
import { performanceMark } from '../utils';
|
|
8
8
|
const viewsLogger = rootLogger.getChild('views');
|
|
@@ -13,9 +13,18 @@ export class IndexManager extends DefaultIndexManager {
|
|
|
13
13
|
}
|
|
14
14
|
projectElements(projectId, nodeType, uris) {
|
|
15
15
|
const projects = this.services.workspace.ProjectsManager;
|
|
16
|
+
const project = projects.getProject(projectId);
|
|
17
|
+
const includePathStrings = project.includePaths?.map(uri => {
|
|
18
|
+
const path = uri.toString();
|
|
19
|
+
return path.endsWith('/') ? path : path + '/';
|
|
20
|
+
}) ?? [];
|
|
16
21
|
let documentUris = stream(this.symbolIndex.keys());
|
|
17
22
|
return documentUris
|
|
18
|
-
.filter(uri =>
|
|
23
|
+
.filter(uri => {
|
|
24
|
+
const belongsToProject = projects.belongsTo(uri) === projectId;
|
|
25
|
+
const inIncludePath = includePathStrings.some(includePath => uri.startsWith(includePath));
|
|
26
|
+
return (belongsToProject || inIncludePath) && (!uris || uris.has(uri));
|
|
27
|
+
})
|
|
19
28
|
.flatMap(uri => this.getFileDescriptions(uri, nodeType));
|
|
20
29
|
}
|
|
21
30
|
}
|
|
@@ -64,7 +64,25 @@ export class LangiumDocuments extends DefaultLangiumDocuments {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
projectDocuments(projectId) {
|
|
67
|
-
|
|
67
|
+
const projects = this.services.workspace.ProjectsManager;
|
|
68
|
+
const project = projects.getProject(projectId);
|
|
69
|
+
const projectFolder = project.folderUri.toString() + (project.folderUri.path.endsWith('/') ? '' : '/');
|
|
70
|
+
const includePathStrings = project.includePaths?.map(uri => {
|
|
71
|
+
const path = uri.toString();
|
|
72
|
+
return path.endsWith('/') ? path : path + '/';
|
|
73
|
+
}) ?? [];
|
|
74
|
+
return this.allExcludingBuiltin.filter(doc => {
|
|
75
|
+
const docUri = doc.uri.toString();
|
|
76
|
+
// Always include documents from the project's own folder
|
|
77
|
+
if (docUri.startsWith(projectFolder)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
// Check for addtional documents when the config has the `include:paths` property set.
|
|
81
|
+
if (includePathStrings.length > 0) {
|
|
82
|
+
return includePathStrings.some(includePath => docUri.startsWith(includePath));
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
});
|
|
68
86
|
}
|
|
69
87
|
groupedByProject() {
|
|
70
88
|
return groupBy(this.allExcludingBuiltin.toArray(), prop('likec4ProjectId'));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type LikeC4ProjectConfig, type LikeC4ProjectConfigInput } from '@likec4/config';
|
|
1
|
+
import { type IncludeConfig, type LikeC4ProjectConfig, type LikeC4ProjectConfigInput } from '@likec4/config';
|
|
2
2
|
import type { NonEmptyReadonlyArray } from '@likec4/core';
|
|
3
3
|
import type { ProjectId, scalar } from '@likec4/core/types';
|
|
4
4
|
import { type Cancellation, type LangiumDocument, URI, WorkspaceCache } from 'langium';
|
|
@@ -16,11 +16,27 @@ interface ProjectData {
|
|
|
16
16
|
folder: ProjectFolder;
|
|
17
17
|
folderUri: URI;
|
|
18
18
|
exclude?: (path: string) => boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Resolved include paths with both URI and folder string representations.
|
|
21
|
+
* These are additional directories that are part of this project.
|
|
22
|
+
*/
|
|
23
|
+
includePaths?: Array<{
|
|
24
|
+
uri: URI;
|
|
25
|
+
folder: ProjectFolder;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Normalized include configuration (paths, maxDepth, fileThreshold).
|
|
29
|
+
*/
|
|
30
|
+
includeConfig: IncludeConfig;
|
|
19
31
|
}
|
|
20
32
|
export interface Project {
|
|
21
33
|
id: scalar.ProjectId;
|
|
22
34
|
folderUri: URI;
|
|
23
35
|
config: LikeC4ProjectConfig;
|
|
36
|
+
/**
|
|
37
|
+
* Resolved include paths as URIs (if configured).
|
|
38
|
+
*/
|
|
39
|
+
includePaths?: URI[];
|
|
24
40
|
}
|
|
25
41
|
export declare class ProjectsManager {
|
|
26
42
|
#private;
|
|
@@ -93,6 +109,15 @@ export declare class ProjectsManager {
|
|
|
93
109
|
* Lazy-created due to initialization order of the LanguageServer
|
|
94
110
|
*/
|
|
95
111
|
protected get documentBelongsTo(): WorkspaceCache<LangiumDocument, ProjectData>;
|
|
112
|
+
/**
|
|
113
|
+
* Returns all include paths from all projects.
|
|
114
|
+
* Used by WorkspaceManager to scan additional directories for C4 files.
|
|
115
|
+
*/
|
|
116
|
+
getAllIncludePaths(): Array<{
|
|
117
|
+
projectId: scalar.ProjectId;
|
|
118
|
+
includePath: URI;
|
|
119
|
+
includeConfig: IncludeConfig;
|
|
120
|
+
}>;
|
|
96
121
|
private getWorkspaceFolder;
|
|
97
122
|
}
|
|
98
123
|
export {};
|