Merge pull request #2498 from alixander/fix-multiple-layer-d2oracle

d2oracle: fix setting in multiple layers
This commit is contained in:
Alexander Wang 2025-04-27 09:26:50 -06:00 committed by GitHub
commit 143d11ca28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 823 additions and 86 deletions

View file

@ -208,55 +208,48 @@ func findFieldAST(ast *d2ast.Map, f *d2ir.Field) *d2ast.Map {
curr = d2ir.ParentField(curr)
}
currAST := ast
for len(path) > 0 {
head := path[0]
found := false
for _, n := range currAST.Nodes {
if n.MapKey == nil {
continue
}
if n.MapKey.Key == nil {
continue
}
if len(n.MapKey.Key.Path) != 1 {
continue
}
head2 := n.MapKey.Key.Path[0].Unbox().ScalarString()
if head == head2 {
currAST = n.MapKey.Value.Map
// The BaseAST is only used for making edits to the AST (through d2oracle)
// If there's no Map for a given board, either it's an empty layer or set to an import
// Either way, in order to make edits, it needs to be expanded into a Map to add lines to
if currAST == nil {
n.MapKey.Value.Map = &d2ast.Map{
Range: d2ast.MakeRange(",1:0:0-1:0:0"),
}
if n.MapKey.Value.Import != nil {
imp := &d2ast.Import{
Range: d2ast.MakeRange(",1:0:0-1:0:0"),
Spread: true,
Pre: n.MapKey.Value.Import.Pre,
Path: n.MapKey.Value.Import.Path,
}
n.MapKey.Value.Map.Nodes = append(n.MapKey.Value.Map.Nodes, d2ast.MapNodeBox{
Import: imp,
})
return _findFieldAST(ast, path)
}
}
currAST = n.MapKey.Value.Map
}
found = true
break
}
}
if !found {
return nil
}
path = path[1:]
func _findFieldAST(ast *d2ast.Map, path []string) *d2ast.Map {
if len(path) == 0 {
return ast
}
return currAST
head := path[0]
remainingPath := path[1:]
for i := range ast.Nodes {
if ast.Nodes[i].MapKey == nil || ast.Nodes[i].MapKey.Key == nil || len(ast.Nodes[i].MapKey.Key.Path) != 1 {
continue
}
head2 := ast.Nodes[i].MapKey.Key.Path[0].Unbox().ScalarString()
if head == head2 {
if ast.Nodes[i].MapKey.Value.Map == nil {
ast.Nodes[i].MapKey.Value.Map = &d2ast.Map{
Range: d2ast.MakeRange(",1:0:0-1:0:0"),
}
if ast.Nodes[i].MapKey.Value.Import != nil {
imp := &d2ast.Import{
Range: d2ast.MakeRange(",1:0:0-1:0:0"),
Spread: true,
Pre: ast.Nodes[i].MapKey.Value.Import.Pre,
Path: ast.Nodes[i].MapKey.Value.Import.Path,
}
ast.Nodes[i].MapKey.Value.Map.Nodes = append(ast.Nodes[i].MapKey.Value.Map.Nodes, d2ast.MapNodeBox{
Import: imp,
})
}
}
if result := _findFieldAST(ast.Nodes[i].MapKey.Value.Map, remainingPath); result != nil {
return result
}
}
}
return nil
}
type compiler struct {

View file

@ -2775,6 +2775,40 @@ scenarios: {
Metricbeat.style.stroke: red
}
}
`,
},
{
name: "set-style-in-layer",
text: `hey
layers: {
k: {
b: {style.stroke: "#969db4"}
}
}
layers: {
x: {
y
}
}
`,
boardPath: []string{"x"},
key: `y.style.fill`,
value: go2.Pointer(`#ff0000`),
exp: `hey
layers: {
k: {
b: {style.stroke: "#969db4"}
}
}
layers: {
x: {
y: {style.fill: "#ff0000"}
}
}
`,
},
}

View file

@ -48,51 +48,35 @@ func ReplaceBoardNode(ast, ast2 *d2ast.Map, boardPath []string) bool {
return false
}
findMap := func(root *d2ast.Map, name string) *d2ast.Map {
for _, n := range root.Nodes {
if n.MapKey != nil && n.MapKey.Key != nil && n.MapKey.Key.Path[0].Unbox().ScalarString() == name {
return n.MapKey.Value.Map
}
}
return nil
}
return replaceBoardNodeInMap(ast, ast2, boardPath, "layers") ||
replaceBoardNodeInMap(ast, ast2, boardPath, "scenarios") ||
replaceBoardNodeInMap(ast, ast2, boardPath, "steps")
}
layersMap := findMap(ast, "layers")
scenariosMap := findMap(ast, "scenarios")
stepsMap := findMap(ast, "steps")
func replaceBoardNodeInMap(ast, ast2 *d2ast.Map, boardPath []string, boardType string) bool {
var matches []*d2ast.Map
if layersMap != nil {
m := findMap(layersMap, boardPath[0])
if m != nil {
if len(boardPath) > 1 {
return ReplaceBoardNode(m, ast2, boardPath[1:])
} else {
m.Nodes = ast2.Nodes
return true
}
for _, n := range ast.Nodes {
if n.MapKey != nil && n.MapKey.Key != nil &&
n.MapKey.Key.Path[0].Unbox().ScalarString() == boardType &&
n.MapKey.Value.Map != nil {
matches = append(matches, n.MapKey.Value.Map)
}
}
if scenariosMap != nil {
m := findMap(scenariosMap, boardPath[0])
if m != nil {
if len(boardPath) > 1 {
return ReplaceBoardNode(m, ast2, boardPath[1:])
} else {
m.Nodes = ast2.Nodes
return true
}
}
}
if stepsMap != nil {
m := findMap(stepsMap, boardPath[0])
if m != nil {
if len(boardPath) > 1 {
return ReplaceBoardNode(m, ast2, boardPath[1:])
} else {
m.Nodes = ast2.Nodes
return true
for _, boardMap := range matches {
for _, n := range boardMap.Nodes {
if n.MapKey != nil && n.MapKey.Key != nil &&
n.MapKey.Key.Path[0].Unbox().ScalarString() == boardPath[0] &&
n.MapKey.Value.Map != nil {
if len(boardPath) > 1 {
if ReplaceBoardNode(n.MapKey.Value.Map, ast2, boardPath[1:]) {
return true
}
} else {
n.MapKey.Value.Map.Nodes = ast2.Nodes
return true
}
}
}
}

726
testdata/d2oracle/TestSet/set-style-in-layer.exp.json generated vendored Normal file
View file

@ -0,0 +1,726 @@
{
"graph": {
"name": "",
"isFolderOnly": false,
"ast": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,0:0:0-13:0:116",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,0:0:0-0:3:3",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,0:0:0-0:3:3",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,0:0:0-0:3:3",
"value": [
{
"string": "hey",
"raw_string": "hey"
}
]
}
}
]
},
"primary": {},
"value": {}
}
},
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,2:0:5-6:1:60",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,2:0:5-2:6:11",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,2:0:5-2:6:11",
"value": [
{
"string": "layers",
"raw_string": "layers"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,2:8:13-6:1:60",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,3:2:17-5:3:58",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,3:2:17-3:3:18",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,3:2:17-3:3:18",
"value": [
{
"string": "k",
"raw_string": "k"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,3:5:20-5:3:58",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:4:26-4:32:54",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:4:26-4:5:27",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:4:26-4:5:27",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:7:29-4:32:54",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:8:30-4:31:53",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:8:30-4:20:42",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:8:30-4:13:35",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
},
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:14:36-4:20:42",
"value": [
{
"string": "stroke",
"raw_string": "stroke"
}
]
}
}
]
},
"primary": {},
"value": {
"double_quoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:22:44-4:31:53",
"value": [
{
"string": "#969db4",
"raw_string": "#969db4"
}
]
}
}
}
}
]
}
}
}
}
]
}
}
}
}
]
}
}
}
},
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,8:0:62-12:1:115",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,8:0:62-8:6:68",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,8:0:62-8:6:68",
"value": [
{
"string": "layers",
"raw_string": "layers"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,8:8:70-12:1:115",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,9:2:74-11:3:113",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,9:2:74-9:3:75",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,9:2:74-9:3:75",
"value": [
{
"string": "x",
"raw_string": "x"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,9:5:77-11:3:113",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:4:83-10:30:109",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:4:83-10:5:84",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:4:83-10:5:84",
"value": [
{
"string": "y",
"raw_string": "y"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:7:86-10:30:109",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:8:87-10:29:108",
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:8:87-10:18:97",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:8:87-10:13:92",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
},
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:14:93-10:18:97",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
}
}
]
},
"primary": {},
"value": {
"double_quoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:20:99-10:29:108",
"value": [
{
"string": "#ff0000",
"raw_string": "#ff0000"
}
]
}
}
}
}
]
}
}
}
}
]
}
}
}
}
]
}
}
}
}
]
},
"root": {
"id": "",
"id_val": "",
"attributes": {
"label": {
"value": ""
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"iconStyle": {},
"near_key": null,
"shape": {
"value": ""
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
"edges": null,
"objects": [
{
"id": "hey",
"id_val": "hey",
"references": [
{
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,0:0:0-0:3:3",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,0:0:0-0:3:3",
"value": [
{
"string": "hey",
"raw_string": "hey"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "hey"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"iconStyle": {},
"near_key": null,
"shape": {
"value": "rectangle"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
}
],
"layers": [
{
"name": "k",
"isFolderOnly": false,
"ast": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,0:0:0-1:0:0",
"nodes": [
{
"map_key": {
"range": ",0:0:0-0:0:0",
"key": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:4:26-4:5:27",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": ",1:0:0-2:0:0",
"nodes": [
{
"map_key": {
"range": ",0:0:0-0:0:0",
"key": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:8:30-4:13:35",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": ",1:0:0-2:0:0",
"nodes": [
{
"map_key": {
"range": ",0:0:0-0:0:0",
"key": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:14:36-4:20:42",
"value": [
{
"string": "stroke",
"raw_string": "stroke"
}
]
}
}
]
},
"primary": {
"double_quoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:22:44-4:31:53",
"value": [
{
"string": "#969db4",
"raw_string": "#969db4"
}
]
}
},
"value": {}
}
}
]
}
}
}
}
]
}
}
}
}
]
},
"root": {
"id": "",
"id_val": "",
"attributes": {
"label": {
"value": ""
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"iconStyle": {},
"near_key": null,
"shape": {
"value": ""
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
"edges": null,
"objects": [
{
"id": "b",
"id_val": "b",
"references": [
{
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:4:26-4:5:27",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,4:4:26-4:5:27",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "b"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {
"stroke": {
"value": "#969db4"
}
},
"iconStyle": {},
"near_key": null,
"shape": {
"value": "rectangle"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
}
]
},
{
"name": "x",
"isFolderOnly": false,
"ast": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,0:0:0-1:0:0",
"nodes": [
{
"map_key": {
"range": ",0:0:0-0:0:0",
"key": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:4:83-10:5:84",
"value": [
{
"string": "y",
"raw_string": "y"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": ",1:0:0-2:0:0",
"nodes": [
{
"map_key": {
"range": ",0:0:0-0:0:0",
"key": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:8:87-10:13:92",
"value": [
{
"string": "style",
"raw_string": "style"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": ",1:0:0-2:0:0",
"nodes": [
{
"map_key": {
"range": ",0:0:0-0:0:0",
"key": {
"range": ",0:0:0-0:0:0",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:14:93-10:18:97",
"value": [
{
"string": "fill",
"raw_string": "fill"
}
]
}
}
]
},
"primary": {
"double_quoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:20:99-10:29:108",
"value": [
{
"string": "#ff0000",
"raw_string": "#ff0000"
}
]
}
},
"value": {}
}
}
]
}
}
}
}
]
}
}
}
}
]
},
"root": {
"id": "",
"id_val": "",
"attributes": {
"label": {
"value": ""
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"iconStyle": {},
"near_key": null,
"shape": {
"value": ""
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
"edges": null,
"objects": [
{
"id": "y",
"id_val": "y",
"references": [
{
"key": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:4:83-10:5:84",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2oracle/TestSet/set-style-in-layer.d2,10:4:83-10:5:84",
"value": [
{
"string": "y",
"raw_string": "y"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "y"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {
"fill": {
"value": "#ff0000"
}
},
"iconStyle": {},
"near_key": null,
"shape": {
"value": "rectangle"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
}
]
}
]
},
"err": "<nil>"
}