diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 3094e0819..f4630137c 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -250,6 +250,7 @@ func (c *compiler) compileMap(obj *d2graph.Object, m *d2ir.Map) { } func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) { + println("\033[1;31m--- DEBUG:", "=======================", "\033[m") keyword := strings.ToLower(f.Name) _, isStyleReserved := d2graph.StyleKeywords[keyword] if isStyleReserved { @@ -303,7 +304,13 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) { } } + if f.Primary() != nil { + if _, ok := f.Primary().Value.(*d2ast.Null); ok { + return + } + } obj = obj.EnsureChild(d2graphIDA([]string{f.Name})) + if f.Primary() != nil { c.compileLabel(&obj.Attributes, f) } @@ -340,9 +347,6 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) { func (c *compiler) compileLabel(attrs *d2graph.Attributes, f d2ir.Node) { scalar := f.Primary().Value switch scalar := scalar.(type) { - case *d2ast.Null: - // TODO: Delete instead. - attrs.Label.Value = scalar.ScalarString() case *d2ast.BlockString: if strings.TrimSpace(scalar.ScalarString()) == "" { c.errorf(f.LastPrimaryKey(), "block string cannot be empty") diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index c6cea1b2f..1101e479c 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -2743,6 +2743,7 @@ func TestCompile2(t *testing.T) { t.Run("boards", testBoards) t.Run("seqdiagrams", testSeqDiagrams) + t.Run("nulls", testNulls) } func testBoards(t *testing.T) { @@ -2923,6 +2924,75 @@ Office chatter: { }) } +func testNulls(t *testing.T) { + t.Parallel() + + t.Run("basic", func(t *testing.T) { + t.Parallel() + + tca := []struct { + name string + skip bool + run func(t *testing.T) + }{ + { + name: "shape", + run: func(t *testing.T) { + g := assertCompile(t, ` +a +a: null +`, "") + assert.Equal(t, 0, len(g.Objects)) + }, + }, + } + + for _, tc := range tca { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if tc.skip { + t.SkipNow() + } + tc.run(t) + }) + } + }) + + t.Run("reappear", func(t *testing.T) { + t.Parallel() + + tca := []struct { + name string + skip bool + run func(t *testing.T) + }{ + { + name: "shape", + run: func(t *testing.T) { + g := assertCompile(t, ` +a +a: null +a +`, "") + assert.Equal(t, 1, len(g.Objects)) + }, + }, + } + + for _, tc := range tca { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if tc.skip { + t.SkipNow() + } + tc.run(t) + }) + } + }) +} + func assertCompile(t *testing.T, text string, expErr string) *d2graph.Graph { d2Path := fmt.Sprintf("d2/testdata/d2compiler/%v.d2", t.Name()) g, err := d2compiler.Compile(d2Path, strings.NewReader(text), nil) diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index eb47de274..27773d75a 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -785,6 +785,39 @@ func (obj *Object) ensureChildEdge(ida []string) *Object { return obj } +// DeleteChild deletes a child from the graph. +// If it has connections or children, those will also be deleted (recursively) +// func (obj *Object) DeleteChild(ida []string) error { +// if len(ida) == 0 { +// return nil +// } +// +// _, is := ReservedKeywordHolders[ida[0]] +// if len(ida) == 1 && !is { +// _, ok := ReservedKeywords[ida[0]] +// if ok { +// return obj +// } +// } +// +// id := ida[0] +// ida = ida[1:] +// +// if id == "_" { +// return obj.Parent.EnsureChild(ida) +// } +// +// child, ok := obj.Children[strings.ToLower(id)] +// if !ok { +// child = obj.newObject(id) +// } +// +// if len(ida) >= 1 { +// return child.EnsureChild(ida) +// } +// return child +// } + // EnsureChild grabs the child by ids or creates it if it does not exist including all // intermediate nodes. func (obj *Object) EnsureChild(ida []string) *Object { diff --git a/testdata/d2compiler/TestCompile2/nulls/basic/shape.exp.json b/testdata/d2compiler/TestCompile2/nulls/basic/shape.exp.json new file mode 100644 index 000000000..4f2c2fd5c --- /dev/null +++ b/testdata/d2compiler/TestCompile2/nulls/basic/shape.exp.json @@ -0,0 +1,87 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/basic/shape.d2,0:0:0-3:0:11", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/basic/shape.d2,1:0:1-1:1:2", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/basic/shape.d2,1:0:1-1:1:2", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/basic/shape.d2,1:0:1-1:1:2", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "primary": {}, + "value": {} + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/basic/shape.d2,2:0:3-2:7:10", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/basic/shape.d2,2:0:3-2:1:4", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/basic/shape.d2,2:0:3-2:1:4", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "null": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/basic/shape.d2,2:3:6-2:7:10" + } + } + } + } + ] + }, + "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": null + }, + "err": null +} diff --git a/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.exp.json b/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.exp.json new file mode 100644 index 000000000..f7b872984 --- /dev/null +++ b/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.exp.json @@ -0,0 +1,153 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,0:0:0-3:0:11", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,1:0:1-1:1:2", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,1:0:1-1:1:2", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,1:0:1-1:1:2", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "primary": {}, + "value": {} + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,2:0:3-2:7:10", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,2:0:3-2:1:4", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,2:0:3-2:1:4", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "null": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,2:3:6-2:7:10" + } + } + } + } + ] + }, + "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": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,1:0:1-1:1:2", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,1:0:1-1:1:2", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + }, + { + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,2:0:3-2:1:4", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/null/basic-shape.d2,2:0:3-2:1:4", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "key_path_index": 0, + "map_key_edge_index": -1 + } + ], + "attributes": { + "label": { + "value": "" + }, + "labelDimensions": { + "width": 0, + "height": 0 + }, + "style": {}, + "near_key": null, + "shape": { + "value": "rectangle" + }, + "direction": { + "value": "" + }, + "constraint": null + }, + "zIndex": 0 + } + ] + }, + "err": null +} diff --git a/testdata/d2compiler/TestCompile2/nulls/reappear/shape.exp.json b/testdata/d2compiler/TestCompile2/nulls/reappear/shape.exp.json new file mode 100644 index 000000000..ebeb96548 --- /dev/null +++ b/testdata/d2compiler/TestCompile2/nulls/reappear/shape.exp.json @@ -0,0 +1,110 @@ +{ + "graph": { + "name": "", + "isFolderOnly": false, + "ast": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,0:0:0-4:0:13", + "nodes": [ + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,1:0:1-1:1:2", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,1:0:1-1:1:2", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,1:0:1-1:1:2", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "primary": {}, + "value": {} + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,2:0:3-2:7:10", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,2:0:3-2:1:4", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,2:0:3-2:1:4", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "primary": {}, + "value": { + "null": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,2:3:6-2:7:10" + } + } + } + }, + { + "map_key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,3:0:11-3:1:12", + "key": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,3:0:11-3:1:12", + "path": [ + { + "unquoted_string": { + "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,3:0:11-3:1:12", + "value": [ + { + "string": "a", + "raw_string": "a" + } + ] + } + } + ] + }, + "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": null + }, + "err": null +}