From 3129df1c02465c2357124ea70bd5433f1481f848 Mon Sep 17 00:00:00 2001 From: Alexander Wang <alex@terrastruct.com> Date: Fri, 9 Feb 2024 10:34:25 -0800 Subject: [PATCH 1/2] fix deleting imported edge --- d2oracle/edit.go | 75 ++-- d2oracle/edit_test.go | 28 ++ d2oracle/get.go | 12 +- .../d2oracle/TestDelete/import/3.exp.json | 203 +++++++++++ testdata/d2oracle/TestSet/import/9.exp.json | 336 ++++++++++++++++++ 5 files changed, 619 insertions(+), 35 deletions(-) create mode 100644 testdata/d2oracle/TestDelete/import/3.exp.json create mode 100644 testdata/d2oracle/TestSet/import/9.exp.json diff --git a/d2oracle/edit.go b/d2oracle/edit.go index 076e5d6ea6..339f56a32f 100644 --- a/d2oracle/edit.go +++ b/d2oracle/edit.go @@ -383,7 +383,7 @@ func _set(g *d2graph.Graph, baseAST *d2ast.Map, key string, tag, value *string) break } obj = o - imported = IsImported(baseAST, obj) + imported = IsImportedObj(baseAST, obj) var maybeNewScope *d2ast.Map if baseAST != g.AST || imported { @@ -476,6 +476,7 @@ func _set(g *d2graph.Graph, baseAST *d2ast.Map, key string, tag, value *string) appendMapKey(scope, mk) return nil } + // TODO what if the edge is imported, how does this work? var ok bool edge, ok = obj.HasEdge(mk) if !ok { @@ -868,45 +869,52 @@ func Delete(g *d2graph.Graph, boardPath []string, key string) (_ *d2graph.Graph, return g, nil } - refs := e.References - if len(boardPath) > 0 { - refs := getWriteableEdgeRefs(e, baseAST) - if len(refs) != len(e.References) { - mk.Value = d2ast.MakeValueBox(&d2ast.Null{}) - } - } + imported := IsImportedEdge(baseAST, e) - if _, ok := mk.Value.Unbox().(*d2ast.Null); !ok { - ref := refs[0] - var refEdges []*d2ast.Edge - for _, ref := range refs { - refEdges = append(refEdges, ref.Edge) + if imported { + mk.Value = d2ast.MakeValueBox(&d2ast.Null{}) + appendMapKey(baseAST, mk) + } else { + refs := e.References + if len(boardPath) > 0 { + refs := getWriteableEdgeRefs(e, baseAST) + if len(refs) != len(e.References) { + mk.Value = d2ast.MakeValueBox(&d2ast.Null{}) + } } - ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, ref.MapKey.Edges[ref.MapKeyEdgeIndex].Src, true) - ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, ref.MapKey.Edges[ref.MapKeyEdgeIndex].Dst, false) - for i := len(e.References) - 1; i >= 0; i-- { - ref := e.References[i] - deleteEdge(g, ref.Scope, ref.MapKey, ref.MapKeyEdgeIndex) - } + if _, ok := mk.Value.Unbox().(*d2ast.Null); !ok { + ref := refs[0] + var refEdges []*d2ast.Edge + for _, ref := range refs { + refEdges = append(refEdges, ref.Edge) + } + ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, ref.MapKey.Edges[ref.MapKeyEdgeIndex].Src, true) + ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, ref.MapKey.Edges[ref.MapKeyEdgeIndex].Dst, false) - edges, ok := obj.FindEdges(mk) - if ok { - for _, e2 := range edges { - if e2.Index <= e.Index { - continue - } - for i := len(e2.References) - 1; i >= 0; i-- { - ref := e2.References[i] - if ref.MapKey.EdgeIndex != nil { - *ref.MapKey.EdgeIndex.Int-- + for i := len(e.References) - 1; i >= 0; i-- { + ref := e.References[i] + deleteEdge(g, ref.Scope, ref.MapKey, ref.MapKeyEdgeIndex) + } + + edges, ok := obj.FindEdges(mk) + if ok { + for _, e2 := range edges { + if e2.Index <= e.Index { + continue + } + for i := len(e2.References) - 1; i >= 0; i-- { + ref := e2.References[i] + if ref.MapKey.EdgeIndex != nil { + *ref.MapKey.EdgeIndex.Int-- + } } } } + } else { + // NOTE: it only needs to be after the last ref, but perhaps simplest and cleanest to append all nulls at the end + appendMapKey(baseAST, mk) } - } else { - // NOTE: it only needs to be after the last ref, but perhaps simplest and cleanest to append all nulls at the end - appendMapKey(baseAST, mk) } if len(boardPath) > 0 { replaced := ReplaceBoardNode(g.AST, baseAST, boardPath) @@ -925,10 +933,9 @@ func Delete(g *d2graph.Graph, boardPath []string, key string) (_ *d2graph.Graph, return g, nil } - imported := IsImported(baseAST, obj) + imported := IsImportedObj(baseAST, obj) if imported { - println(d2format.Format(boardG.AST)) mk.Value = d2ast.MakeValueBox(&d2ast.Null{}) appendMapKey(baseAST, mk) } else { diff --git a/d2oracle/edit_test.go b/d2oracle/edit_test.go index 198cc83dc3..452488f16c 100644 --- a/d2oracle/edit_test.go +++ b/d2oracle/edit_test.go @@ -2171,6 +2171,20 @@ layers: { b.style.fill: red } } +`, + }, + { + name: "import/9", + + text: `...@yo +`, + fsTexts: map[string]string{ + "yo.d2": `a -> b`, + }, + key: `(a -> b).style.stroke`, + value: go2.Pointer(`red`), + exp: `...@yo +(a -> b).style.stroke: red `, }, } @@ -7205,6 +7219,20 @@ scenarios: { x: null } } +`, + }, + { + name: "import/3", + + text: `...@meow +`, + fsTexts: map[string]string{ + "meow.d2": `a -> b +`, + }, + key: `(a -> b)[0]`, + exp: `...@meow +(a -> b)[0]: null `, }, } diff --git a/d2oracle/get.go b/d2oracle/get.go index 6e5721ee89..c33a465973 100644 --- a/d2oracle/get.go +++ b/d2oracle/get.go @@ -140,7 +140,7 @@ func GetParentID(g *d2graph.Graph, boardPath []string, absID string) (string, er return obj.Parent.AbsID(), nil } -func IsImported(ast *d2ast.Map, obj *d2graph.Object) bool { +func IsImportedObj(ast *d2ast.Map, obj *d2graph.Object) bool { for _, ref := range obj.References { if ref.Key.Range.Path != ast.Range.Path { return true @@ -150,6 +150,16 @@ func IsImported(ast *d2ast.Map, obj *d2graph.Object) bool { return false } +func IsImportedEdge(ast *d2ast.Map, edge *d2graph.Edge) bool { + for _, ref := range edge.References { + if ref.Edge.Range.Path != ast.Range.Path { + return true + } + } + + return false +} + func GetObj(g *d2graph.Graph, boardPath []string, absID string) *d2graph.Object { g = GetBoardGraph(g, boardPath) if g == nil { diff --git a/testdata/d2oracle/TestDelete/import/3.exp.json b/testdata/d2oracle/TestDelete/import/3.exp.json new file mode 100644 index 0000000000..90559179d1 --- /dev/null +++ b/testdata/d2oracle/TestDelete/import/3.exp.json @@ -0,0 +1,203 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "index.d2,0:0:0-2:0:27", + "nodes": [ + { + "import": { + "range": "index.d2,0:0:0-0:8:8", + "spread": true, + "pre": "", + "path": [ + { + "unquoted_string": { + "range": "index.d2,0:4:4-0:8:8", + "value": [ + { + "string": "meow", + "raw_string": "meow" + } + ] + } + } + ] + } + }, + { + "map_key": { + "range": "index.d2,1:0:9-1:17:26", + "edges": [ + { + "range": "index.d2,1:1:10-1:7:16", + "src": { + "range": "index.d2,1:1:10-1:2:11", + "path": [ + { + "unquoted_string": { + "range": "index.d2,1:1:10-1:2:11", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "src_arrow": "", + "dst": { + "range": "index.d2,1:6:15-1:7:16", + "path": [ + { + "unquoted_string": { + "range": "index.d2,1:6:15-1:7:16", + "value": [ + { + "string": "b", + "raw_string": "b" + } + ] + } + } + ] + }, + "dst_arrow": ">" + } + ], + "edge_index": { + "range": "index.d2,1:8:17-1:11:20", + "int": 0, + "glob": false + }, + "primary": {}, + "value": { + "null": { + "range": "index.d2,1:13:22-1:17:26" + } + } + } + } + ] + }, + "root": { + "id": "", + "id_val": "", + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + "edges": null, + "objects": [ + { + "id": "a", + "id_val": "a", + "references": [ + { + "key": { + "range": "meow.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "meow.d2,0:0:0-0:1:1", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "a" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + { + "id": "b", + "id_val": "b", + "references": [ + { + "key": { + "range": "meow.d2,0:5:5-0:6:6", + "path": [ + { + "unquoted_string": { + "range": "meow.d2,0:5:5-0:6:6", + "value": [ + { + "string": "b", + "raw_string": "b" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "b" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": "<nil>" +} diff --git a/testdata/d2oracle/TestSet/import/9.exp.json b/testdata/d2oracle/TestSet/import/9.exp.json new file mode 100644 index 0000000000..98c4629b6e --- /dev/null +++ b/testdata/d2oracle/TestSet/import/9.exp.json @@ -0,0 +1,336 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "index.d2,0:0:0-2:0:34", + "nodes": [ + { + "import": { + "range": "index.d2,0:0:0-0:6:6", + "spread": true, + "pre": "", + "path": [ + { + "unquoted_string": { + "range": "index.d2,0:4:4-0:6:6", + "value": [ + { + "string": "yo", + "raw_string": "yo" + } + ] + } + } + ] + } + }, + { + "map_key": { + "range": "index.d2,1:0:7-1:26:33", + "edges": [ + { + "range": "index.d2,1:1:8-1:7:14", + "src": { + "range": "index.d2,1:1:8-1:2:9", + "path": [ + { + "unquoted_string": { + "range": "index.d2,1:1:8-1:2:9", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "src_arrow": "", + "dst": { + "range": "index.d2,1:6:13-1:7:14", + "path": [ + { + "unquoted_string": { + "range": "index.d2,1:6:13-1:7:14", + "value": [ + { + "string": "b", + "raw_string": "b" + } + ] + } + } + ] + }, + "dst_arrow": ">" + } + ], + "edge_key": { + "range": "index.d2,1:9:16-1:21:28", + "path": [ + { + "unquoted_string": { + "range": "index.d2,1:9:16-1:14:21", + "value": [ + { + "string": "style", + "raw_string": "style" + } + ] + } + }, + { + "unquoted_string": { + "range": "index.d2,1:15:22-1:21:28", + "value": [ + { + "string": "stroke", + "raw_string": "stroke" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "unquoted_string": { + "range": "index.d2,1:23:30-1:26:33", + "value": [ + { + "string": "red", + "raw_string": "red" + } + ] + } + } + } + } + ] + }, + "root": { + "id": "", + "id_val": "", + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + "edges": [ + { + "index": 0, + "isCurve": false, + "src_arrow": false, + "dst_arrow": true, + "references": [ + { + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + { + "index": 1, + "isCurve": false, + "src_arrow": false, + "dst_arrow": true, + "references": [ + { + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": { + "stroke": { + "value": "red" + } + }, + "near_key": null, + "shape": { + "value": "" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ], + "objects": [ + { + "id": "a", + "id_val": "a", + "references": [ + { + "key": { + "range": "yo.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "yo.d2,0:0:0-0:1:1", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + }, + { + "key": { + "range": "index.d2,1:1:8-1:2:9", + "path": [ + { + "unquoted_string": { + "range": "index.d2,1:1:8-1:2:9", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "a" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + { + "id": "b", + "id_val": "b", + "references": [ + { + "key": { + "range": "yo.d2,0:5:5-0:6:6", + "path": [ + { + "unquoted_string": { + "range": "yo.d2,0:5:5-0:6:6", + "value": [ + { + "string": "b", + "raw_string": "b" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + }, + { + "key": { + "range": "index.d2,1:6:13-1:7:14", + "path": [ + { + "unquoted_string": { + "range": "index.d2,1:6:13-1:7:14", + "value": [ + { + "string": "b", + "raw_string": "b" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": 0 + } + ], + "attributes": { + "label": { + "value": "b" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": "<nil>" +} From 706951b85b497e59dddd324ef6243ca610363848 Mon Sep 17 00:00:00 2001 From: Alexander Wang <alex@terrastruct.com> Date: Fri, 9 Feb 2024 11:49:25 -0800 Subject: [PATCH 2/2] fix d2oracle.set on imported edge --- d2oracle/edit.go | 4 +- d2oracle/edit_test.go | 4 +- testdata/d2oracle/TestSet/import/9.exp.json | 44 ++++++--------------- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/d2oracle/edit.go b/d2oracle/edit.go index 339f56a32f..71fa327ddf 100644 --- a/d2oracle/edit.go +++ b/d2oracle/edit.go @@ -476,14 +476,14 @@ func _set(g *d2graph.Graph, baseAST *d2ast.Map, key string, tag, value *string) appendMapKey(scope, mk) return nil } - // TODO what if the edge is imported, how does this work? var ok bool edge, ok = obj.HasEdge(mk) if !ok { return errors.New("edge not found") } + imported = IsImportedEdge(baseAST, edge) refs := edge.References - if baseAST != g.AST { + if baseAST != g.AST || imported { refs = getWriteableEdgeRefs(edge, baseAST) } onlyInChain := true diff --git a/d2oracle/edit_test.go b/d2oracle/edit_test.go index 452488f16c..ab0737e382 100644 --- a/d2oracle/edit_test.go +++ b/d2oracle/edit_test.go @@ -2181,10 +2181,10 @@ layers: { fsTexts: map[string]string{ "yo.d2": `a -> b`, }, - key: `(a -> b).style.stroke`, + key: `(a -> b)[0].style.stroke`, value: go2.Pointer(`red`), exp: `...@yo -(a -> b).style.stroke: red +(a -> b)[0].style.stroke: red `, }, } diff --git a/testdata/d2oracle/TestSet/import/9.exp.json b/testdata/d2oracle/TestSet/import/9.exp.json index 98c4629b6e..3c942e675f 100644 --- a/testdata/d2oracle/TestSet/import/9.exp.json +++ b/testdata/d2oracle/TestSet/import/9.exp.json @@ -3,7 +3,7 @@ "name": "", "isFolderOnly": false, "ast": { - "range": "index.d2,0:0:0-2:0:34", + "range": "index.d2,0:0:0-2:0:37", "nodes": [ { "import": { @@ -27,7 +27,7 @@ }, { "map_key": { - "range": "index.d2,1:0:7-1:26:33", + "range": "index.d2,1:0:7-1:29:36", "edges": [ { "range": "index.d2,1:1:8-1:7:14", @@ -67,12 +67,17 @@ "dst_arrow": ">" } ], + "edge_index": { + "range": "index.d2,1:8:15-1:11:18", + "int": 0, + "glob": false + }, "edge_key": { - "range": "index.d2,1:9:16-1:21:28", + "range": "index.d2,1:12:19-1:24:31", "path": [ { "unquoted_string": { - "range": "index.d2,1:9:16-1:14:21", + "range": "index.d2,1:12:19-1:17:24", "value": [ { "string": "style", @@ -83,7 +88,7 @@ }, { "unquoted_string": { - "range": "index.d2,1:15:22-1:21:28", + "range": "index.d2,1:18:25-1:24:31", "value": [ { "string": "stroke", @@ -97,7 +102,7 @@ "primary": {}, "value": { "unquoted_string": { - "range": "index.d2,1:23:30-1:26:33", + "range": "index.d2,1:26:33-1:29:36", "value": [ { "string": "red", @@ -142,34 +147,7 @@ "references": [ { "map_key_edge_index": 0 - } - ], - "attributes": { - "label": { - "value": "" - }, - "labelDimensions": { - "width": 0, - "height": 0 }, - "style": {}, - "near_key": null, - "shape": { - "value": "" - }, - "direction": { - "value": "" - }, - "constraint": null - }, - "zIndex": 0 - }, - { - "index": 1, - "isCurve": false, - "src_arrow": false, - "dst_arrow": true, - "references": [ { "map_key_edge_index": 0 }