@domainlang/language 0.1.81
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/README.md +32 -0
- package/out/ast-augmentation.d.ts +6 -0
- package/out/ast-augmentation.js +2 -0
- package/out/ast-augmentation.js.map +1 -0
- package/out/domain-lang-module.d.ts +55 -0
- package/out/domain-lang-module.js +59 -0
- package/out/domain-lang-module.js.map +1 -0
- package/out/generated/ast.d.ts +770 -0
- package/out/generated/ast.js +565 -0
- package/out/generated/ast.js.map +1 -0
- package/out/generated/grammar.d.ts +6 -0
- package/out/generated/grammar.js +2502 -0
- package/out/generated/grammar.js.map +1 -0
- package/out/generated/module.d.ts +13 -0
- package/out/generated/module.js +21 -0
- package/out/generated/module.js.map +1 -0
- package/out/index.d.ts +13 -0
- package/out/index.js +17 -0
- package/out/index.js.map +1 -0
- package/out/lsp/domain-lang-completion.d.ts +37 -0
- package/out/lsp/domain-lang-completion.js +452 -0
- package/out/lsp/domain-lang-completion.js.map +1 -0
- package/out/lsp/domain-lang-formatter.d.ts +15 -0
- package/out/lsp/domain-lang-formatter.js +43 -0
- package/out/lsp/domain-lang-formatter.js.map +1 -0
- package/out/lsp/domain-lang-naming.d.ts +34 -0
- package/out/lsp/domain-lang-naming.js +49 -0
- package/out/lsp/domain-lang-naming.js.map +1 -0
- package/out/lsp/domain-lang-scope.d.ts +59 -0
- package/out/lsp/domain-lang-scope.js +102 -0
- package/out/lsp/domain-lang-scope.js.map +1 -0
- package/out/lsp/hover/ddd-pattern-explanations.d.ts +50 -0
- package/out/lsp/hover/ddd-pattern-explanations.js +196 -0
- package/out/lsp/hover/ddd-pattern-explanations.js.map +1 -0
- package/out/lsp/hover/domain-lang-hover.d.ts +19 -0
- package/out/lsp/hover/domain-lang-hover.js +306 -0
- package/out/lsp/hover/domain-lang-hover.js.map +1 -0
- package/out/lsp/hover/domain-lang-keywords.d.ts +13 -0
- package/out/lsp/hover/domain-lang-keywords.js +47 -0
- package/out/lsp/hover/domain-lang-keywords.js.map +1 -0
- package/out/main-browser.d.ts +1 -0
- package/out/main-browser.js +11 -0
- package/out/main-browser.js.map +1 -0
- package/out/main.d.ts +1 -0
- package/out/main.js +74 -0
- package/out/main.js.map +1 -0
- package/out/sdk/ast-augmentation.d.ts +136 -0
- package/out/sdk/ast-augmentation.js +62 -0
- package/out/sdk/ast-augmentation.js.map +1 -0
- package/out/sdk/index.d.ts +94 -0
- package/out/sdk/index.js +97 -0
- package/out/sdk/index.js.map +1 -0
- package/out/sdk/indexes.d.ts +16 -0
- package/out/sdk/indexes.js +97 -0
- package/out/sdk/indexes.js.map +1 -0
- package/out/sdk/loader-node.d.ts +47 -0
- package/out/sdk/loader-node.js +104 -0
- package/out/sdk/loader-node.js.map +1 -0
- package/out/sdk/loader.d.ts +49 -0
- package/out/sdk/loader.js +85 -0
- package/out/sdk/loader.js.map +1 -0
- package/out/sdk/patterns.d.ts +93 -0
- package/out/sdk/patterns.js +123 -0
- package/out/sdk/patterns.js.map +1 -0
- package/out/sdk/query.d.ts +90 -0
- package/out/sdk/query.js +679 -0
- package/out/sdk/query.js.map +1 -0
- package/out/sdk/resolution.d.ts +52 -0
- package/out/sdk/resolution.js +68 -0
- package/out/sdk/resolution.js.map +1 -0
- package/out/sdk/types.d.ts +301 -0
- package/out/sdk/types.js +8 -0
- package/out/sdk/types.js.map +1 -0
- package/out/services/dependency-analyzer.d.ts +94 -0
- package/out/services/dependency-analyzer.js +279 -0
- package/out/services/dependency-analyzer.js.map +1 -0
- package/out/services/dependency-resolver.d.ts +123 -0
- package/out/services/dependency-resolver.js +252 -0
- package/out/services/dependency-resolver.js.map +1 -0
- package/out/services/git-url-resolver.browser.d.ts +18 -0
- package/out/services/git-url-resolver.browser.js +15 -0
- package/out/services/git-url-resolver.browser.js.map +1 -0
- package/out/services/git-url-resolver.d.ts +192 -0
- package/out/services/git-url-resolver.js +382 -0
- package/out/services/git-url-resolver.js.map +1 -0
- package/out/services/governance-validator.d.ts +80 -0
- package/out/services/governance-validator.js +159 -0
- package/out/services/governance-validator.js.map +1 -0
- package/out/services/import-resolver.d.ts +18 -0
- package/out/services/import-resolver.js +22 -0
- package/out/services/import-resolver.js.map +1 -0
- package/out/services/performance-optimizer.d.ts +60 -0
- package/out/services/performance-optimizer.js +140 -0
- package/out/services/performance-optimizer.js.map +1 -0
- package/out/services/relationship-inference.d.ts +11 -0
- package/out/services/relationship-inference.js +98 -0
- package/out/services/relationship-inference.js.map +1 -0
- package/out/services/workspace-manager.d.ts +76 -0
- package/out/services/workspace-manager.js +323 -0
- package/out/services/workspace-manager.js.map +1 -0
- package/out/syntaxes/domain-lang.monarch.d.ts +76 -0
- package/out/syntaxes/domain-lang.monarch.js +29 -0
- package/out/syntaxes/domain-lang.monarch.js.map +1 -0
- package/out/utils/import-utils.d.ts +57 -0
- package/out/utils/import-utils.js +228 -0
- package/out/utils/import-utils.js.map +1 -0
- package/out/validation/bounded-context.d.ts +11 -0
- package/out/validation/bounded-context.js +79 -0
- package/out/validation/bounded-context.js.map +1 -0
- package/out/validation/classification.d.ts +3 -0
- package/out/validation/classification.js +3 -0
- package/out/validation/classification.js.map +1 -0
- package/out/validation/constants.d.ts +77 -0
- package/out/validation/constants.js +96 -0
- package/out/validation/constants.js.map +1 -0
- package/out/validation/domain-lang-validator.d.ts +2 -0
- package/out/validation/domain-lang-validator.js +27 -0
- package/out/validation/domain-lang-validator.js.map +1 -0
- package/out/validation/domain.d.ts +11 -0
- package/out/validation/domain.js +18 -0
- package/out/validation/domain.js.map +1 -0
- package/out/validation/import.d.ts +44 -0
- package/out/validation/import.js +135 -0
- package/out/validation/import.js.map +1 -0
- package/out/validation/maps.d.ts +21 -0
- package/out/validation/maps.js +56 -0
- package/out/validation/maps.js.map +1 -0
- package/out/validation/metadata.d.ts +7 -0
- package/out/validation/metadata.js +12 -0
- package/out/validation/metadata.js.map +1 -0
- package/out/validation/model.d.ts +12 -0
- package/out/validation/model.js +29 -0
- package/out/validation/model.js.map +1 -0
- package/out/validation/relationships.d.ts +12 -0
- package/out/validation/relationships.js +94 -0
- package/out/validation/relationships.js.map +1 -0
- package/out/validation/shared.d.ts +6 -0
- package/out/validation/shared.js +12 -0
- package/out/validation/shared.js.map +1 -0
- package/package.json +97 -0
- package/src/ast-augmentation.ts +5 -0
- package/src/domain-lang-module.ts +100 -0
- package/src/domain-lang.langium +356 -0
- package/src/generated/ast.ts +999 -0
- package/src/generated/grammar.ts +2504 -0
- package/src/generated/module.ts +25 -0
- package/src/index.ts +17 -0
- package/src/lsp/domain-lang-completion.ts +514 -0
- package/src/lsp/domain-lang-formatter.ts +51 -0
- package/src/lsp/domain-lang-naming.ts +56 -0
- package/src/lsp/domain-lang-scope.ts +137 -0
- package/src/lsp/hover/ddd-pattern-explanations.ts +237 -0
- package/src/lsp/hover/domain-lang-hover.ts +340 -0
- package/src/lsp/hover/domain-lang-keywords.ts +50 -0
- package/src/main-browser.ts +15 -0
- package/src/main.ts +85 -0
- package/src/sdk/README.md +297 -0
- package/src/sdk/ast-augmentation.ts +157 -0
- package/src/sdk/index.ts +128 -0
- package/src/sdk/indexes.ts +155 -0
- package/src/sdk/loader-node.ts +126 -0
- package/src/sdk/loader.ts +99 -0
- package/src/sdk/patterns.ts +147 -0
- package/src/sdk/query.ts +802 -0
- package/src/sdk/resolution.ts +78 -0
- package/src/sdk/types.ts +346 -0
- package/src/services/dependency-analyzer.ts +381 -0
- package/src/services/dependency-resolver.ts +334 -0
- package/src/services/git-url-resolver.browser.ts +31 -0
- package/src/services/git-url-resolver.ts +524 -0
- package/src/services/governance-validator.ts +219 -0
- package/src/services/import-resolver.ts +30 -0
- package/src/services/performance-optimizer.ts +170 -0
- package/src/services/relationship-inference.ts +121 -0
- package/src/services/workspace-manager.ts +416 -0
- package/src/syntaxes/domain-lang.monarch.ts +29 -0
- package/src/utils/import-utils.ts +274 -0
- package/src/validation/bounded-context.ts +99 -0
- package/src/validation/classification.ts +5 -0
- package/src/validation/constants.ts +124 -0
- package/src/validation/domain-lang-validator.ts +33 -0
- package/src/validation/domain.ts +24 -0
- package/src/validation/import.ts +171 -0
- package/src/validation/maps.ts +72 -0
- package/src/validation/metadata.ts +14 -0
- package/src/validation/model.ts +37 -0
- package/src/validation/relationships.ts +154 -0
- package/src/validation/shared.ts +14 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/******************************************************************************
|
|
2
|
+
* This file was generated by langium-cli 4.1.0.
|
|
3
|
+
* DO NOT EDIT MANUALLY!
|
|
4
|
+
******************************************************************************/
|
|
5
|
+
|
|
6
|
+
import type { LangiumSharedCoreServices, LangiumCoreServices, LangiumGeneratedCoreServices, LangiumGeneratedSharedCoreServices, LanguageMetaData, Module } from 'langium';
|
|
7
|
+
import { DomainLangAstReflection } from './ast.js';
|
|
8
|
+
import { DomainLangGrammar } from './grammar.js';
|
|
9
|
+
|
|
10
|
+
export const DomainLangLanguageMetaData = {
|
|
11
|
+
languageId: 'domain-lang',
|
|
12
|
+
fileExtensions: ['.dlang'],
|
|
13
|
+
caseInsensitive: false,
|
|
14
|
+
mode: 'development'
|
|
15
|
+
} as const satisfies LanguageMetaData;
|
|
16
|
+
|
|
17
|
+
export const DomainLangGeneratedSharedModule: Module<LangiumSharedCoreServices, LangiumGeneratedSharedCoreServices> = {
|
|
18
|
+
AstReflection: () => new DomainLangAstReflection()
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const DomainLangGeneratedModule: Module<LangiumCoreServices, LangiumGeneratedCoreServices> = {
|
|
22
|
+
Grammar: () => DomainLangGrammar(),
|
|
23
|
+
LanguageMetaData: () => DomainLangLanguageMetaData,
|
|
24
|
+
parser: {}
|
|
25
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './domain-lang-module.js';
|
|
2
|
+
export * from './generated/ast.js';
|
|
3
|
+
export * from './generated/grammar.js';
|
|
4
|
+
export * from './generated/module.js';
|
|
5
|
+
// Note: main.js is intentionally NOT exported here - it's the LSP entry point
|
|
6
|
+
// and creates a connection when imported, which breaks CLI/SDK standalone usage
|
|
7
|
+
export * from './ast-augmentation.js';
|
|
8
|
+
|
|
9
|
+
// Export services
|
|
10
|
+
export * from './services/workspace-manager.js';
|
|
11
|
+
export * from './services/dependency-resolver.js';
|
|
12
|
+
export * from './services/dependency-analyzer.js';
|
|
13
|
+
export * from './services/governance-validator.js';
|
|
14
|
+
export * from './services/import-resolver.js';
|
|
15
|
+
export * from './services/relationship-inference.js';
|
|
16
|
+
export * from './services/git-url-resolver.js';
|
|
17
|
+
export * from './services/performance-optimizer.js';
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completion provider for DomainLang - context-aware, grammar-aligned.
|
|
3
|
+
*
|
|
4
|
+
* **Design:**
|
|
5
|
+
* - Context-aware: Only suggests what's valid at cursor position
|
|
6
|
+
* - Grammar-aligned: Completions match grammar structure exactly
|
|
7
|
+
* - Simple: Uses parent node to determine context
|
|
8
|
+
* - Maintainable: Clear mapping from grammar to completions
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { CompletionAcceptor, CompletionContext, DefaultCompletionProvider, NextFeature } from 'langium/lsp';
|
|
12
|
+
import { CompletionItemKind, InsertTextFormat } from 'vscode-languageserver';
|
|
13
|
+
import * as ast from '../generated/ast.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Top-level snippet templates for creating new AST nodes.
|
|
17
|
+
*/
|
|
18
|
+
const TOP_LEVEL_SNIPPETS = [
|
|
19
|
+
{
|
|
20
|
+
label: '⚡ Domain (simple)',
|
|
21
|
+
kind: CompletionItemKind.Snippet,
|
|
22
|
+
insertText: 'Domain ${1:Name} {}',
|
|
23
|
+
documentation: '📝 Snippet: Create a simple domain',
|
|
24
|
+
sortText: '0_snippet_domain_simple'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: '⚡ Domain (detailed)',
|
|
28
|
+
kind: CompletionItemKind.Snippet,
|
|
29
|
+
insertText: [
|
|
30
|
+
'Domain ${1:Name} {',
|
|
31
|
+
'\tdescription: "${2:Description}"',
|
|
32
|
+
'\tvision: "${3:Vision}"',
|
|
33
|
+
'\tclassification: ${4:CoreDomain}',
|
|
34
|
+
'}'
|
|
35
|
+
].join('\n'),
|
|
36
|
+
documentation: '📝 Snippet: Create a domain with description and vision',
|
|
37
|
+
sortText: '0_snippet_domain_detailed'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: '⚡ BoundedContext (simple)',
|
|
41
|
+
kind: CompletionItemKind.Snippet,
|
|
42
|
+
insertText: 'bc ${1:Name} for ${2:Domain} as ${3:Core} by ${4:Team}',
|
|
43
|
+
documentation: '📝 Snippet: Quick bounded context definition',
|
|
44
|
+
sortText: '0_snippet_bc_simple'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: '⚡ BoundedContext (detailed)',
|
|
48
|
+
kind: CompletionItemKind.Snippet,
|
|
49
|
+
insertText: [
|
|
50
|
+
'BoundedContext ${1:Name} for ${2:Domain} {',
|
|
51
|
+
'\tdescription: "${3:Description}"',
|
|
52
|
+
'\tteam: ${4:Team}',
|
|
53
|
+
'\trole: ${5:Core}',
|
|
54
|
+
'\t',
|
|
55
|
+
'\tterminology {',
|
|
56
|
+
'\t\tterm ${6:Term}: "${7:Definition}"',
|
|
57
|
+
'\t}',
|
|
58
|
+
'}'
|
|
59
|
+
].join('\n'),
|
|
60
|
+
documentation: '📝 Snippet: Full bounded context with all common blocks',
|
|
61
|
+
sortText: '0_snippet_bc_detailed'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: '⚡ ContextMap',
|
|
65
|
+
kind: CompletionItemKind.Snippet,
|
|
66
|
+
insertText: [
|
|
67
|
+
'ContextMap ${1:Name} {',
|
|
68
|
+
'\tcontains ${2:Context1}, ${3:Context2}',
|
|
69
|
+
'}'
|
|
70
|
+
].join('\n'),
|
|
71
|
+
documentation: '📝 Snippet: Create a context map',
|
|
72
|
+
sortText: '0_snippet_contextmap'
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: '⚡ DomainMap',
|
|
76
|
+
kind: CompletionItemKind.Snippet,
|
|
77
|
+
insertText: [
|
|
78
|
+
'DomainMap ${1:Name} {',
|
|
79
|
+
'\tcontains ${2:Domain1}, ${3:Domain2}',
|
|
80
|
+
'}'
|
|
81
|
+
].join('\n'),
|
|
82
|
+
documentation: '📝 Snippet: Create a domain map',
|
|
83
|
+
sortText: '0_snippet_domainmap'
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: '⚡ Team',
|
|
87
|
+
kind: CompletionItemKind.Snippet,
|
|
88
|
+
insertText: 'Team ${1:TeamName}',
|
|
89
|
+
documentation: '📝 Snippet: Define a team',
|
|
90
|
+
sortText: '0_snippet_team'
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: '⚡ Classification',
|
|
94
|
+
kind: CompletionItemKind.Snippet,
|
|
95
|
+
insertText: 'Classification ${1:Name}',
|
|
96
|
+
documentation: '📝 Snippet: Define a reusable classification label',
|
|
97
|
+
sortText: '0_snippet_classification'
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
label: '⚡ Metadata',
|
|
101
|
+
kind: CompletionItemKind.Snippet,
|
|
102
|
+
insertText: 'Metadata ${1:KeyName}',
|
|
103
|
+
documentation: '📝 Snippet: Define a metadata key',
|
|
104
|
+
sortText: '0_snippet_metadata'
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
label: '⚡ Namespace',
|
|
108
|
+
kind: CompletionItemKind.Snippet,
|
|
109
|
+
insertText: [
|
|
110
|
+
'Namespace ${1:name.space} {',
|
|
111
|
+
'\t$0',
|
|
112
|
+
'}'
|
|
113
|
+
].join('\n'),
|
|
114
|
+
documentation: '📝 Snippet: Create a hierarchical namespace',
|
|
115
|
+
sortText: '0_snippet_namespace'
|
|
116
|
+
}
|
|
117
|
+
] as const;
|
|
118
|
+
|
|
119
|
+
export class DomainLangCompletionProvider extends DefaultCompletionProvider {
|
|
120
|
+
protected override completionFor(
|
|
121
|
+
context: CompletionContext,
|
|
122
|
+
next: NextFeature,
|
|
123
|
+
acceptor: CompletionAcceptor
|
|
124
|
+
): void {
|
|
125
|
+
const node = context.node;
|
|
126
|
+
if (!node) {
|
|
127
|
+
super.completionFor(context, next, acceptor);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Strategy: Check node type and container to determine what's allowed at cursor position
|
|
132
|
+
|
|
133
|
+
// Check if cursor is after the node (for top-level positioning)
|
|
134
|
+
const offset = context.offset;
|
|
135
|
+
const nodeEnd = node.$cstNode?.end ?? 0;
|
|
136
|
+
const isAfterNode = offset >= nodeEnd;
|
|
137
|
+
|
|
138
|
+
// If we're positioned after a BC/Domain (e.g., on next line): show top-level
|
|
139
|
+
if ((ast.isBoundedContext(node) || ast.isDomain(node)) && isAfterNode) {
|
|
140
|
+
this.addTopLevelSnippets(acceptor, context);
|
|
141
|
+
// Let Langium provide keywords like "bc", "Domain", etc.
|
|
142
|
+
super.completionFor(context, next, acceptor);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// If we're AT a BoundedContext node: only BC documentation blocks
|
|
147
|
+
if (ast.isBoundedContext(node)) {
|
|
148
|
+
this.addBoundedContextCompletions(node, acceptor, context);
|
|
149
|
+
super.completionFor(context, next, acceptor);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// If we're AT a Domain node: only Domain documentation blocks
|
|
154
|
+
if (ast.isDomain(node)) {
|
|
155
|
+
this.addDomainCompletions(node, acceptor, context);
|
|
156
|
+
super.completionFor(context, next, acceptor);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// If we're AT a ContextMap node: relationships and contains
|
|
161
|
+
if (ast.isContextMap(node)) {
|
|
162
|
+
this.addContextMapCompletions(node, acceptor, context);
|
|
163
|
+
super.completionFor(context, next, acceptor);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// If we're AT a DomainMap node: contains
|
|
168
|
+
if (ast.isDomainMap(node)) {
|
|
169
|
+
this.addDomainMapCompletions(node, acceptor, context);
|
|
170
|
+
super.completionFor(context, next, acceptor);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// If we're AT the Model or NamespaceDeclaration level: all top-level constructs
|
|
175
|
+
if (ast.isModel(node) || ast.isNamespaceDeclaration(node)) {
|
|
176
|
+
this.addTopLevelSnippets(acceptor, context);
|
|
177
|
+
super.completionFor(context, next, acceptor);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const container = node.$container;
|
|
182
|
+
|
|
183
|
+
// Inside BoundedContext body: suggest missing scalar properties and collections
|
|
184
|
+
if (ast.isBoundedContext(container)) {
|
|
185
|
+
this.addBoundedContextCompletions(container, acceptor, context);
|
|
186
|
+
super.completionFor(context, next, acceptor);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Inside Domain body: suggest missing scalar properties
|
|
191
|
+
if (ast.isDomain(container)) {
|
|
192
|
+
this.addDomainCompletions(container, acceptor, context);
|
|
193
|
+
super.completionFor(context, next, acceptor);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Inside ContextMap body: relationships and contains
|
|
198
|
+
if (ast.isContextMap(container)) {
|
|
199
|
+
this.addContextMapCompletions(container, acceptor, context);
|
|
200
|
+
super.completionFor(context, next, acceptor);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Inside DomainMap body: contains
|
|
205
|
+
if (ast.isDomainMap(container)) {
|
|
206
|
+
this.addDomainMapCompletions(container, acceptor, context);
|
|
207
|
+
super.completionFor(context, next, acceptor);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (ast.isRelationship(node) || ast.isRelationship(container)) {
|
|
212
|
+
this.addRelationshipCompletions(acceptor, context);
|
|
213
|
+
super.completionFor(context, next, acceptor);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Top level container (Model or NamespaceDeclaration): all top-level constructs
|
|
218
|
+
if (ast.isModel(container) || ast.isNamespaceDeclaration(container)) {
|
|
219
|
+
this.addTopLevelSnippets(acceptor, context);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Let Langium handle default completions
|
|
223
|
+
super.completionFor(context, next, acceptor);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private addTopLevelSnippets(acceptor: CompletionAcceptor, context: CompletionContext): void {
|
|
227
|
+
for (const snippet of TOP_LEVEL_SNIPPETS) {
|
|
228
|
+
acceptor(context, {
|
|
229
|
+
label: snippet.label,
|
|
230
|
+
kind: snippet.kind,
|
|
231
|
+
insertText: snippet.insertText,
|
|
232
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
233
|
+
documentation: snippet.documentation,
|
|
234
|
+
sortText: snippet.sortText
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Add property/collection completions for BoundedContext.
|
|
241
|
+
*/
|
|
242
|
+
private addBoundedContextCompletions(
|
|
243
|
+
node: ast.BoundedContext,
|
|
244
|
+
acceptor: CompletionAcceptor,
|
|
245
|
+
context: CompletionContext
|
|
246
|
+
): void {
|
|
247
|
+
if (!node.description) {
|
|
248
|
+
acceptor(context, {
|
|
249
|
+
label: '⚡ description',
|
|
250
|
+
kind: CompletionItemKind.Snippet,
|
|
251
|
+
insertText: 'description: "${1:Description}"',
|
|
252
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
253
|
+
documentation: '📝 Snippet: Describe the bounded context\'s responsibility',
|
|
254
|
+
sortText: '0_snippet_description'
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (node.team.length === 0) {
|
|
259
|
+
acceptor(context, {
|
|
260
|
+
label: '⚡ team',
|
|
261
|
+
kind: CompletionItemKind.Snippet,
|
|
262
|
+
insertText: 'team: ${1:TeamName}',
|
|
263
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
264
|
+
documentation: '📝 Snippet: Assign the responsible team',
|
|
265
|
+
sortText: '0_snippet_team'
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (node.classification.length === 0) {
|
|
270
|
+
acceptor(context, {
|
|
271
|
+
label: '⚡ classification',
|
|
272
|
+
kind: CompletionItemKind.Snippet,
|
|
273
|
+
insertText: 'classification: ${1:Core}',
|
|
274
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
275
|
+
documentation: '📝 Snippet: Assign the strategic classification (Core, Supporting, Generic)',
|
|
276
|
+
sortText: '0_snippet_classification'
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!node.businessModel) {
|
|
281
|
+
acceptor(context, {
|
|
282
|
+
label: '⚡ businessModel',
|
|
283
|
+
kind: CompletionItemKind.Snippet,
|
|
284
|
+
insertText: 'businessModel: ${1:Commercial}',
|
|
285
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
286
|
+
documentation: '📝 Snippet: Classify the business model',
|
|
287
|
+
sortText: '0_snippet_businessModel'
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!node.evolution) {
|
|
292
|
+
acceptor(context, {
|
|
293
|
+
label: '⚡ evolution',
|
|
294
|
+
kind: CompletionItemKind.Snippet,
|
|
295
|
+
insertText: 'evolution: ${1:Product}',
|
|
296
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
297
|
+
documentation: '📝 Snippet: Define the evolution stage (Genesis, Custom, Product, Commodity)',
|
|
298
|
+
sortText: '0_snippet_evolution'
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (node.terminology.length === 0) {
|
|
303
|
+
acceptor(context, {
|
|
304
|
+
label: '⚡ terminology',
|
|
305
|
+
kind: CompletionItemKind.Snippet,
|
|
306
|
+
insertText: [
|
|
307
|
+
'terminology {',
|
|
308
|
+
'\tterm ${1:Term}: "${2:Definition}"',
|
|
309
|
+
'}'
|
|
310
|
+
].join('\n'),
|
|
311
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
312
|
+
documentation: '📝 Snippet: Define ubiquitous language terms',
|
|
313
|
+
sortText: '0_snippet_terminology'
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (node.decisions.length === 0) {
|
|
318
|
+
acceptor(context, {
|
|
319
|
+
label: '⚡ decisions',
|
|
320
|
+
kind: CompletionItemKind.Snippet,
|
|
321
|
+
insertText: [
|
|
322
|
+
'decisions {',
|
|
323
|
+
'\tdecision [${1|technical,business|}] ${2:DecisionName}: "${3:Rationale}"',
|
|
324
|
+
'}'
|
|
325
|
+
].join('\n'),
|
|
326
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
327
|
+
documentation: '📝 Snippet: Document architectural decisions',
|
|
328
|
+
sortText: '0_snippet_decisions'
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (node.relationships.length === 0) {
|
|
333
|
+
acceptor(context, {
|
|
334
|
+
label: '⚡ relationships',
|
|
335
|
+
kind: CompletionItemKind.Snippet,
|
|
336
|
+
insertText: [
|
|
337
|
+
'relationships {',
|
|
338
|
+
'\t${1:Context1} -> ${2:Context2}',
|
|
339
|
+
'}'
|
|
340
|
+
].join('\n'),
|
|
341
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
342
|
+
documentation: '📝 Snippet: Define relationships with other bounded contexts',
|
|
343
|
+
sortText: '0_snippet_relationships'
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (node.metadata.length === 0) {
|
|
348
|
+
acceptor(context, {
|
|
349
|
+
label: '⚡ metadata',
|
|
350
|
+
kind: CompletionItemKind.Snippet,
|
|
351
|
+
insertText: [
|
|
352
|
+
'metadata {',
|
|
353
|
+
'\t${1:Language}: "${2:TypeScript}"',
|
|
354
|
+
'}'
|
|
355
|
+
].join('\n'),
|
|
356
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
357
|
+
documentation: '📝 Snippet: Add metadata key-value pairs',
|
|
358
|
+
sortText: '0_snippet_metadata'
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Add property completions for Domain.
|
|
365
|
+
*/
|
|
366
|
+
private addDomainCompletions(
|
|
367
|
+
node: ast.Domain,
|
|
368
|
+
acceptor: CompletionAcceptor,
|
|
369
|
+
context: CompletionContext
|
|
370
|
+
): void {
|
|
371
|
+
if (!node.description) {
|
|
372
|
+
acceptor(context, {
|
|
373
|
+
label: '⚡ description',
|
|
374
|
+
kind: CompletionItemKind.Snippet,
|
|
375
|
+
insertText: 'description: "${1:Description}"',
|
|
376
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
377
|
+
documentation: '📝 Snippet: Describe what this domain encompasses',
|
|
378
|
+
sortText: '0_snippet_description'
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (!node.vision) {
|
|
383
|
+
acceptor(context, {
|
|
384
|
+
label: '⚡ vision',
|
|
385
|
+
kind: CompletionItemKind.Snippet,
|
|
386
|
+
insertText: 'vision: "${1:Vision statement}"',
|
|
387
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
388
|
+
documentation: '📝 Snippet: Define the strategic vision for this domain',
|
|
389
|
+
sortText: '0_snippet_vision'
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (!node.type) {
|
|
394
|
+
acceptor(context, {
|
|
395
|
+
label: '⚡ type',
|
|
396
|
+
kind: CompletionItemKind.Snippet,
|
|
397
|
+
insertText: 'type: ${1:Core}',
|
|
398
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
399
|
+
documentation: '📝 Snippet: Classify as Core, Supporting, or Generic domain type',
|
|
400
|
+
sortText: '0_snippet_type'
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Add completions for ContextMap.
|
|
407
|
+
* Suggests relationship patterns and context references.
|
|
408
|
+
*/
|
|
409
|
+
private addContextMapCompletions(
|
|
410
|
+
node: ast.ContextMap,
|
|
411
|
+
acceptor: CompletionAcceptor,
|
|
412
|
+
context: CompletionContext
|
|
413
|
+
): void {
|
|
414
|
+
// Suggest contains if no contexts yet
|
|
415
|
+
if (node.boundedContexts.length === 0) {
|
|
416
|
+
acceptor(context, {
|
|
417
|
+
label: 'contains',
|
|
418
|
+
kind: CompletionItemKind.Keyword,
|
|
419
|
+
insertText: 'contains ${1:Context1}, ${2:Context2}',
|
|
420
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
421
|
+
documentation: 'Add bounded contexts to this map',
|
|
422
|
+
sortText: '0_contains'
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Always suggest relationship snippet
|
|
427
|
+
acceptor(context, {
|
|
428
|
+
label: 'relationship (simple)',
|
|
429
|
+
kind: CompletionItemKind.Snippet,
|
|
430
|
+
insertText: '${1:Context1} -> ${2:Context2}',
|
|
431
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
432
|
+
documentation: 'Add a simple upstream-downstream relationship',
|
|
433
|
+
sortText: '1_relationship_simple'
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
acceptor(context, {
|
|
437
|
+
label: 'relationship (with patterns)',
|
|
438
|
+
kind: CompletionItemKind.Snippet,
|
|
439
|
+
insertText: '[${1|OHS,PL,ACL,CF,P,SK|}] ${2:Context1} -> [${3|OHS,PL,ACL,CF,P,SK|}] ${4:Context2}',
|
|
440
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
441
|
+
documentation: 'Add a relationship with integration patterns',
|
|
442
|
+
sortText: '1_relationship_patterns'
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Add completions for DomainMap.
|
|
448
|
+
* Suggests domain references.
|
|
449
|
+
*/
|
|
450
|
+
private addDomainMapCompletions(
|
|
451
|
+
node: ast.DomainMap,
|
|
452
|
+
acceptor: CompletionAcceptor,
|
|
453
|
+
context: CompletionContext
|
|
454
|
+
): void {
|
|
455
|
+
// Suggest contains if no domains yet
|
|
456
|
+
if (node.domains.length === 0) {
|
|
457
|
+
acceptor(context, {
|
|
458
|
+
label: 'contains',
|
|
459
|
+
kind: CompletionItemKind.Keyword,
|
|
460
|
+
insertText: 'contains ${1:Domain1}, ${2:Domain2}',
|
|
461
|
+
insertTextFormat: InsertTextFormat.Snippet,
|
|
462
|
+
documentation: 'Add domains to this map',
|
|
463
|
+
sortText: '0_contains'
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Add completions for relationships.
|
|
470
|
+
* Provides integration patterns and relationship types.
|
|
471
|
+
*/
|
|
472
|
+
private addRelationshipCompletions(
|
|
473
|
+
acceptor: CompletionAcceptor,
|
|
474
|
+
context: CompletionContext
|
|
475
|
+
): void {
|
|
476
|
+
// Integration pattern completions
|
|
477
|
+
const patterns = [
|
|
478
|
+
{ label: 'OHS (Open Host Service)', insertText: '[OHS]', doc: 'Open Host Service pattern' },
|
|
479
|
+
{ label: 'PL (Published Language)', insertText: '[PL]', doc: 'Published Language pattern' },
|
|
480
|
+
{ label: 'ACL (Anti-Corruption Layer)', insertText: '[ACL]', doc: 'Anti-Corruption Layer pattern' },
|
|
481
|
+
{ label: 'CF (Conformist)', insertText: '[CF]', doc: 'Conformist pattern' },
|
|
482
|
+
{ label: 'P (Partnership)', insertText: '[P]', doc: 'Partnership pattern' },
|
|
483
|
+
{ label: 'SK (Shared Kernel)', insertText: '[SK]', doc: 'Shared Kernel pattern' }
|
|
484
|
+
];
|
|
485
|
+
|
|
486
|
+
for (const pattern of patterns) {
|
|
487
|
+
acceptor(context, {
|
|
488
|
+
label: pattern.label,
|
|
489
|
+
kind: CompletionItemKind.EnumMember,
|
|
490
|
+
insertText: pattern.insertText,
|
|
491
|
+
documentation: pattern.doc,
|
|
492
|
+
sortText: `0_${pattern.label}`
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Relationship arrow completions
|
|
497
|
+
const arrows = [
|
|
498
|
+
{ label: '->', doc: 'Upstream to downstream' },
|
|
499
|
+
{ label: '<-', doc: 'Downstream to upstream' },
|
|
500
|
+
{ label: '<->', doc: 'Bidirectional/Partnership' },
|
|
501
|
+
{ label: '><', doc: 'Separate Ways' }
|
|
502
|
+
];
|
|
503
|
+
|
|
504
|
+
for (const arrow of arrows) {
|
|
505
|
+
acceptor(context, {
|
|
506
|
+
label: arrow.label,
|
|
507
|
+
kind: CompletionItemKind.Operator,
|
|
508
|
+
insertText: arrow.label,
|
|
509
|
+
documentation: arrow.doc,
|
|
510
|
+
sortText: `1_${arrow.label}`
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AstNode } from "langium";
|
|
2
|
+
import { AbstractFormatter, Formatting } from "langium/lsp";
|
|
3
|
+
import * as ast from '../generated/ast.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Provides formatting for DomainLang documents.
|
|
7
|
+
* Handles indentation and newlines for block-based constructs.
|
|
8
|
+
*/
|
|
9
|
+
export class DomainLangFormatter extends AbstractFormatter {
|
|
10
|
+
|
|
11
|
+
protected format(node: AstNode): void {
|
|
12
|
+
// Namespace declarations
|
|
13
|
+
if (ast.isNamespaceDeclaration(node)) {
|
|
14
|
+
this.formatBlock(node);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Domain declarations
|
|
18
|
+
if (ast.isDomain(node)) {
|
|
19
|
+
this.formatBlock(node);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Bounded contexts
|
|
23
|
+
if (ast.isBoundedContext(node)) {
|
|
24
|
+
this.formatBlock(node);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Context maps
|
|
28
|
+
if (ast.isContextMap(node)) {
|
|
29
|
+
this.formatBlock(node);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Domain maps
|
|
33
|
+
if (ast.isDomainMap(node)) {
|
|
34
|
+
this.formatBlock(node);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Formats a block node with standard indentation and newlines.
|
|
40
|
+
*
|
|
41
|
+
* @param node - The AST node to format
|
|
42
|
+
*/
|
|
43
|
+
private formatBlock(node: AstNode): void {
|
|
44
|
+
const formatter = this.getNodeFormatter(node);
|
|
45
|
+
const open = formatter.keyword('{');
|
|
46
|
+
const close = formatter.keyword('}');
|
|
47
|
+
|
|
48
|
+
formatter.interior(open, close).prepend(Formatting.indent());
|
|
49
|
+
close.prepend(Formatting.newLine());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/******************************************************************************
|
|
2
|
+
* Copyright 2021 TypeFox GmbH
|
|
3
|
+
* This program and the accompanying materials are made available under the
|
|
4
|
+
* terms of the MIT License, which is available in the project root.
|
|
5
|
+
******************************************************************************/
|
|
6
|
+
|
|
7
|
+
// domain-lang-naming.ts
|
|
8
|
+
// Provides utilities for generating fully qualified names (FQN) for domain language elements, supporting nested namespaces and disambiguation.
|
|
9
|
+
|
|
10
|
+
import type { Container, NamespaceDeclaration } from '../generated/ast.js';
|
|
11
|
+
import { isModel, isNamespaceDeclaration } from '../generated/ast.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Joins parent and child names into a fully qualified name, using '.' as a separator.
|
|
15
|
+
* Ensures no leading or trailing dots and supports arbitrary nesting.
|
|
16
|
+
* @param parent - The parent name or empty string
|
|
17
|
+
* @param child - The child name
|
|
18
|
+
* @returns The fully qualified name
|
|
19
|
+
*/
|
|
20
|
+
export function joinQualifiedName(parent: string, child: string): string {
|
|
21
|
+
return parent ? `${parent}.${child}` : child;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Recursively computes the fully qualified name for a child element within nested namespaces.
|
|
26
|
+
* @param namespace - The current namespace declaration
|
|
27
|
+
* @param childName - The name of the child element
|
|
28
|
+
* @returns The fully qualified name as a string
|
|
29
|
+
*/
|
|
30
|
+
export function toQualifiedName(namespace: NamespaceDeclaration, childName: string): string {
|
|
31
|
+
return isNamespaceDeclaration(namespace.$container)
|
|
32
|
+
? joinQualifiedName(toQualifiedName(namespace.$container, namespace.name), childName)
|
|
33
|
+
: joinQualifiedName(namespace.name, childName);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Provides qualified name computation for domain language elements, supporting nested namespaces and models.
|
|
38
|
+
* Used for FQN disambiguation and reference resolution.
|
|
39
|
+
*/
|
|
40
|
+
export class QualifiedNameProvider {
|
|
41
|
+
/**
|
|
42
|
+
* Computes the qualified name for a given qualifier and name.
|
|
43
|
+
* @param qualifier - The parent container (Model, NamespaceDeclaration, or string)
|
|
44
|
+
* @param name - The simple name of the element
|
|
45
|
+
* @returns The fully qualified name as a string
|
|
46
|
+
*/
|
|
47
|
+
getQualifiedName(qualifier: Container | string, name: string): string {
|
|
48
|
+
let prefix = isModel(qualifier) ? '' : qualifier;
|
|
49
|
+
if (isNamespaceDeclaration(prefix)) {
|
|
50
|
+
prefix = isNamespaceDeclaration(prefix.$container)
|
|
51
|
+
? this.getQualifiedName(prefix.$container, prefix.name)
|
|
52
|
+
: prefix.name;
|
|
53
|
+
}
|
|
54
|
+
return prefix ? `${prefix}.${name}` : name;
|
|
55
|
+
}
|
|
56
|
+
}
|