@domainlang/language 0.10.0 → 0.12.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.
Files changed (87) hide show
  1. package/README.md +44 -102
  2. package/out/diagram/context-map-diagram-generator.d.ts +65 -0
  3. package/out/diagram/context-map-diagram-generator.js +356 -0
  4. package/out/diagram/context-map-diagram-generator.js.map +1 -0
  5. package/out/diagram/context-map-layout-configurator.d.ts +15 -0
  6. package/out/diagram/context-map-layout-configurator.js +39 -0
  7. package/out/diagram/context-map-layout-configurator.js.map +1 -0
  8. package/out/diagram/elk-layout-factory.d.ts +43 -0
  9. package/out/diagram/elk-layout-factory.js +64 -0
  10. package/out/diagram/elk-layout-factory.js.map +1 -0
  11. package/out/domain-lang-module.d.ts +9 -2
  12. package/out/domain-lang-module.js +13 -4
  13. package/out/domain-lang-module.js.map +1 -1
  14. package/out/index.d.ts +6 -0
  15. package/out/index.js +7 -0
  16. package/out/index.js.map +1 -1
  17. package/out/lsp/domain-lang-code-lens-provider.d.ts +8 -0
  18. package/out/lsp/domain-lang-code-lens-provider.js +48 -0
  19. package/out/lsp/domain-lang-code-lens-provider.js.map +1 -0
  20. package/out/lsp/domain-lang-completion.js +1 -1
  21. package/out/lsp/domain-lang-completion.js.map +1 -1
  22. package/out/lsp/domain-lang-document-symbol-provider.js +5 -5
  23. package/out/lsp/domain-lang-document-symbol-provider.js.map +1 -1
  24. package/out/lsp/domain-lang-index-manager.d.ts +149 -5
  25. package/out/lsp/domain-lang-index-manager.js +388 -52
  26. package/out/lsp/domain-lang-index-manager.js.map +1 -1
  27. package/out/lsp/domain-lang-refresh.d.ts +35 -0
  28. package/out/lsp/domain-lang-refresh.js +129 -0
  29. package/out/lsp/domain-lang-refresh.js.map +1 -0
  30. package/out/lsp/domain-lang-workspace-manager.d.ts +10 -0
  31. package/out/lsp/domain-lang-workspace-manager.js +35 -0
  32. package/out/lsp/domain-lang-workspace-manager.js.map +1 -1
  33. package/out/lsp/tool-handlers.js +63 -57
  34. package/out/lsp/tool-handlers.js.map +1 -1
  35. package/out/main.js +30 -190
  36. package/out/main.js.map +1 -1
  37. package/out/sdk/loader-node.js +1 -1
  38. package/out/sdk/loader-node.js.map +1 -1
  39. package/out/sdk/validator.js +17 -14
  40. package/out/sdk/validator.js.map +1 -1
  41. package/out/services/import-resolver.d.ts +67 -17
  42. package/out/services/import-resolver.js +146 -65
  43. package/out/services/import-resolver.js.map +1 -1
  44. package/out/services/lsp-logger.d.ts +42 -0
  45. package/out/services/lsp-logger.js +50 -0
  46. package/out/services/lsp-logger.js.map +1 -0
  47. package/out/services/lsp-runtime-settings.d.ts +20 -0
  48. package/out/services/lsp-runtime-settings.js +20 -0
  49. package/out/services/lsp-runtime-settings.js.map +1 -0
  50. package/out/services/performance-optimizer.d.ts +9 -9
  51. package/out/services/performance-optimizer.js +17 -41
  52. package/out/services/performance-optimizer.js.map +1 -1
  53. package/out/services/workspace-manager.d.ts +22 -1
  54. package/out/services/workspace-manager.js +57 -9
  55. package/out/services/workspace-manager.js.map +1 -1
  56. package/out/utils/import-utils.js +6 -6
  57. package/out/utils/import-utils.js.map +1 -1
  58. package/out/validation/constants.d.ts +6 -0
  59. package/out/validation/constants.js +7 -0
  60. package/out/validation/constants.js.map +1 -1
  61. package/out/validation/import.d.ts +13 -3
  62. package/out/validation/import.js +54 -10
  63. package/out/validation/import.js.map +1 -1
  64. package/package.json +5 -2
  65. package/src/diagram/context-map-diagram-generator.ts +451 -0
  66. package/src/diagram/context-map-layout-configurator.ts +43 -0
  67. package/src/diagram/elk-layout-factory.ts +83 -0
  68. package/src/domain-lang-module.ts +22 -5
  69. package/src/index.ts +8 -0
  70. package/src/lsp/domain-lang-code-lens-provider.ts +54 -0
  71. package/src/lsp/domain-lang-completion.ts +3 -3
  72. package/src/lsp/domain-lang-document-symbol-provider.ts +5 -5
  73. package/src/lsp/domain-lang-index-manager.ts +438 -56
  74. package/src/lsp/domain-lang-refresh.ts +205 -0
  75. package/src/lsp/domain-lang-workspace-manager.ts +45 -0
  76. package/src/lsp/tool-handlers.ts +61 -47
  77. package/src/main.ts +36 -244
  78. package/src/sdk/loader-node.ts +1 -1
  79. package/src/sdk/validator.ts +17 -13
  80. package/src/services/import-resolver.ts +196 -89
  81. package/src/services/lsp-logger.ts +89 -0
  82. package/src/services/lsp-runtime-settings.ts +34 -0
  83. package/src/services/performance-optimizer.ts +18 -57
  84. package/src/services/workspace-manager.ts +62 -10
  85. package/src/utils/import-utils.ts +6 -6
  86. package/src/validation/constants.ts +9 -0
  87. package/src/validation/import.ts +67 -12
@@ -0,0 +1,43 @@
1
+ import type { LayoutOptions } from 'elkjs/lib/elk-api.js';
2
+ import { DefaultLayoutConfigurator } from 'sprotty-elk/lib/elk-layout.js';
3
+ import type { SGraph, SNode, SModelIndex, SEdge } from 'sprotty-protocol';
4
+
5
+ /**
6
+ * ELK layout configurator for DomainLang context map diagrams.
7
+ *
8
+ * Uses the `layered` algorithm with **DOWN** direction (top-to-bottom) so
9
+ * upstream contexts appear above downstream contexts, matching the conventional
10
+ * DDD Context Map layout direction.
11
+ */
12
+ export class ContextMapLayoutConfigurator extends DefaultLayoutConfigurator {
13
+ protected override graphOptions(_graph: SGraph, _index: SModelIndex): LayoutOptions {
14
+ return {
15
+ 'elk.algorithm': 'layered',
16
+ 'elk.direction': 'DOWN',
17
+ 'elk.layered.nodePlacement.strategy': 'BRANDES_KOEPF',
18
+ 'elk.spacing.nodeNode': '100',
19
+ 'elk.layered.spacing.nodeNodeBetweenLayers': '140',
20
+ 'elk.spacing.edgeNode': '60',
21
+ 'elk.spacing.edgeEdge': '40',
22
+ // Edge routing mode is irrelevant — the webview SmoothBezierEdgeView
23
+ // ignores ELK routing points and computes dynamic bezier curves
24
+ // anchored directly to node ellipses with obstacle avoidance.
25
+ 'elk.edgeRouting': 'POLYLINE',
26
+ 'elk.layered.mergeEdges': 'false',
27
+ 'elk.layered.crossingMinimization.strategy': 'LAYER_SWEEP',
28
+ 'elk.layered.thoroughness': '7',
29
+ };
30
+ }
31
+
32
+ protected override nodeOptions(_node: SNode, _index: SModelIndex): LayoutOptions {
33
+ return {
34
+ 'elk.layered.nodePlacement.bk.fixedAlignment': 'BALANCED',
35
+ };
36
+ }
37
+
38
+ protected override edgeOptions(_edge: SEdge, _index: SModelIndex): LayoutOptions {
39
+ return {
40
+ 'elk.layered.priority.direction': '1',
41
+ };
42
+ }
43
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * ELK layout engine factory for DomainLang diagrams.
3
+ *
4
+ * Encapsulates the ELK construction, factory wiring, and Sprotty-ELK layout
5
+ * engine instantiation so that both the VS Code extension (via the LSP server
6
+ * DI module) and the CLI (for standalone diagram export) can reuse the same
7
+ * layout logic without embedding the setup inline.
8
+ *
9
+ * @module diagram/elk-layout-factory
10
+ */
11
+ import * as ElkBundled from 'elkjs/lib/elk.bundled.js';
12
+ import type { ELK } from 'elkjs/lib/elk-api.js';
13
+ import {
14
+ DefaultElementFilter,
15
+ ElkLayoutEngine,
16
+ type ElkFactory,
17
+ type IElementFilter,
18
+ type ILayoutConfigurator,
19
+ } from 'sprotty-elk/lib/elk-layout.js';
20
+ import type { IModelLayoutEngine } from 'sprotty-protocol';
21
+ import { ContextMapLayoutConfigurator } from './context-map-layout-configurator.js';
22
+
23
+ /** @internal Handles the dual ESM/CJS export shape of the bundled ELK module. */
24
+ type ElkConstructor = new (args?: { algorithms?: string[] }) => ELK;
25
+ const _elkConstructor: ElkConstructor = (
26
+ (ElkBundled as unknown as { default?: ElkConstructor }).default
27
+ ?? (ElkBundled as unknown as ElkConstructor)
28
+ );
29
+
30
+ /**
31
+ * Creates an {@link ElkFactory} that produces a new bundled ELK instance
32
+ * pre-configured for the `layered` algorithm.
33
+ *
34
+ * Use this in Langium's DI module (`layout.ElkFactory`) or directly in the
35
+ * CLI when running the layout engine standalone.
36
+ */
37
+ export function createElkFactory(): ElkFactory {
38
+ return () => new _elkConstructor({ algorithms: ['layered'] });
39
+ }
40
+
41
+ /**
42
+ * Creates the full set of objects required to run the ELK layout engine for
43
+ * DomainLang context-map diagrams.
44
+ *
45
+ * Returns the three collaborators that together constitute a complete
46
+ * `IModelLayoutEngine`:
47
+ * - `elkFactory` — produces the ELK worker instance
48
+ * - `elementFilter` — controls which model elements are laid out
49
+ * - `layoutConfigurator` — supplies algorithm-specific ELK options
50
+ *
51
+ * Callers that use Langium DI should register these individually via the
52
+ * `layout` service group. Callers that operate outside of DI (e.g. the CLI)
53
+ * can call {@link createElkLayoutEngine} for a fully assembled engine.
54
+ */
55
+ export function createElkLayoutComponents(): {
56
+ elkFactory: ElkFactory;
57
+ elementFilter: IElementFilter;
58
+ layoutConfigurator: ILayoutConfigurator;
59
+ } {
60
+ return {
61
+ elkFactory: createElkFactory(),
62
+ elementFilter: new DefaultElementFilter(),
63
+ layoutConfigurator: new ContextMapLayoutConfigurator(),
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Creates a fully assembled {@link IModelLayoutEngine} for DomainLang
69
+ * context-map diagrams using the bundled ELK engine.
70
+ *
71
+ * Intended for use outside of Langium's DI container — for example, in the
72
+ * CLI when generating standalone SVG or image exports.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const layoutEngine = createElkLayoutEngine();
77
+ * const laidOutModel = await layoutEngine.layout(rootModel);
78
+ * ```
79
+ */
80
+ export function createElkLayoutEngine(): IModelLayoutEngine {
81
+ const { elkFactory, elementFilter, layoutConfigurator } = createElkLayoutComponents();
82
+ return new ElkLayoutEngine(elkFactory, elementFilter, layoutConfigurator) as IModelLayoutEngine;
83
+ }
@@ -16,13 +16,18 @@ import { DomainLangFormatter } from './lsp/domain-lang-formatter.js';
16
16
  import { DomainLangHoverProvider } from './lsp/hover/domain-lang-hover.js';
17
17
  import { DomainLangCompletionProvider } from './lsp/domain-lang-completion.js';
18
18
  import { DomainLangCodeActionProvider } from './lsp/domain-lang-code-actions.js';
19
+ import { DomainLangCodeLensProvider } from './lsp/domain-lang-code-lens-provider.js';
19
20
  import { DomainLangNodeKindProvider } from './lsp/domain-lang-node-kind-provider.js';
20
21
  import { DomainLangDocumentSymbolProvider } from './lsp/domain-lang-document-symbol-provider.js';
22
+ import { SprottyDefaultModule, SprottySharedModule } from 'langium-sprotty';
23
+ import type { IDiagramGenerator, IModelLayoutEngine } from 'sprotty-protocol';
21
24
  import { ImportResolver } from './services/import-resolver.js';
22
- import { WorkspaceManager } from './services/workspace-manager.js';
25
+ import { ManifestManager } from './services/workspace-manager.js';
23
26
  import { PackageBoundaryDetector } from './services/package-boundary-detector.js';
24
27
  import { DomainLangWorkspaceManager } from './lsp/domain-lang-workspace-manager.js';
25
28
  import { DomainLangIndexManager } from './lsp/domain-lang-index-manager.js';
29
+ import { DomainLangContextMapDiagramGenerator } from './diagram/context-map-diagram-generator.js';
30
+ import { createElkLayoutEngine } from './diagram/elk-layout-factory.js';
26
31
 
27
32
  /**
28
33
  * Declaration of custom services - add your own service classes here.
@@ -30,7 +35,7 @@ import { DomainLangIndexManager } from './lsp/domain-lang-index-manager.js';
30
35
  export type DomainLangAddedServices = {
31
36
  imports: {
32
37
  ImportResolver: ImportResolver,
33
- WorkspaceManager: WorkspaceManager,
38
+ ManifestManager: ManifestManager,
34
39
  PackageBoundaryDetector: PackageBoundaryDetector
35
40
  },
36
41
  references: {
@@ -40,8 +45,13 @@ export type DomainLangAddedServices = {
40
45
  Formatter: DomainLangFormatter,
41
46
  HoverProvider: DomainLangHoverProvider,
42
47
  CompletionProvider: DomainLangCompletionProvider,
48
+ CodeLensProvider: DomainLangCodeLensProvider,
43
49
  CodeActionProvider: DomainLangCodeActionProvider
44
- }
50
+ },
51
+ diagram: {
52
+ DiagramGenerator: IDiagramGenerator,
53
+ ModelLayoutEngine: IModelLayoutEngine
54
+ },
45
55
  }
46
56
 
47
57
  /**
@@ -68,7 +78,7 @@ const DomainLangSharedModule: Module<LangiumSharedServices, PartialLangiumShared
68
78
  export const DomainLangModule: Module<DomainLangServices, PartialLangiumServices & DomainLangAddedServices> = {
69
79
  imports: {
70
80
  ImportResolver: (services) => new ImportResolver(services),
71
- WorkspaceManager: () => new WorkspaceManager({ autoResolve: false, allowNetwork: false }),
81
+ ManifestManager: () => new ManifestManager({ autoResolve: false, allowNetwork: false }),
72
82
  PackageBoundaryDetector: () => new PackageBoundaryDetector()
73
83
  },
74
84
  references: {
@@ -80,9 +90,14 @@ export const DomainLangModule: Module<DomainLangServices, PartialLangiumServices
80
90
  Formatter: () => new DomainLangFormatter(),
81
91
  HoverProvider: (services) => new DomainLangHoverProvider(services),
82
92
  CompletionProvider: (services) => new DomainLangCompletionProvider(services),
93
+ CodeLensProvider: () => new DomainLangCodeLensProvider(),
83
94
  CodeActionProvider: () => new DomainLangCodeActionProvider(),
84
95
  DocumentSymbolProvider: (services) => new DomainLangDocumentSymbolProvider(services)
85
96
  },
97
+ diagram: {
98
+ DiagramGenerator: (services) => new DomainLangContextMapDiagramGenerator(services as never),
99
+ ModelLayoutEngine: () => createElkLayoutEngine(),
100
+ },
86
101
  };
87
102
 
88
103
  /**
@@ -107,11 +122,13 @@ export function createDomainLangServices(context: DefaultSharedModuleContext): {
107
122
  const shared = inject(
108
123
  createDefaultSharedModule(context),
109
124
  DomainLangGeneratedSharedModule,
110
- DomainLangSharedModule
125
+ DomainLangSharedModule,
126
+ SprottySharedModule as never
111
127
  );
112
128
  const DomainLang = inject(
113
129
  createDefaultModule({ shared }),
114
130
  DomainLangGeneratedModule,
131
+ SprottyDefaultModule as never,
115
132
  DomainLangModule
116
133
  );
117
134
  shared.ServiceRegistry.register(DomainLang);
package/src/index.ts CHANGED
@@ -14,6 +14,8 @@ export * from './services/workspace-manager.js';
14
14
  export * from './services/import-resolver.js';
15
15
  export * from './services/relationship-inference.js';
16
16
  export * from './services/performance-optimizer.js';
17
+ export * from './services/lsp-logger.js';
18
+ export * from './services/lsp-runtime-settings.js';
17
19
 
18
20
  // Export shared utilities
19
21
  export * from './utils/manifest-utils.js';
@@ -28,4 +30,10 @@ export * from './utils/manifest-utils.js';
28
30
  // Export LSP services
29
31
  export * from './lsp/manifest-diagnostics.js';
30
32
  export { DomainLangIndexManager } from './lsp/domain-lang-index-manager.js';
33
+ export { registerDomainLangRefresh, processWatchedFileChanges } from './lsp/domain-lang-refresh.js';
31
34
  export { registerToolHandlers } from './lsp/tool-handlers.js';
35
+
36
+ // Export diagram services (reusable by CLI and extension)
37
+ export { DomainLangContextMapDiagramGenerator } from './diagram/context-map-diagram-generator.js';
38
+ export { ContextMapLayoutConfigurator } from './diagram/context-map-layout-configurator.js';
39
+ export { createElkLayoutEngine, createElkFactory } from './diagram/elk-layout-factory.js';
@@ -0,0 +1,54 @@
1
+ import { AstUtils, type LangiumDocument } from 'langium';
2
+ import type { CodeLens, CodeLensParams } from 'vscode-languageserver';
3
+ import { isContextMap, type ContextMap, type Model } from '../generated/ast.js';
4
+
5
+ const OPEN_DIAGRAM_COMMAND = 'domainlang.diagram.open';
6
+
7
+ /**
8
+ * Provides contextual code lenses for context map diagram actions.
9
+ */
10
+ export class DomainLangCodeLensProvider {
11
+ async provideCodeLens(document: LangiumDocument, _params: CodeLensParams): Promise<CodeLens[]> {
12
+ try {
13
+ const model = (document as LangiumDocument<Model>).parseResult?.value;
14
+ if (!model) {
15
+ return [];
16
+ }
17
+
18
+ const contextMaps: ContextMap[] = [];
19
+
20
+ if (isContextMap(model)) {
21
+ contextMaps.push(model);
22
+ }
23
+
24
+ for (const node of AstUtils.streamAllContents(model)) {
25
+ if (isContextMap(node)) {
26
+ contextMaps.push(node);
27
+ }
28
+ }
29
+
30
+ return contextMaps.flatMap((contextMap) => {
31
+ const range = contextMap.$cstNode?.range;
32
+ if (!range) {
33
+ return [];
34
+ }
35
+
36
+ return [{
37
+ range: {
38
+ start: { line: range.start.line, character: 0 },
39
+ end: { line: range.start.line, character: 0 },
40
+ },
41
+ command: {
42
+ title: 'Open diagram',
43
+ command: OPEN_DIAGRAM_COMMAND,
44
+ arguments: [{
45
+ uri: document.uri.toString(),
46
+ }],
47
+ },
48
+ }];
49
+ });
50
+ } catch {
51
+ return [];
52
+ }
53
+ }
54
+ }
@@ -16,7 +16,7 @@ import { CompletionItemKind, CompletionList, InsertTextFormat, TextEdit } from '
16
16
  import type { CancellationToken, CompletionItem, CompletionParams } from 'vscode-languageserver-protocol';
17
17
  import * as ast from '../generated/ast.js';
18
18
  import type { DomainLangServices } from '../domain-lang-module.js';
19
- import type { WorkspaceManager } from '../services/workspace-manager.js';
19
+ import type { ManifestManager } from '../services/workspace-manager.js';
20
20
  import type { ModelManifest, DependencySpec } from '../services/types.js';
21
21
 
22
22
  /**
@@ -124,7 +124,7 @@ const TOP_LEVEL_SNIPPETS = [
124
124
  ] as const;
125
125
 
126
126
  export class DomainLangCompletionProvider extends DefaultCompletionProvider {
127
- private readonly workspaceManager: WorkspaceManager;
127
+ private readonly workspaceManager: ManifestManager;
128
128
 
129
129
  override readonly completionOptions = {
130
130
  triggerCharacters: ['.']
@@ -132,7 +132,7 @@ export class DomainLangCompletionProvider extends DefaultCompletionProvider {
132
132
 
133
133
  constructor(services: DomainLangServices) {
134
134
  super(services);
135
- this.workspaceManager = services.imports.WorkspaceManager;
135
+ this.workspaceManager = services.imports.ManifestManager;
136
136
  }
137
137
 
138
138
  /**
@@ -147,8 +147,8 @@ export class DomainLangDocumentSymbolProvider extends DefaultDocumentSymbolProvi
147
147
  const cstNode = rel.$cstNode;
148
148
  if (!cstNode) return undefined;
149
149
 
150
- const left = isThisRef(rel.left) ? 'this' : rel.left?.link?.ref?.name ?? '?';
151
- const right = isThisRef(rel.right) ? 'this' : rel.right?.link?.ref?.name ?? '?';
150
+ const left = isThisRef(rel.left) ? 'this' : rel.left?.link?.$refText ?? '?';
151
+ const right = isThisRef(rel.right) ? 'this' : rel.right?.link?.$refText ?? '?';
152
152
  const name = `${left} → ${right}`;
153
153
 
154
154
  const range = CstUtils.toDocumentSegment(cstNode).range;
@@ -168,7 +168,7 @@ export class DomainLangDocumentSymbolProvider extends DefaultDocumentSymbolProvi
168
168
  const cstNode = meta.$cstNode;
169
169
  if (!cstNode) return undefined;
170
170
 
171
- const name = meta.key?.ref?.name ?? 'unknown';
171
+ const name = meta.key?.$refText ?? 'unknown';
172
172
  const range = CstUtils.toDocumentSegment(cstNode).range;
173
173
 
174
174
  return DocumentSymbol.create(
@@ -197,8 +197,8 @@ export class DomainLangDocumentSymbolProvider extends DefaultDocumentSymbolProvi
197
197
  /** Builds BC detail: "BC for DomainName — description". */
198
198
  private getBcDetail(node: BoundedContext): string | undefined {
199
199
  const parts: string[] = [];
200
- if (node.domain?.ref?.name) {
201
- parts.push(`BC for ${node.domain.ref.name}`);
200
+ if (node.domain?.$refText) {
201
+ parts.push(`BC for ${node.domain.$refText}`);
202
202
  }
203
203
  if (node.description) {
204
204
  parts.push(node.description);