Skip to content

Commit 3b277cf

Browse files
akoclaude
andcommitted
feat: add ALTER ENTITY SET POSITION (x, y) for domain model layout
Allows repositioning entities on the domain model canvas without recreating them: ALTER ENTITY Module.Customer SET POSITION (100, 200); Wired through grammar, AST, visitor, and executor. Uses the existing UpdateEntity writer method to update the Location field. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f2cab5c commit 3b277cf

File tree

15 files changed

+5796
-5533
lines changed

15 files changed

+5796
-5533
lines changed

.claude/skills/mendix/generate-domain-model.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,9 +810,13 @@ ALTER ENTITY Module.Customer
810810
-- Add an index
811811
ALTER ENTITY Module.Customer
812812
ADD INDEX idx_email (Email ASC);
813+
814+
-- Reposition entity on domain model canvas
815+
ALTER ENTITY Module.Customer
816+
SET POSITION (100, 200);
813817
```
814818

815-
**Supported operations:** ADD ATTRIBUTE, RENAME ATTRIBUTE, MODIFY ATTRIBUTE, DROP ATTRIBUTE, SET DOCUMENTATION, SET COMMENT, ADD INDEX, DROP INDEX.
819+
**Supported operations:** ADD ATTRIBUTE, RENAME ATTRIBUTE, MODIFY ATTRIBUTE, DROP ATTRIBUTE, SET DOCUMENTATION, SET COMMENT, ADD INDEX, DROP INDEX, SET POSITION.
816820

817821
### Entity Migration with CREATE OR MODIFY
818822

docs-site/src/appendixes/quick-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Modifies an existing entity without full replacement.
4747
| Add index | `ALTER ENTITY Module.Name ADD INDEX (Col1 [ASC\|DESC], ...);` | |
4848
| Drop index | `ALTER ENTITY Module.Name DROP INDEX (Col1, ...);` | |
4949
| Set documentation | `ALTER ENTITY Module.Name SET DOCUMENTATION 'text';` | |
50+
| Set position | `ALTER ENTITY Module.Name SET POSITION (100, 200);` | Canvas position |
5051

5152
**Example:**
5253
```sql

docs-site/src/language/alter-entity.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ ALTER ENTITY <Module>.<Entity>
112112

113113
ALTER ENTITY <Module>.<Entity>
114114
SET DOCUMENTATION '<text>'
115+
116+
ALTER ENTITY <Module>.<Entity>
117+
SET POSITION (<x>, <y>)
115118
```
116119

117120
## See Also

docs-site/src/reference/domain-model/alter-entity.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ ALTER ENTITY Sales.Customer
115115
SET DOCUMENTATION 'Customer master data for the sales module.';
116116
```
117117

118+
### Set position
119+
120+
Reposition an entity on the domain model canvas:
121+
122+
```sql
123+
ALTER ENTITY Sales.Customer
124+
SET POSITION (100, 200);
125+
```
126+
118127
## Notes
119128

120129
- Each `ALTER ENTITY` statement performs a single operation. Chain multiple statements for multiple changes.

docs/01-project/MDL_QUICK_REFERENCE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Modifies an existing entity without full replacement.
4949
| Add index | `ALTER ENTITY Module.Name ADD INDEX (Col1 [ASC\|DESC], ...);` | |
5050
| Drop index | `ALTER ENTITY Module.Name DROP INDEX (Col1, ...);` | |
5151
| Set documentation | `ALTER ENTITY Module.Name SET DOCUMENTATION 'text';` | |
52+
| Set position | `ALTER ENTITY Module.Name SET POSITION (100, 200);` | Canvas position |
5253

5354
**Example:**
5455
```sql

docs/05-mdl-specification/01-language-reference.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,9 @@ ALTER ENTITY <qualified-name>
524524

525525
ALTER ENTITY <qualified-name>
526526
SET DOCUMENTATION '<text>'
527+
528+
ALTER ENTITY <qualified-name>
529+
SET POSITION (<x>, <y>)
527530
```
528531

529532
**Examples:**
@@ -547,6 +550,10 @@ ALTER ENTITY Sales.Customer
547550
-- Set documentation
548551
ALTER ENTITY Sales.Customer
549552
SET DOCUMENTATION 'Customer master data';
553+
554+
-- Reposition entity on domain model canvas
555+
ALTER ENTITY Sales.Customer
556+
SET POSITION (100, 200);
550557
```
551558

552559
---

docs/06-mdl-reference/grammar-reference.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2883,6 +2883,8 @@ alterEntityAction
28832883
| | DROP COLUMN IDENTIFIER
28842884
| | SET DOCUMENTATION STRING_LITERAL
28852885
| | SET COMMENT STRING_LITERAL
2886+
| | SET STORE OWNER
2887+
| | SET POSITION LPAREN NUMBER_LITERAL COMMA NUMBER_LITERAL RPAREN
28862888
| | ADD INDEX indexDefinition
28872889
| | DROP INDEX IDENTIFIER
28882890
```
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Proposal: Entity Positioning in ALTER ENTITY
2+
3+
## Problem
4+
5+
Entity positions in the domain model canvas can only be set during `CREATE ENTITY` via the `@position` annotation. There's no way to reposition existing entities without dropping and recreating them — which destroys associations, access rules, and references.
6+
7+
This matters for:
8+
- **Scripts that create multiple related entities** — auto-positioning stacks them linearly, making the domain model hard to read in Studio Pro
9+
- **Domain model cleanup** — reorganizing entity layout after schema evolution
10+
- **Auto-layout** — enabling tools to compute optimal positions and apply them in a single pass
11+
12+
## Proposed Syntax
13+
14+
### Single entity
15+
16+
```sql
17+
ALTER ENTITY Module.Customer SET POSITION (100, 200);
18+
```
19+
20+
Consistent with existing `ALTER ENTITY ... SET DOCUMENTATION '...'` and `SET COMMENT '...'` patterns.
21+
22+
### Multiple entities (batch repositioning)
23+
24+
```sql
25+
ALTER ENTITY Module.Customer SET POSITION (100, 100);
26+
ALTER ENTITY Module.Order SET POSITION (400, 100);
27+
ALTER ENTITY Module.Product SET POSITION (400, 300);
28+
```
29+
30+
### Auto-layout command (future extension)
31+
32+
A separate top-level command for automatic layout of all entities in a module:
33+
34+
```sql
35+
ARRANGE DOMAIN MODEL IN Module;
36+
```
37+
38+
This is out of scope for the initial implementation but the grammar should not conflict with it.
39+
40+
## Grammar Change
41+
42+
Add one alternative to the `alterEntityAction` rule:
43+
44+
```ebnf
45+
alterEntityAction
46+
: ...existing alternatives...
47+
| SET POSITION LPAREN NUMBER_LITERAL COMMA NUMBER_LITERAL RPAREN
48+
;
49+
```
50+
51+
`POSITION` is already a keyword in the lexer (used by `@position` annotations and notebook actions).
52+
53+
## AST Change
54+
55+
Add a new operation constant and position field to `AlterEntityStmt`:
56+
57+
```go
58+
const (
59+
...
60+
AlterEntitySetPosition // SET POSITION (x, y)
61+
)
62+
63+
type AlterEntityStmt struct {
64+
...
65+
Position *Position // For SET POSITION
66+
}
67+
```
68+
69+
## Executor Change
70+
71+
In `cmd_entities.go`, handle `AlterEntitySetPosition`:
72+
73+
1. Find the entity in the domain model
74+
2. Update `entity.Location = model.Point{X: s.Position.X, Y: s.Position.Y}`
75+
3. Write the updated entity back via `writer.UpdateEntityLocation(entityID, location)`
76+
77+
This is a lightweight operation — it only updates the `Location` field in the BSON, no structural changes.
78+
79+
## DESCRIBE Output
80+
81+
`DESCRIBE ENTITY` should include a `@position` annotation when the entity has a non-default position, so the output is round-trippable:
82+
83+
```sql
84+
@position(100, 200)
85+
CREATE OR REPLACE PERSISTENT ENTITY Module.Customer (
86+
Name: String(100),
87+
...
88+
);
89+
```
90+
91+
Currently DESCRIBE omits the `@position` annotation. This should be added regardless of whether `ALTER ENTITY SET POSITION` is implemented, since it improves roundtrip fidelity.
92+
93+
## Implementation Scope
94+
95+
| Component | Change |
96+
|-----------|--------|
97+
| `MDLParser.g4` | Add `SET POSITION (x, y)` to `alterEntityAction` |
98+
| `MDLLexer.g4` | No change (`POSITION` already exists) |
99+
| `mdl/ast/ast_entity.go` | Add `AlterEntitySetPosition` op, `Position` field |
100+
| `mdl/visitor/visitor_entity.go` | Parse the new alternative |
101+
| `mdl/executor/cmd_entities.go` | Handle `AlterEntitySetPosition` |
102+
| `sdk/mpr/writer.go` | Add `UpdateEntityLocation()` (if not already present) |
103+
| DESCRIBE output | Add `@position` annotation |
104+
105+
## Alternatives Considered
106+
107+
**`@position` annotation on ALTER ENTITY** — e.g., `@position(100,200) ALTER ENTITY ...`. Rejected because annotations are a CREATE-time concept; SET is the established ALTER pattern.
108+
109+
**Dedicated MOVE POSITION command** — e.g., `MOVE ENTITY Module.E TO POSITION (x, y)`. Rejected because MOVE already means "move to different module" in MDL.
110+
111+
**ARRANGE command only** — Skip per-entity positioning, just auto-layout. Insufficient for scripts that need precise control (e.g., aligning entities in a specific pattern).

mdl-examples/doctype-tests/01-domain-model-examples.mdl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,10 @@ ALTER ENTITY DmTest.VATRate
14451445
ALTER ENTITY DmTest.VATRate
14461446
ADD INDEX (TaxRange ASC);
14471447

1448+
-- Reposition entity on domain model canvas
1449+
ALTER ENTITY DmTest.VATRate
1450+
SET POSITION (300, 400);
1451+
14481452
-- ============================================================================
14491453
-- PART 11: COMPLEX VIEW ENTITIES WITH SUBQUERIES
14501454
-- ============================================================================

mdl/ast/ast_entity.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const (
6666
AlterEntityAddIndex // ADD INDEX
6767
AlterEntityDropIndex // DROP INDEX
6868
AlterEntitySetStoreOwner // SET STORE OWNER
69+
AlterEntitySetPosition // SET POSITION (x, y)
6970
)
7071

7172
// AlterEntityStmt represents: ALTER ENTITY Module.Name ADD/DROP/RENAME/MODIFY ATTRIBUTE ...
@@ -82,6 +83,7 @@ type AlterEntityStmt struct {
8283
Comment string // For SET COMMENT
8384
Index *Index // For ADD INDEX
8485
IndexName string // For DROP INDEX
86+
Position *Position // For SET POSITION
8587
}
8688

8789
func (s *AlterEntityStmt) isStatement() {}

0 commit comments

Comments
 (0)