From 9857e92b40075db58c1a5ea0b4e20c5003bc0b28 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 17 Jul 2024 21:05:53 -0600 Subject: [PATCH] fix import underscore links --- d2compiler/compile_test.go | 30 + d2ir/compile.go | 42 +- .../TestCompile/link-file-underscore.exp.json | 1086 +++++++++++++++++ 3 files changed, 1143 insertions(+), 15 deletions(-) create mode 100644 testdata/d2compiler/TestCompile/link-file-underscore.exp.json diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index f22171af6..5588fcc60 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -2351,6 +2351,36 @@ layers: { tassert.Equal(t, "root.layers.x", g.Layers[0].Layers[0].Objects[1].Link.Value) }, }, + { + name: "link-file-underscore", + text: `...@x`, + files: map[string]string{ + "x.d2": `x + +layers: { + a: { c } + b: { d.link: _.layers.a } + e: { + l + + layers: { + j: { + k.link: _ + n.link: _._ + m.link: _._.layers.a + } + } + } +} +`, + }, + assertions: func(t *testing.T, g *d2graph.Graph) { + tassert.Equal(t, "root.layers.a", g.Layers[1].Objects[0].Link.Value) + tassert.Equal(t, "root.layers.e", g.Layers[2].Layers[0].Objects[0].Link.Value) + tassert.Equal(t, "root", g.Layers[2].Layers[0].Objects[1].Link.Value) + tassert.Equal(t, "root.layers.a", g.Layers[2].Layers[0].Objects[2].Link.Value) + }, + }, { name: "link-board-underscore-not-found", text: `x diff --git a/d2ir/compile.go b/d2ir/compile.go index 53c4920da..c119287cc 100644 --- a/d2ir/compile.go +++ b/d2ir/compile.go @@ -512,7 +512,7 @@ func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) { } OverlayMap(dst, impn.Map()) - c.updateLinks(dst) + c.extendLinks(dst, ParentField(dst)) if impnf, ok := impn.(*Field); ok { if impnf.Primary_ != nil { @@ -843,6 +843,32 @@ func (c *compiler) ignoreLazyGlob(n Node) bool { return false } +// When importing a file, all of its board links need to be extended to reflect their new path +func (c *compiler) extendLinks(m *Map, importF *Field) { + for _, f := range m.Fields { + if f.Name == "link" { + val := f.Primary().Value.ScalarString() + link, err := d2parser.ParseKey(val) + if err != nil { + continue + } + linkIDA := link.IDA() + if len(linkIDA) == 0 { + continue + } + + importIDA := IDA(importF) + extendedIDA := append(importIDA, linkIDA[1:]...) + kp := d2ast.MakeKeyPath(extendedIDA) + s := d2format.Format(kp) + f.Primary_.Value = d2ast.MakeValueBox(d2ast.FlatUnquotedString(s)).ScalarBox().Unbox() + } + if f.Map() != nil { + c.extendLinks(f.Map(), importF) + } + } +} + func (c *compiler) updateLinks(m *Map) { for _, f := range m.Fields { if f.Name == "link" { @@ -864,22 +890,8 @@ func (c *compiler) updateLinks(m *Map) { bida := BoardIDA(f) aida := IDA(f) - uplevels := -1 - // The id path from that board to field - if len(aida)-len(bida) > len(bida)+1 { - relaida := aida[len(bida)+1 : len(aida)-len(bida)] - // If the link value has underscores, the path length can be less than the path length of the field - uplevels = len(relaida) - len(linkIDA) + 1 - } - if len(bida) != len(aida) { prependIDA := aida[:len(aida)-len(bida)] - if uplevels > 0 { - prependIDA = prependIDA[:len(prependIDA)-uplevels] - } else if uplevels == 0 { - // It's a sibling, so we go up one level to find it - prependIDA = prependIDA[:len(prependIDA)-2] - } fullIDA := []string{"root"} // With nested imports, a value may already have been updated with part of the absolute path // E.g., diff --git a/testdata/d2compiler/TestCompile/link-file-underscore.exp.json b/testdata/d2compiler/TestCompile/link-file-underscore.exp.json new file mode 100644 index 000000000..80284b3cb --- /dev/null +++ b/testdata/d2compiler/TestCompile/link-file-underscore.exp.json @@ -0,0 +1,1086 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile/link-file-underscore.d2,0:0:0-0:5:5", + "nodes": [ + { + "import": { + "range": "d2/testdata/d2compiler/TestCompile/link-file-underscore.d2,0:0:0-0:5:5", + "spread": true, + "pre": "", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/link-file-underscore.d2,0:4:4-0:5:5", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + } + } + ] + }, + "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": "x", + "id_val": "x", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,0:0:0-0:1:1", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,0:0:0-0:1:1", + "value": [ + { + "string": "x", + "raw_string": "x" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "x" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ], + "layers": [ + { + "name": "a", + "isFolderOnly": false, + "ast": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "c" + } + ] + } + } + ] + }, + "primary": {}, + "value": {} + } + } + ] + }, + "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": "c", + "id_val": "c", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,3:7:20-3:8:21", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,3:7:20-3:8:21", + "value": [ + { + "string": "c", + "raw_string": "c" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "c" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + { + "name": "b", + "isFolderOnly": false, + "ast": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "d" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "link" + } + ] + } + } + ] + }, + "primary": { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "root.layers.a" + } + ] + } + }, + "value": {} + } + } + ] + } + } + } + } + ] + }, + "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": "d", + "id_val": "d", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,4:7:31-4:13:37", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,4:7:31-4:8:32", + "value": [ + { + "string": "d", + "raw_string": "d" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,4:9:33-4:13:37", + "value": [ + { + "string": "link", + "raw_string": "link" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "d" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "link": { + "value": "root.layers.a" + }, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + { + "name": "e", + "isFolderOnly": false, + "ast": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "l" + } + ] + } + } + ] + }, + "primary": {}, + "value": {} + } + }, + { + "map_key": { + "range": ",0:0:0-0:0:0", + "key": { + "range": ",0:0:0-0:0:0", + "path": [ + { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "layers" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "j" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "k" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "link" + } + ] + } + } + ] + }, + "primary": { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "root.layers.e" + } + ] + } + }, + "value": {} + } + } + ] + } + } + } + }, + { + "map_key": { + "range": ",0:0:0-0:0:0", + "key": { + "range": ",0:0:0-0:0:0", + "path": [ + { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "n" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "link" + } + ] + } + } + ] + }, + "primary": { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "root" + } + ] + } + }, + "value": {} + } + } + ] + } + } + } + }, + { + "map_key": { + "range": ",0:0:0-0:0:0", + "key": { + "range": ",0:0:0-0:0:0", + "path": [ + { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "m" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "link" + } + ] + } + } + ] + }, + "primary": { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "root.layers.a" + } + ] + } + }, + "value": {} + } + } + ] + } + } + } + } + ] + } + } + } + } + ] + } + } + } + } + ] + }, + "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": "l", + "id_val": "l", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,6:4:62-6:5:63", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,6:4:62-6:5:63", + "value": [ + { + "string": "l", + "raw_string": "l" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "l" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ], + "layers": [ + { + "name": "j", + "isFolderOnly": false, + "ast": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "k" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "link" + } + ] + } + } + ] + }, + "primary": { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "root.layers.e" + } + ] + } + }, + "value": {} + } + } + ] + } + } + } + }, + { + "map_key": { + "range": ",0:0:0-0:0:0", + "key": { + "range": ",0:0:0-0:0:0", + "path": [ + { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "n" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "link" + } + ] + } + } + ] + }, + "primary": { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "root" + } + ] + } + }, + "value": {} + } + } + ] + } + } + } + }, + { + "map_key": { + "range": ",0:0:0-0:0:0", + "key": { + "range": ",0:0:0-0:0:0", + "path": [ + { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "m" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "map": { + "range": ",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": ",0:0:0-0:0:0", + "value": [ + { + "string": "link" + } + ] + } + } + ] + }, + "primary": { + "unquoted_string": { + "range": ",0:0:0-0:0:0", + "value": [ + { + "string": "root.layers.a" + } + ] + } + }, + "value": {} + } + } + ] + } + } + } + } + ] + }, + "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": "k", + "id_val": "k", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,10:5:90-10:11:96", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,10:5:90-10:6:91", + "value": [ + { + "string": "k", + "raw_string": "k" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,10:7:92-10:11:96", + "value": [ + { + "string": "link", + "raw_string": "link" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "k" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "link": { + "value": "root.layers.e" + }, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + { + "id": "n", + "id_val": "n", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,11:5:105-11:11:111", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,11:5:105-11:6:106", + "value": [ + { + "string": "n", + "raw_string": "n" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,11:7:107-11:11:111", + "value": [ + { + "string": "link", + "raw_string": "link" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "n" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "link": { + "value": "root" + }, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + }, + { + "id": "m", + "id_val": "m", + "references": [ + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,12:5:122-12:11:128", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,12:5:122-12:6:123", + "value": [ + { + "string": "m", + "raw_string": "m" + } + ] + } + }, + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile/x.d2,12:7:124-12:11:128", + "value": [ + { + "string": "link", + "raw_string": "link" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "m" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "link": { + "value": "root.layers.a" + }, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + } + ] + } + ] + }, + "err": null +}