@domainlang/language 0.7.0 → 0.9.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.
- package/out/domain-lang-module.d.ts +2 -0
- package/out/domain-lang-module.js +21 -2
- package/out/domain-lang-module.js.map +1 -1
- package/out/lsp/domain-lang-completion.d.ts +142 -1
- package/out/lsp/domain-lang-completion.js +620 -22
- package/out/lsp/domain-lang-completion.js.map +1 -1
- package/out/lsp/domain-lang-document-symbol-provider.d.ts +79 -0
- package/out/lsp/domain-lang-document-symbol-provider.js +210 -0
- package/out/lsp/domain-lang-document-symbol-provider.js.map +1 -0
- package/out/lsp/domain-lang-index-manager.d.ts +34 -5
- package/out/lsp/domain-lang-index-manager.js +66 -27
- package/out/lsp/domain-lang-index-manager.js.map +1 -1
- package/out/lsp/domain-lang-node-kind-provider.d.ts +27 -0
- package/out/lsp/domain-lang-node-kind-provider.js +87 -0
- package/out/lsp/domain-lang-node-kind-provider.js.map +1 -0
- package/out/lsp/domain-lang-scope-provider.d.ts +53 -20
- package/out/lsp/domain-lang-scope-provider.js +119 -44
- package/out/lsp/domain-lang-scope-provider.js.map +1 -1
- package/out/lsp/domain-lang-workspace-manager.d.ts +23 -2
- package/out/lsp/domain-lang-workspace-manager.js +51 -6
- package/out/lsp/domain-lang-workspace-manager.js.map +1 -1
- package/out/lsp/hover/domain-lang-hover.d.ts +16 -6
- package/out/lsp/hover/domain-lang-hover.js +160 -134
- package/out/lsp/hover/domain-lang-hover.js.map +1 -1
- package/out/lsp/hover/hover-builders.d.ts +57 -0
- package/out/lsp/hover/hover-builders.js +171 -0
- package/out/lsp/hover/hover-builders.js.map +1 -0
- package/out/main.js +2 -1
- package/out/main.js.map +1 -1
- package/out/sdk/index.d.ts +31 -11
- package/out/sdk/index.js +30 -11
- package/out/sdk/index.js.map +1 -1
- package/out/sdk/loader-node.d.ts +2 -0
- package/out/sdk/loader-node.js +3 -1
- package/out/sdk/loader-node.js.map +1 -1
- package/out/sdk/loader.d.ts +55 -2
- package/out/sdk/loader.js +87 -28
- package/out/sdk/loader.js.map +1 -1
- package/out/sdk/query.js +14 -11
- package/out/sdk/query.js.map +1 -1
- package/out/sdk/validator.d.ts +134 -0
- package/out/sdk/validator.js +249 -0
- package/out/sdk/validator.js.map +1 -0
- package/out/services/package-boundary-detector.d.ts +101 -0
- package/out/services/package-boundary-detector.js +211 -0
- package/out/services/package-boundary-detector.js.map +1 -0
- package/out/services/performance-optimizer.js +6 -2
- package/out/services/performance-optimizer.js.map +1 -1
- package/out/services/types.d.ts +24 -0
- package/out/services/types.js.map +1 -1
- package/out/services/workspace-manager.d.ts +73 -6
- package/out/services/workspace-manager.js +210 -57
- package/out/services/workspace-manager.js.map +1 -1
- package/out/utils/import-utils.d.ts +9 -6
- package/out/utils/import-utils.js +26 -15
- package/out/utils/import-utils.js.map +1 -1
- package/out/validation/constants.d.ts +7 -0
- package/out/validation/constants.js +21 -3
- package/out/validation/constants.js.map +1 -1
- package/out/validation/import.d.ts +11 -1
- package/out/validation/import.js +42 -14
- package/out/validation/import.js.map +1 -1
- package/out/validation/maps.js +50 -1
- package/out/validation/maps.js.map +1 -1
- package/package.json +8 -9
- package/src/domain-lang-module.ts +24 -3
- package/src/lsp/domain-lang-completion.ts +736 -27
- package/src/lsp/domain-lang-document-symbol-provider.ts +254 -0
- package/src/lsp/domain-lang-index-manager.ts +79 -27
- package/src/lsp/domain-lang-node-kind-provider.ts +119 -0
- package/src/lsp/domain-lang-scope-provider.ts +171 -55
- package/src/lsp/domain-lang-workspace-manager.ts +64 -6
- package/src/lsp/hover/domain-lang-hover.ts +189 -131
- package/src/lsp/hover/hover-builders.ts +208 -0
- package/src/main.ts +3 -1
- package/src/sdk/index.ts +33 -11
- package/src/sdk/loader-node.ts +6 -1
- package/src/sdk/loader.ts +125 -34
- package/src/sdk/query.ts +15 -11
- package/src/sdk/validator.ts +358 -0
- package/src/services/package-boundary-detector.ts +238 -0
- package/src/services/performance-optimizer.ts +6 -2
- package/src/services/types.ts +25 -0
- package/src/services/workspace-manager.ts +259 -62
- package/src/utils/import-utils.ts +27 -15
- package/src/validation/constants.ts +23 -6
- package/src/validation/import.ts +49 -14
- package/src/validation/maps.ts +59 -2
|
@@ -6,21 +6,29 @@ import type {
|
|
|
6
6
|
MaybePromise,
|
|
7
7
|
Reference
|
|
8
8
|
} from 'langium';
|
|
9
|
-
import { CstUtils, isReference } from 'langium';
|
|
9
|
+
import { AstUtils, CstUtils, isReference } from 'langium';
|
|
10
10
|
import type { LangiumServices } from 'langium/lsp';
|
|
11
11
|
import { AstNodeHoverProvider } from 'langium/lsp';
|
|
12
12
|
import type { Hover, HoverParams } from 'vscode-languageserver';
|
|
13
13
|
import * as ast from '../../generated/ast.js';
|
|
14
14
|
import type { DomainLangServices } from '../../domain-lang-module.js';
|
|
15
15
|
import { QualifiedNameProvider } from '../domain-lang-naming.js';
|
|
16
|
+
import type { DomainLangIndexManager } from '../domain-lang-index-manager.js';
|
|
16
17
|
import { keywordExplanations } from './domain-lang-keywords.js';
|
|
17
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
buildDomainFields,
|
|
20
|
+
buildBcFields,
|
|
21
|
+
formatHoverContent,
|
|
22
|
+
} from './hover-builders.js';
|
|
18
23
|
|
|
19
24
|
/**
|
|
20
25
|
* Type-specific hover content generator.
|
|
21
26
|
* Returns undefined if the generator doesn't handle this node type.
|
|
27
|
+
* @param node - The AST node to generate hover content for
|
|
28
|
+
* @param commentBlock - Documentation comment block
|
|
29
|
+
* @param importAlias - Optional import alias if the node is from an imported document
|
|
22
30
|
*/
|
|
23
|
-
type HoverContentGenerator = (node: AstNode, commentBlock: string) => string | undefined;
|
|
31
|
+
type HoverContentGenerator = (node: AstNode, commentBlock: string, importAlias?: string) => string | undefined;
|
|
24
32
|
|
|
25
33
|
/**
|
|
26
34
|
* Provides hover information for DomainLang elements.
|
|
@@ -32,6 +40,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
32
40
|
protected readonly documentationProvider: DocumentationProvider;
|
|
33
41
|
protected readonly commentProvider: CommentProvider;
|
|
34
42
|
protected readonly qualifiedNameProvider: QualifiedNameProvider;
|
|
43
|
+
protected readonly indexManager: DomainLangIndexManager;
|
|
35
44
|
|
|
36
45
|
/**
|
|
37
46
|
* Registry of type-specific hover content generators.
|
|
@@ -45,24 +54,25 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
45
54
|
this.commentProvider = services.documentation.CommentProvider;
|
|
46
55
|
const domainServices = services as DomainLangServices;
|
|
47
56
|
this.qualifiedNameProvider = domainServices.references.QualifiedNameProvider;
|
|
57
|
+
this.indexManager = services.shared.workspace.IndexManager as DomainLangIndexManager;
|
|
48
58
|
|
|
49
59
|
// Register type-specific generators
|
|
50
60
|
this.hoverGenerators = [
|
|
51
|
-
(node, comment) => this.getDomainHover(node, comment),
|
|
52
|
-
(node, comment) => this.getThisRefHover(node, comment),
|
|
53
|
-
(node, comment) => this.getBoundedContextHover(node, comment),
|
|
54
|
-
(node, comment) => this.getNamespaceHover(node, comment),
|
|
55
|
-
(node, comment) => this.getContextMapHover(node, comment),
|
|
56
|
-
(node, comment) => this.getDomainMapHover(node, comment),
|
|
57
|
-
(node, comment) => this.getDecisionHover(node, comment),
|
|
58
|
-
(node, comment) => this.getPolicyHover(node, comment),
|
|
59
|
-
(node, comment) => this.getBusinessRuleHover(node, comment),
|
|
60
|
-
(node, comment) => this.getDomainTermHover(node, comment),
|
|
61
|
-
(node, comment) => this.getTeamHover(node, comment),
|
|
62
|
-
(node, comment) => this.getClassificationHover(node, comment),
|
|
63
|
-
(node, comment) => this.getMetadataHover(node, comment),
|
|
64
|
-
(node, comment) => this.getRelationshipHover(node, comment),
|
|
65
|
-
(node, comment) => this.getImportHover(node, comment),
|
|
61
|
+
(node, comment, alias) => this.getDomainHover(node, comment, alias),
|
|
62
|
+
(node, comment, alias) => this.getThisRefHover(node, comment, alias),
|
|
63
|
+
(node, comment, alias) => this.getBoundedContextHover(node, comment, alias),
|
|
64
|
+
(node, comment, alias) => this.getNamespaceHover(node, comment, alias),
|
|
65
|
+
(node, comment, alias) => this.getContextMapHover(node, comment, alias),
|
|
66
|
+
(node, comment, alias) => this.getDomainMapHover(node, comment, alias),
|
|
67
|
+
(node, comment, alias) => this.getDecisionHover(node, comment, alias),
|
|
68
|
+
(node, comment, alias) => this.getPolicyHover(node, comment, alias),
|
|
69
|
+
(node, comment, alias) => this.getBusinessRuleHover(node, comment, alias),
|
|
70
|
+
(node, comment, alias) => this.getDomainTermHover(node, comment, alias),
|
|
71
|
+
(node, comment, alias) => this.getTeamHover(node, comment, alias),
|
|
72
|
+
(node, comment, alias) => this.getClassificationHover(node, comment, alias),
|
|
73
|
+
(node, comment, alias) => this.getMetadataHover(node, comment, alias),
|
|
74
|
+
(node, comment, alias) => this.getRelationshipHover(node, comment, alias),
|
|
75
|
+
(node, comment, alias) => this.getImportHover(node, comment, alias),
|
|
66
76
|
];
|
|
67
77
|
}
|
|
68
78
|
|
|
@@ -80,13 +90,13 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
80
90
|
}
|
|
81
91
|
|
|
82
92
|
// Try declaration hover first
|
|
83
|
-
const declarationHover = await this.tryGetDeclarationHover(cstNode);
|
|
93
|
+
const declarationHover = await this.tryGetDeclarationHover(cstNode, document);
|
|
84
94
|
if (declarationHover) {
|
|
85
95
|
return declarationHover;
|
|
86
96
|
}
|
|
87
97
|
|
|
88
98
|
// Then try keyword hover
|
|
89
|
-
return
|
|
99
|
+
return this.tryGetKeywordHover(cstNode);
|
|
90
100
|
} catch (error) {
|
|
91
101
|
console.error('Error in getHoverContent:', error);
|
|
92
102
|
return undefined;
|
|
@@ -95,21 +105,46 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
95
105
|
|
|
96
106
|
/**
|
|
97
107
|
* Try to get hover for a declaration node (AST node).
|
|
108
|
+
* For qualified name references, only shows hover when hovering over the last segment.
|
|
109
|
+
* For qualified name declarations (like namespace names), shows hover on any segment.
|
|
98
110
|
*/
|
|
99
|
-
private async tryGetDeclarationHover(
|
|
111
|
+
private async tryGetDeclarationHover(
|
|
112
|
+
cstNode: ReturnType<typeof CstUtils.findDeclarationNodeAtOffset>,
|
|
113
|
+
document: LangiumDocument
|
|
114
|
+
): Promise<Hover | undefined> {
|
|
100
115
|
if (!cstNode) return undefined;
|
|
101
116
|
|
|
117
|
+
// For qualified name REFERENCES (e.g., "Core.Baunwalls.Jannie" in a BC classification),
|
|
118
|
+
// only show hover on the last segment. But for qualified name DECLARATIONS (like namespace names),
|
|
119
|
+
// show hover on any segment.
|
|
120
|
+
const isNamespaceDeclaration = ast.isNamespaceDeclaration(cstNode.astNode);
|
|
121
|
+
|
|
122
|
+
if (!isNamespaceDeclaration) {
|
|
123
|
+
// This is a reference context - check if there's a dot immediately after this node
|
|
124
|
+
const fullText = document.textDocument.getText();
|
|
125
|
+
const nodeEndOffset = cstNode.offset + cstNode.length;
|
|
126
|
+
|
|
127
|
+
// If the next non-whitespace character is a dot, this is not the last segment
|
|
128
|
+
if (nodeEndOffset < fullText.length) {
|
|
129
|
+
const nextChar = fullText.charAt(nodeEndOffset);
|
|
130
|
+
if (nextChar === '.') {
|
|
131
|
+
// This node is followed by a dot, so it's not the last segment
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
102
137
|
const targetNodes = this.references.findDeclarations(cstNode);
|
|
103
138
|
const targetNode = targetNodes?.[0];
|
|
104
139
|
if (targetNode) {
|
|
105
|
-
const content = await this.getAstNodeHoverContent(targetNode);
|
|
140
|
+
const content = await this.getAstNodeHoverContent(targetNode, document);
|
|
106
141
|
if (content) {
|
|
107
142
|
return { contents: { kind: 'markdown', value: content } };
|
|
108
143
|
}
|
|
109
144
|
}
|
|
110
145
|
|
|
111
146
|
if (cstNode.astNode && ast.isThisRef(cstNode.astNode)) {
|
|
112
|
-
const content = await this.getAstNodeHoverContent(cstNode.astNode);
|
|
147
|
+
const content = await this.getAstNodeHoverContent(cstNode.astNode, document);
|
|
113
148
|
if (content) {
|
|
114
149
|
return { contents: { kind: 'markdown', value: content } };
|
|
115
150
|
}
|
|
@@ -122,7 +157,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
122
157
|
* Try to get hover for a keyword node.
|
|
123
158
|
* Uses the keyword dictionary for all keywords.
|
|
124
159
|
*/
|
|
125
|
-
private
|
|
160
|
+
private tryGetKeywordHover(cstNode: ReturnType<typeof CstUtils.findDeclarationNodeAtOffset>): Hover | undefined {
|
|
126
161
|
if (!cstNode || cstNode.grammarSource?.$type !== 'Keyword') {
|
|
127
162
|
return undefined;
|
|
128
163
|
}
|
|
@@ -136,54 +171,76 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
136
171
|
return undefined;
|
|
137
172
|
}
|
|
138
173
|
|
|
139
|
-
protected getAstNodeHoverContent(node: AstNode): MaybePromise<string | undefined> {
|
|
174
|
+
protected getAstNodeHoverContent(node: AstNode, currentDocument?: LangiumDocument): MaybePromise<string | undefined> {
|
|
140
175
|
try {
|
|
141
176
|
const content = this.documentationProvider.getDocumentation(node);
|
|
142
177
|
const commentBlock = content ? `*${content}*\n\n` : '';
|
|
143
178
|
|
|
179
|
+
// Get import alias if the node is from an imported document
|
|
180
|
+
const importAlias = currentDocument ? this.getImportAliasForNode(node, currentDocument) : undefined;
|
|
181
|
+
|
|
144
182
|
// Try each type-specific generator
|
|
145
183
|
for (const generator of this.hoverGenerators) {
|
|
146
|
-
const result = generator(node, commentBlock);
|
|
184
|
+
const result = generator(node, commentBlock, importAlias);
|
|
147
185
|
if (result !== undefined) {
|
|
148
186
|
return result;
|
|
149
187
|
}
|
|
150
188
|
}
|
|
151
189
|
|
|
152
190
|
// Default fallback for unknown types
|
|
153
|
-
return this.getDefaultHover(node, commentBlock);
|
|
191
|
+
return this.getDefaultHover(node, commentBlock, importAlias);
|
|
154
192
|
} catch (error) {
|
|
155
193
|
console.error('Error in getAstNodeHoverContent:', error);
|
|
156
194
|
return undefined;
|
|
157
195
|
}
|
|
158
196
|
}
|
|
159
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Gets the import alias for a node if it's from an imported document.
|
|
200
|
+
* Returns undefined if the node is in the same document or not imported with an alias.
|
|
201
|
+
*/
|
|
202
|
+
private getImportAliasForNode(targetNode: AstNode, currentDocument: LangiumDocument): string | undefined {
|
|
203
|
+
try {
|
|
204
|
+
const targetDoc = AstUtils.getDocument(targetNode);
|
|
205
|
+
const currentDocUri = currentDocument.uri.toString();
|
|
206
|
+
const targetDocUri = targetDoc.uri.toString();
|
|
207
|
+
|
|
208
|
+
// If same document, no alias needed
|
|
209
|
+
if (currentDocUri === targetDocUri) {
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Look up import info for the current document
|
|
214
|
+
const importInfo = this.indexManager.getImportInfo(currentDocUri);
|
|
215
|
+
|
|
216
|
+
// Find the import that brings in the target document
|
|
217
|
+
for (const imp of importInfo) {
|
|
218
|
+
if (imp.resolvedUri === targetDocUri && imp.alias) {
|
|
219
|
+
return imp.alias;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return undefined;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error('Error getting import alias for node:', error);
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
160
230
|
// ============================================================
|
|
161
231
|
// Type-specific hover generators
|
|
162
232
|
// ============================================================
|
|
163
233
|
|
|
164
|
-
private getDomainHover(node: AstNode, commentBlock: string): string | undefined {
|
|
234
|
+
private getDomainHover(node: AstNode, commentBlock: string, importAlias?: string): string | undefined {
|
|
165
235
|
if (!ast.isDomain(node)) return undefined;
|
|
166
236
|
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const signatureParts = ['Domain', node.name];
|
|
173
|
-
if (node.parent?.ref?.name) signatureParts.push('in', node.parent.ref.name);
|
|
174
|
-
const signature = this.codeBlock(signatureParts.join(' '));
|
|
175
|
-
|
|
176
|
-
const fields: string[] = [signature];
|
|
177
|
-
if (description) fields.push(description);
|
|
178
|
-
if (vision || type || node.parent) fields.push('---');
|
|
179
|
-
if (vision) fields.push(`**Vision:** ${vision}`);
|
|
180
|
-
if (type) fields.push(`**Type:** ${this.refLink(typeRef, type)}`);
|
|
181
|
-
if (node.parent) fields.push(`**Parent:** ${this.refLink(node.parent)}`);
|
|
182
|
-
|
|
183
|
-
return this.formatHover(commentBlock, '📁', 'domain', node.name, fields);
|
|
237
|
+
const displayName = this.getDisplayNameForHover(node, importAlias);
|
|
238
|
+
const refLink = (ref: ast.Type | undefined, label?: string): string => this.refLink(ref, label);
|
|
239
|
+
const fields = buildDomainFields(node, refLink);
|
|
240
|
+
return formatHoverContent(commentBlock, '📁', 'domain', displayName, fields);
|
|
184
241
|
}
|
|
185
242
|
|
|
186
|
-
private getThisRefHover(node: AstNode, _commentBlock: string): string | undefined {
|
|
243
|
+
private getThisRefHover(node: AstNode, _commentBlock: string, _importAlias?: string): string | undefined {
|
|
187
244
|
if (!ast.isThisRef(node)) return undefined;
|
|
188
245
|
|
|
189
246
|
let parent = node.$container;
|
|
@@ -196,7 +253,17 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
196
253
|
ast.isDomainMap(parent) ||
|
|
197
254
|
ast.isModel(parent)
|
|
198
255
|
) {
|
|
199
|
-
|
|
256
|
+
const result = this.getAstNodeHoverContent(parent);
|
|
257
|
+
// getAstNodeHoverContent returns MaybePromise<string | undefined>.
|
|
258
|
+
// All registered hover generators are synchronous, so the result
|
|
259
|
+
// should always be a plain string. Guard defensively in case a
|
|
260
|
+
// future generator becomes async.
|
|
261
|
+
if (typeof result === 'string' || result === undefined) {
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
// If somehow a Promise is returned, we cannot await in a sync
|
|
265
|
+
// context - fall through to the default message.
|
|
266
|
+
return undefined;
|
|
200
267
|
}
|
|
201
268
|
parent = parent.$container;
|
|
202
269
|
}
|
|
@@ -204,58 +271,27 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
204
271
|
return '*this* refers to the current context';
|
|
205
272
|
}
|
|
206
273
|
|
|
207
|
-
private getBoundedContextHover(node: AstNode, commentBlock: string): string | undefined {
|
|
274
|
+
private getBoundedContextHover(node: AstNode, commentBlock: string, importAlias?: string): string | undefined {
|
|
208
275
|
if (!ast.isBoundedContext(node)) return undefined;
|
|
209
276
|
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const classificationName = classification?.name;
|
|
219
|
-
const teamName = team?.name;
|
|
220
|
-
|
|
221
|
-
const signatureParts = ['boundedcontext', node.name];
|
|
222
|
-
if (node.domain?.ref?.name) signatureParts.push('for', node.domain.ref.name);
|
|
223
|
-
if (classificationName) signatureParts.push('as', classificationName);
|
|
224
|
-
if (teamName) signatureParts.push('by', teamName);
|
|
225
|
-
const signature = this.codeBlock(signatureParts.join(' '));
|
|
226
|
-
|
|
227
|
-
const fields: string[] = [signature];
|
|
228
|
-
if (description) fields.push(description);
|
|
229
|
-
if (classification || team || businessModel || evolution) fields.push('---');
|
|
230
|
-
if (classification) fields.push(`🔖 **Classification:** ${this.refLink(classification)}`);
|
|
231
|
-
if (team) fields.push(`👥 **Team:** ${this.refLink(team)}`);
|
|
232
|
-
if (businessModel) fields.push(`💼 **Business Model:** ${this.refLink(businessModel)}`);
|
|
233
|
-
if (evolution) fields.push(`🔄 **Evolution:** ${this.refLink(evolution)}`);
|
|
234
|
-
|
|
235
|
-
if (relationships.length > 0) {
|
|
236
|
-
const lines = relationships.map(rel => this.formatRelationshipLine(rel));
|
|
237
|
-
fields.push(`**Relationships:**\n${lines.join('\n')}`);
|
|
238
|
-
}
|
|
239
|
-
if (terminology.length > 0) {
|
|
240
|
-
const lines = terminology.map(t => `- \`${t.name}\`: ${t.meaning ?? ''}`);
|
|
241
|
-
fields.push(`**Terminology:**\n${lines.join('\n')}`);
|
|
242
|
-
}
|
|
243
|
-
if (decisions.length > 0) {
|
|
244
|
-
const lines = decisions.map(d => `- \`${d.name}\`: ${d.value ?? ''}`);
|
|
245
|
-
fields.push(`**Decisions:**\n${lines.join('\n')}`);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return this.formatHover(commentBlock, '📕', 'boundedcontext', node.name, fields);
|
|
277
|
+
const displayName = this.getDisplayNameForHover(node, importAlias);
|
|
278
|
+
const refLink = (ref: ast.Type | undefined, label?: string): string => this.refLink(ref, label);
|
|
279
|
+
const fields = buildBcFields(
|
|
280
|
+
node,
|
|
281
|
+
refLink,
|
|
282
|
+
(rel) => this.formatRelationshipLine(rel)
|
|
283
|
+
);
|
|
284
|
+
return formatHoverContent(commentBlock, '🎯', 'bounded context', displayName, fields);
|
|
249
285
|
}
|
|
250
286
|
|
|
251
|
-
private getNamespaceHover(node: AstNode, commentBlock: string): string | undefined {
|
|
287
|
+
private getNamespaceHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
252
288
|
if (!ast.isNamespaceDeclaration(node)) return undefined;
|
|
253
289
|
|
|
254
290
|
const fields: string[] = [`Contains ${node.children.length} elements.`];
|
|
255
291
|
return this.formatHover(commentBlock, '🧭', 'namespace', node.name, fields);
|
|
256
292
|
}
|
|
257
293
|
|
|
258
|
-
private getContextMapHover(node: AstNode, commentBlock: string): string | undefined {
|
|
294
|
+
private getContextMapHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
259
295
|
if (!ast.isContextMap(node)) return undefined;
|
|
260
296
|
|
|
261
297
|
const fields: string[] = [];
|
|
@@ -274,7 +310,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
274
310
|
return this.formatHover(commentBlock, '🗺️', 'contextmap', node.name, fields);
|
|
275
311
|
}
|
|
276
312
|
|
|
277
|
-
private getDomainMapHover(node: AstNode, commentBlock: string): string | undefined {
|
|
313
|
+
private getDomainMapHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
278
314
|
if (!ast.isDomainMap(node)) return undefined;
|
|
279
315
|
|
|
280
316
|
const fields: string[] = [];
|
|
@@ -288,7 +324,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
288
324
|
return this.formatHover(commentBlock, '🗺️', 'domainmap', node.name, fields);
|
|
289
325
|
}
|
|
290
326
|
|
|
291
|
-
private getDecisionHover(node: AstNode, commentBlock: string): string | undefined {
|
|
327
|
+
private getDecisionHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
292
328
|
if (!ast.isDecision(node)) return undefined;
|
|
293
329
|
|
|
294
330
|
const fields: string[] = [];
|
|
@@ -298,7 +334,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
298
334
|
return this.formatHover(commentBlock, '⚖️', 'decision', node.name, fields);
|
|
299
335
|
}
|
|
300
336
|
|
|
301
|
-
private getPolicyHover(node: AstNode, commentBlock: string): string | undefined {
|
|
337
|
+
private getPolicyHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
302
338
|
if (!ast.isPolicy(node)) return undefined;
|
|
303
339
|
|
|
304
340
|
const fields: string[] = [];
|
|
@@ -308,7 +344,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
308
344
|
return this.formatHover(commentBlock, '📜', 'policy', node.name, fields);
|
|
309
345
|
}
|
|
310
346
|
|
|
311
|
-
private getBusinessRuleHover(node: AstNode, commentBlock: string): string | undefined {
|
|
347
|
+
private getBusinessRuleHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
312
348
|
if (!ast.isBusinessRule(node)) return undefined;
|
|
313
349
|
|
|
314
350
|
const fields: string[] = [];
|
|
@@ -318,7 +354,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
318
354
|
return this.formatHover(commentBlock, '⚖️', 'rule', node.name, fields);
|
|
319
355
|
}
|
|
320
356
|
|
|
321
|
-
private getDomainTermHover(node: AstNode, commentBlock: string): string | undefined {
|
|
357
|
+
private getDomainTermHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
322
358
|
if (!ast.isDomainTerm(node)) return undefined;
|
|
323
359
|
|
|
324
360
|
const fields: string[] = [];
|
|
@@ -328,22 +364,25 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
328
364
|
return this.formatHover(commentBlock, '🗝️', 'term', node.name, fields);
|
|
329
365
|
}
|
|
330
366
|
|
|
331
|
-
private getTeamHover(node: AstNode, commentBlock: string): string | undefined {
|
|
367
|
+
private getTeamHover(node: AstNode, commentBlock: string, importAlias?: string): string | undefined {
|
|
332
368
|
if (!ast.isTeam(node)) return undefined;
|
|
333
|
-
|
|
369
|
+
const displayName = this.getDisplayNameForHover(node, importAlias);
|
|
370
|
+
return this.formatHover(commentBlock, '👥', 'team', displayName, []);
|
|
334
371
|
}
|
|
335
372
|
|
|
336
|
-
private getClassificationHover(node: AstNode, commentBlock: string): string | undefined {
|
|
373
|
+
private getClassificationHover(node: AstNode, commentBlock: string, importAlias?: string): string | undefined {
|
|
337
374
|
if (!ast.isClassification(node)) return undefined;
|
|
338
|
-
|
|
375
|
+
const displayName = this.getDisplayNameForHover(node, importAlias);
|
|
376
|
+
return this.formatHover(commentBlock, '🏷️', 'classification', displayName, []);
|
|
339
377
|
}
|
|
340
378
|
|
|
341
|
-
private getMetadataHover(node: AstNode, commentBlock: string): string | undefined {
|
|
379
|
+
private getMetadataHover(node: AstNode, commentBlock: string, importAlias?: string): string | undefined {
|
|
342
380
|
if (!ast.isMetadata(node)) return undefined;
|
|
343
|
-
|
|
381
|
+
const displayName = this.getDisplayNameForHover(node, importAlias);
|
|
382
|
+
return this.formatHover(commentBlock, '🔖', 'metadata', displayName, []);
|
|
344
383
|
}
|
|
345
384
|
|
|
346
|
-
private getRelationshipHover(node: AstNode, commentBlock: string): string | undefined {
|
|
385
|
+
private getRelationshipHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
347
386
|
if (!ast.isRelationship(node)) return undefined;
|
|
348
387
|
|
|
349
388
|
const leftPatterns = node.leftPatterns.join(', ');
|
|
@@ -358,7 +397,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
358
397
|
return this.formatHover(commentBlock, '🔗', 'relationship', undefined, fields);
|
|
359
398
|
}
|
|
360
399
|
|
|
361
|
-
private getImportHover(node: AstNode, commentBlock: string): string | undefined {
|
|
400
|
+
private getImportHover(node: AstNode, commentBlock: string, _importAlias?: string): string | undefined {
|
|
362
401
|
if (!ast.isImportStatement(node)) return undefined;
|
|
363
402
|
|
|
364
403
|
const fields: string[] = [`**URI:** \`${node.uri}\``];
|
|
@@ -366,7 +405,7 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
366
405
|
return this.formatHover(commentBlock, '📦', 'import', undefined, fields);
|
|
367
406
|
}
|
|
368
407
|
|
|
369
|
-
private getDefaultHover(node: AstNode, commentBlock: string): string {
|
|
408
|
+
private getDefaultHover(node: AstNode, commentBlock: string, _importAlias?: string): string {
|
|
370
409
|
const title = ast.isType(node) ? node.name : node.$type.toLowerCase();
|
|
371
410
|
const typeName = node.$type.toLowerCase();
|
|
372
411
|
const name = ast.isType(node) ? ` ${title}` : '';
|
|
@@ -379,6 +418,21 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
379
418
|
// Helper methods
|
|
380
419
|
// ============================================================
|
|
381
420
|
|
|
421
|
+
/**
|
|
422
|
+
* Computes the display name for a node in hover, considering import aliases.
|
|
423
|
+
* If node is imported with an alias, shows `alias.name`, otherwise shows full qualified name.
|
|
424
|
+
*/
|
|
425
|
+
private getDisplayNameForHover(node: ast.Type, importAlias?: string): string {
|
|
426
|
+
if (importAlias) {
|
|
427
|
+
return `${importAlias}.${node.name}`;
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
return this.qualifiedNameProvider.getQualifiedName(node.$container, node.name);
|
|
431
|
+
} catch {
|
|
432
|
+
return node.name;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
382
436
|
/**
|
|
383
437
|
* Formats a relationship line for hover display.
|
|
384
438
|
*/
|
|
@@ -389,15 +443,9 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
389
443
|
return `- ${left} ${rel.arrow} ${right}${type}`;
|
|
390
444
|
}
|
|
391
445
|
|
|
392
|
-
/**
|
|
393
|
-
* Wraps text in a domain-lang code block.
|
|
394
|
-
*/
|
|
395
|
-
private codeBlock(text: string): string {
|
|
396
|
-
return `\`\`\`domain-lang\n${text}\n\`\`\``;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
446
|
/**
|
|
400
447
|
* Formats the final hover content with consistent structure.
|
|
448
|
+
* Delegates to the shared hover-builders utility.
|
|
401
449
|
*/
|
|
402
450
|
private formatHover(
|
|
403
451
|
commentBlock: string,
|
|
@@ -406,38 +454,48 @@ export class DomainLangHoverProvider extends AstNodeHoverProvider {
|
|
|
406
454
|
name: string | undefined,
|
|
407
455
|
fields: string[]
|
|
408
456
|
): string {
|
|
409
|
-
|
|
410
|
-
const nameDisplay = name ? ` ${name}` : '';
|
|
411
|
-
const header = `${emoji} **\`(${typeName})\`${nameDisplay}**`;
|
|
412
|
-
const body = fields.length > 0 ? `\n\n${fields.join('\n\n')}` : '';
|
|
413
|
-
return `${separator}${header}${body}`;
|
|
457
|
+
return formatHoverContent(commentBlock, emoji, typeName, name, fields);
|
|
414
458
|
}
|
|
415
459
|
|
|
416
|
-
private
|
|
460
|
+
private refLink(ref: Reference<ast.Type> | ast.Type | undefined, label?: string): string {
|
|
417
461
|
const node = isReference(ref) ? ref.ref : ref;
|
|
418
|
-
if (node && ast.isType(node)) {
|
|
419
|
-
return node.name;
|
|
420
|
-
}
|
|
421
|
-
return '';
|
|
422
|
-
}
|
|
423
462
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
return `[${label}](#${encodeURIComponent(label)})`;
|
|
463
|
+
if (!node || !ast.isType(node)) {
|
|
464
|
+
return label ? `\`${label}\`` : '';
|
|
427
465
|
}
|
|
428
466
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
if (
|
|
432
|
-
|
|
467
|
+
// Get display name (use label if provided, otherwise qualified name)
|
|
468
|
+
let linkLabel: string;
|
|
469
|
+
if (label) {
|
|
470
|
+
linkLabel = label;
|
|
471
|
+
} else {
|
|
433
472
|
try {
|
|
434
473
|
linkLabel = this.qualifiedNameProvider.getQualifiedName(node.$container, node.name);
|
|
435
474
|
} catch {
|
|
436
|
-
|
|
475
|
+
linkLabel = node.name;
|
|
437
476
|
}
|
|
438
|
-
return `[${linkLabel}](#${encodeURIComponent(linkLabel)})`;
|
|
439
477
|
}
|
|
440
|
-
|
|
478
|
+
|
|
479
|
+
// Try to create clickable go-to-definition link
|
|
480
|
+
try {
|
|
481
|
+
const doc = AstUtils.getDocument(node);
|
|
482
|
+
const cstNode = node.$cstNode;
|
|
483
|
+
|
|
484
|
+
if (doc && cstNode) {
|
|
485
|
+
const range = CstUtils.toDocumentSegment(cstNode).range;
|
|
486
|
+
const line = range.start.line + 1; // LSP lines are 0-indexed, file links use 1-indexed
|
|
487
|
+
const col = range.start.character + 1;
|
|
488
|
+
const uri = doc.uri.toString();
|
|
489
|
+
|
|
490
|
+
// VS Code recognizes file:// URIs with #Lline,col for go-to-definition
|
|
491
|
+
return `[${linkLabel}](${uri}#L${line},${col})`;
|
|
492
|
+
}
|
|
493
|
+
} catch (error) {
|
|
494
|
+
console.error('Error creating hover link:', error);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Fallback to plain text if we can't create a link
|
|
498
|
+
return `\`${linkLabel}\``;
|
|
441
499
|
}
|
|
442
500
|
|
|
443
501
|
}
|