refactor d2graph.Attributes

This commit is contained in:
Gavin Nishizawa 2023-04-13 20:04:55 -07:00
parent 3c43db31d0
commit 214c95eefc
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD
18 changed files with 564 additions and 572 deletions

View file

@ -150,7 +150,7 @@ func (c *compiler) compileMap(obj *d2graph.Object, m *d2ir.Map) {
c.compileField(obj, f) c.compileField(obj, f)
} }
switch obj.Attributes.Shape.Value { switch obj.Shape.Value {
case d2target.ShapeClass: case d2target.ShapeClass:
c.compileClass(obj) c.compileClass(obj)
case d2target.ShapeSQLTable: case d2target.ShapeSQLTable:
@ -191,25 +191,25 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) {
} }
return return
} else if isReserved { } else if isReserved {
c.compileReserved(obj.Attributes, f) c.compileReserved(&obj.Attributes, f)
return return
} else if f.Name == "style" { } else if f.Name == "style" {
if f.Map() == nil { if f.Map() == nil {
return return
} }
c.compileStyle(obj.Attributes, f.Map()) c.compileStyle(&obj.Attributes, f.Map())
if obj.Attributes.Style.Animated != nil { if obj.Style.Animated != nil {
c.errorf(obj.Attributes.Style.Animated.MapKey, `key "animated" can only be applied to edges`) c.errorf(obj.Style.Animated.MapKey, `key "animated" can only be applied to edges`)
} }
return return
} }
if obj.Parent != nil { if obj.Parent != nil {
if obj.Parent.Attributes.Shape.Value == d2target.ShapeSQLTable { if obj.Parent.Shape.Value == d2target.ShapeSQLTable {
c.errorf(f.LastRef().AST(), "sql_table columns cannot have children") c.errorf(f.LastRef().AST(), "sql_table columns cannot have children")
return return
} }
if obj.Parent.Attributes.Shape.Value == d2target.ShapeClass { if obj.Parent.Shape.Value == d2target.ShapeClass {
c.errorf(f.LastRef().AST(), "class fields cannot have children") c.errorf(f.LastRef().AST(), "class fields cannot have children")
return return
} }
@ -217,14 +217,14 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) {
obj = obj.EnsureChild(d2graphIDA([]string{f.Name})) obj = obj.EnsureChild(d2graphIDA([]string{f.Name}))
if f.Primary() != nil { if f.Primary() != nil {
c.compileLabel(obj.Attributes, f) c.compileLabel(&obj.Attributes, f)
} }
if f.Map() != nil { if f.Map() != nil {
c.compileMap(obj, f.Map()) c.compileMap(obj, f.Map())
} }
if obj.Attributes.Label.MapKey == nil { if obj.Label.MapKey == nil {
obj.Attributes.Label.MapKey = f.LastPrimaryKey() obj.Label.MapKey = f.LastPrimaryKey()
} }
for _, fr := range f.References { for _, fr := range f.References {
if fr.Primary() { if fr.Primary() {
@ -337,18 +337,18 @@ func (c *compiler) compileReserved(attrs *d2graph.Attributes, f *d2ir.Field) {
c.errorf(scalar, "non-integer width %#v: %s", scalar.ScalarString(), err) c.errorf(scalar, "non-integer width %#v: %s", scalar.ScalarString(), err)
return return
} }
attrs.Width = &d2graph.Scalar{} attrs.WidthAttr = &d2graph.Scalar{}
attrs.Width.Value = scalar.ScalarString() attrs.WidthAttr.Value = scalar.ScalarString()
attrs.Width.MapKey = f.LastPrimaryKey() attrs.WidthAttr.MapKey = f.LastPrimaryKey()
case "height": case "height":
_, err := strconv.Atoi(scalar.ScalarString()) _, err := strconv.Atoi(scalar.ScalarString())
if err != nil { if err != nil {
c.errorf(scalar, "non-integer height %#v: %s", scalar.ScalarString(), err) c.errorf(scalar, "non-integer height %#v: %s", scalar.ScalarString(), err)
return return
} }
attrs.Height = &d2graph.Scalar{} attrs.HeightAttr = &d2graph.Scalar{}
attrs.Height.Value = scalar.ScalarString() attrs.HeightAttr.Value = scalar.ScalarString()
attrs.Height.MapKey = f.LastPrimaryKey() attrs.HeightAttr.MapKey = f.LastPrimaryKey()
case "top": case "top":
v, err := strconv.Atoi(scalar.ScalarString()) v, err := strconv.Atoi(scalar.ScalarString())
if err != nil { if err != nil {
@ -530,9 +530,9 @@ func compileStyleFieldInit(attrs *d2graph.Attributes, f *d2ir.Field) {
case "filled": case "filled":
attrs.Style.Filled = &d2graph.Scalar{MapKey: f.LastPrimaryKey()} attrs.Style.Filled = &d2graph.Scalar{MapKey: f.LastPrimaryKey()}
case "width": case "width":
attrs.Width = &d2graph.Scalar{MapKey: f.LastPrimaryKey()} attrs.WidthAttr = &d2graph.Scalar{MapKey: f.LastPrimaryKey()}
case "height": case "height":
attrs.Height = &d2graph.Scalar{MapKey: f.LastPrimaryKey()} attrs.HeightAttr = &d2graph.Scalar{MapKey: f.LastPrimaryKey()}
case "top": case "top":
attrs.Top = &d2graph.Scalar{MapKey: f.LastPrimaryKey()} attrs.Top = &d2graph.Scalar{MapKey: f.LastPrimaryKey()}
case "left": case "left":
@ -552,13 +552,13 @@ func (c *compiler) compileEdge(obj *d2graph.Object, e *d2ir.Edge) {
} }
if e.Primary() != nil { if e.Primary() != nil {
c.compileLabel(edge.Attributes, e) c.compileLabel(&edge.Attributes, e)
} }
if e.Map() != nil { if e.Map() != nil {
c.compileEdgeMap(edge, e.Map()) c.compileEdgeMap(edge, e.Map())
} }
edge.Attributes.Label.MapKey = e.LastPrimaryKey() edge.Label.MapKey = e.LastPrimaryKey()
for _, er := range e.References { for _, er := range e.References {
scopeObjIDA := d2ir.BoardIDA(er.Context.ScopeMap) scopeObjIDA := d2ir.BoardIDA(er.Context.ScopeMap)
scopeObj := edge.Src.Graph.Root.EnsureChildIDVal(scopeObjIDA) scopeObj := edge.Src.Graph.Root.EnsureChildIDVal(scopeObjIDA)
@ -604,13 +604,13 @@ func (c *compiler) compileEdgeField(edge *d2graph.Edge, f *d2ir.Field) {
} }
_, isReserved := d2graph.SimpleReservedKeywords[keyword] _, isReserved := d2graph.SimpleReservedKeywords[keyword]
if isReserved { if isReserved {
c.compileReserved(edge.Attributes, f) c.compileReserved(&edge.Attributes, f)
return return
} else if f.Name == "style" { } else if f.Name == "style" {
if f.Map() == nil { if f.Map() == nil {
return return
} }
c.compileStyle(edge.Attributes, f.Map()) c.compileStyle(&edge.Attributes, f.Map())
return return
} }
@ -686,7 +686,7 @@ func (c *compiler) compileClass(obj *d2graph.Object) {
} }
if !strings.Contains(f.IDVal, "(") { if !strings.Contains(f.IDVal, "(") {
typ := f.Attributes.Label.Value typ := f.Label.Value
if typ == f.IDVal { if typ == f.IDVal {
typ = "" typ = ""
} }
@ -698,7 +698,7 @@ func (c *compiler) compileClass(obj *d2graph.Object) {
} else { } else {
// TODO: Not great, AST should easily allow specifying alternate primary field // TODO: Not great, AST should easily allow specifying alternate primary field
// as an explicit label should change the name. // as an explicit label should change the name.
returnType := f.Attributes.Label.Value returnType := f.Label.Value
if returnType == f.IDVal { if returnType == f.IDVal {
returnType = "void" returnType = "void"
} }
@ -725,7 +725,7 @@ func (c *compiler) compileClass(obj *d2graph.Object) {
func (c *compiler) compileSQLTable(obj *d2graph.Object) { func (c *compiler) compileSQLTable(obj *d2graph.Object) {
obj.SQLTable = &d2target.SQLTable{} obj.SQLTable = &d2target.SQLTable{}
for _, col := range obj.ChildrenArray { for _, col := range obj.ChildrenArray {
typ := col.Attributes.Label.Value typ := col.Label.Value
if typ == col.IDVal { if typ == col.IDVal {
// Not great, AST should easily allow specifying alternate primary field // Not great, AST should easily allow specifying alternate primary field
// as an explicit label should change the name. // as an explicit label should change the name.
@ -735,8 +735,8 @@ func (c *compiler) compileSQLTable(obj *d2graph.Object) {
Name: d2target.Text{Label: col.IDVal}, Name: d2target.Text{Label: col.IDVal},
Type: d2target.Text{Label: typ}, Type: d2target.Text{Label: typ},
} }
if col.Attributes.Constraint.Value != "" { if col.Constraint.Value != "" {
d2Col.Constraint = col.Attributes.Constraint.Value d2Col.Constraint = col.Constraint.Value
} }
obj.SQLTable.Columns = append(obj.SQLTable.Columns, d2Col) obj.SQLTable.Columns = append(obj.SQLTable.Columns, d2Col)
} }
@ -766,35 +766,35 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) {
keyword := strings.ToLower(f.Name) keyword := strings.ToLower(f.Name)
_, isReserved := d2graph.ReservedKeywords[keyword] _, isReserved := d2graph.ReservedKeywords[keyword]
if isReserved { if isReserved {
switch obj.Attributes.Shape.Value { switch obj.Shape.Value {
case d2target.ShapeCircle, d2target.ShapeSquare: case d2target.ShapeCircle, d2target.ShapeSquare:
checkEqual := (keyword == "width" && obj.Attributes.Height != nil) || (keyword == "height" && obj.Attributes.Width != nil) checkEqual := (keyword == "width" && obj.HeightAttr != nil) || (keyword == "height" && obj.WidthAttr != nil)
if checkEqual && obj.Attributes.Width.Value != obj.Attributes.Height.Value { if checkEqual && obj.WidthAttr.Value != obj.HeightAttr.Value {
c.errorf(f.LastPrimaryKey(), "width and height must be equal for %s shapes", obj.Attributes.Shape.Value) c.errorf(f.LastPrimaryKey(), "width and height must be equal for %s shapes", obj.Shape.Value)
} }
} }
switch f.Name { switch f.Name {
case "style": case "style":
if obj.Attributes.Style.ThreeDee != nil { if obj.Style.ThreeDee != nil {
if !strings.EqualFold(obj.Attributes.Shape.Value, d2target.ShapeSquare) && !strings.EqualFold(obj.Attributes.Shape.Value, d2target.ShapeRectangle) && !strings.EqualFold(obj.Attributes.Shape.Value, d2target.ShapeHexagon) { if !strings.EqualFold(obj.Shape.Value, d2target.ShapeSquare) && !strings.EqualFold(obj.Shape.Value, d2target.ShapeRectangle) && !strings.EqualFold(obj.Shape.Value, d2target.ShapeHexagon) {
c.errorf(obj.Attributes.Style.ThreeDee.MapKey, `key "3d" can only be applied to squares, rectangles, and hexagons`) c.errorf(obj.Style.ThreeDee.MapKey, `key "3d" can only be applied to squares, rectangles, and hexagons`)
} }
} }
if obj.Attributes.Style.DoubleBorder != nil { if obj.Style.DoubleBorder != nil {
if obj.Attributes.Shape.Value != "" && obj.Attributes.Shape.Value != d2target.ShapeSquare && obj.Attributes.Shape.Value != d2target.ShapeRectangle && obj.Attributes.Shape.Value != d2target.ShapeCircle && obj.Attributes.Shape.Value != d2target.ShapeOval { if obj.Shape.Value != "" && obj.Shape.Value != d2target.ShapeSquare && obj.Shape.Value != d2target.ShapeRectangle && obj.Shape.Value != d2target.ShapeCircle && obj.Shape.Value != d2target.ShapeOval {
c.errorf(obj.Attributes.Style.DoubleBorder.MapKey, `key "double-border" can only be applied to squares, rectangles, circles, ovals`) c.errorf(obj.Style.DoubleBorder.MapKey, `key "double-border" can only be applied to squares, rectangles, circles, ovals`)
} }
} }
case "shape": case "shape":
if obj.Attributes.Shape.Value == d2target.ShapeImage && obj.Attributes.Icon == nil { if obj.Shape.Value == d2target.ShapeImage && obj.Icon == nil {
c.errorf(f.LastPrimaryKey(), `image shape must include an "icon" field`) c.errorf(f.LastPrimaryKey(), `image shape must include an "icon" field`)
} }
in := d2target.IsShape(obj.Attributes.Shape.Value) in := d2target.IsShape(obj.Shape.Value)
_, arrowheadIn := d2target.Arrowheads[obj.Attributes.Shape.Value] _, arrowheadIn := d2target.Arrowheads[obj.Shape.Value]
if !in && arrowheadIn { if !in && arrowheadIn {
c.errorf(f.LastPrimaryKey(), fmt.Sprintf(`invalid shape, can only set "%s" for arrowheads`, obj.Attributes.Shape.Value)) c.errorf(f.LastPrimaryKey(), fmt.Sprintf(`invalid shape, can only set "%s" for arrowheads`, obj.Shape.Value))
} }
case "grid-rows", "grid-columns", "grid-gap", "vertical-gap", "horizontal-gap": case "grid-rows", "grid-columns", "grid-gap", "vertical-gap", "horizontal-gap":
for _, child := range obj.ChildrenArray { for _, child := range obj.ChildrenArray {
@ -807,7 +807,7 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) {
return return
} }
if obj.Attributes.Shape.Value == d2target.ShapeImage { if obj.Shape.Value == d2target.ShapeImage {
c.errorf(f.LastRef().AST(), "image shapes cannot have children.") c.errorf(f.LastRef().AST(), "image shapes cannot have children.")
return return
} }
@ -820,9 +820,9 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) {
func (c *compiler) validateNear(g *d2graph.Graph) { func (c *compiler) validateNear(g *d2graph.Graph) {
for _, obj := range g.Objects { for _, obj := range g.Objects {
if obj.Attributes.NearKey != nil { if obj.NearKey != nil {
nearObj, isKey := g.Root.HasChild(d2graph.Key(obj.Attributes.NearKey)) nearObj, isKey := g.Root.HasChild(d2graph.Key(obj.NearKey))
_, isConst := d2graph.NearConstants[d2graph.Key(obj.Attributes.NearKey)[0]] _, isConst := d2graph.NearConstants[d2graph.Key(obj.NearKey)[0]]
if isKey { if isKey {
// Doesn't make sense to set near to an ancestor or descendant // Doesn't make sense to set near to an ancestor or descendant
nearIsAncestor := false nearIsAncestor := false
@ -833,7 +833,7 @@ func (c *compiler) validateNear(g *d2graph.Graph) {
} }
} }
if nearIsAncestor { if nearIsAncestor {
c.errorf(obj.Attributes.NearKey, "near keys cannot be set to an ancestor") c.errorf(obj.NearKey, "near keys cannot be set to an ancestor")
continue continue
} }
nearIsDescendant := false nearIsDescendant := false
@ -844,27 +844,27 @@ func (c *compiler) validateNear(g *d2graph.Graph) {
} }
} }
if nearIsDescendant { if nearIsDescendant {
c.errorf(obj.Attributes.NearKey, "near keys cannot be set to an descendant") c.errorf(obj.NearKey, "near keys cannot be set to an descendant")
continue continue
} }
if nearObj.OuterSequenceDiagram() != nil { if nearObj.OuterSequenceDiagram() != nil {
c.errorf(obj.Attributes.NearKey, "near keys cannot be set to an object within sequence diagrams") c.errorf(obj.NearKey, "near keys cannot be set to an object within sequence diagrams")
continue continue
} }
if nearObj.Attributes.NearKey != nil { if nearObj.NearKey != nil {
_, nearObjNearIsConst := d2graph.NearConstants[d2graph.Key(nearObj.Attributes.NearKey)[0]] _, nearObjNearIsConst := d2graph.NearConstants[d2graph.Key(nearObj.NearKey)[0]]
if nearObjNearIsConst { if nearObjNearIsConst {
c.errorf(obj.Attributes.NearKey, "near keys cannot be set to an object with a constant near key") c.errorf(obj.NearKey, "near keys cannot be set to an object with a constant near key")
continue continue
} }
} }
} else if isConst { } else if isConst {
if obj.Parent != g.Root { if obj.Parent != g.Root {
c.errorf(obj.Attributes.NearKey, "constant near keys can only be set on root level shapes") c.errorf(obj.NearKey, "constant near keys can only be set on root level shapes")
continue continue
} }
} else { } else {
c.errorf(obj.Attributes.NearKey, "near key %#v must be the absolute path to a shape or one of the following constants: %s", d2format.Format(obj.Attributes.NearKey), strings.Join(d2graph.NearConstantsArray, ", ")) c.errorf(obj.NearKey, "near key %#v must be the absolute path to a shape or one of the following constants: %s", d2format.Format(obj.NearKey), strings.Join(d2graph.NearConstantsArray, ", "))
continue continue
} }
} }
@ -877,10 +877,10 @@ func (c *compiler) validateNear(g *d2graph.Graph) {
var isSrcNearConst, isDstNearConst bool var isSrcNearConst, isDstNearConst bool
if srcNearContainer != nil { if srcNearContainer != nil {
_, isSrcNearConst = d2graph.NearConstants[d2graph.Key(srcNearContainer.Attributes.NearKey)[0]] _, isSrcNearConst = d2graph.NearConstants[d2graph.Key(srcNearContainer.NearKey)[0]]
} }
if dstNearContainer != nil { if dstNearContainer != nil {
_, isDstNearConst = d2graph.NearConstants[d2graph.Key(dstNearContainer.Attributes.NearKey)[0]] _, isDstNearConst = d2graph.NearConstants[d2graph.Key(dstNearContainer.NearKey)[0]]
} }
if (isSrcNearConst || isDstNearConst) && srcNearContainer != dstNearContainer { if (isSrcNearConst || isDstNearConst) && srcNearContainer != dstNearContainer {
@ -905,11 +905,11 @@ func (c *compiler) validateEdges(g *d2graph.Graph) {
func (c *compiler) validateBoardLinks(g *d2graph.Graph) { func (c *compiler) validateBoardLinks(g *d2graph.Graph) {
for _, obj := range g.Objects { for _, obj := range g.Objects {
if obj.Attributes.Link == nil { if obj.Link == nil {
continue continue
} }
linkKey, err := d2parser.ParseKey(obj.Attributes.Link.Value) linkKey, err := d2parser.ParseKey(obj.Link.Value)
if err != nil { if err != nil {
continue continue
} }
@ -919,7 +919,7 @@ func (c *compiler) validateBoardLinks(g *d2graph.Graph) {
} }
if !hasBoard(g.RootBoard(), linkKey.IDA()) { if !hasBoard(g.RootBoard(), linkKey.IDA()) {
c.errorf(obj.Attributes.Link.MapKey, "linked board not found") c.errorf(obj.Link.MapKey, "linked board not found")
continue continue
} }
} }

View file

@ -43,8 +43,8 @@ x: {
t.Fatalf("expected g.Objects[0].ID to be x: %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be x: %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Shape.Value != d2target.ShapeCircle { if g.Objects[0].Shape.Value != d2target.ShapeCircle {
t.Fatalf("expected g.Objects[0].Attributes.Shape.Value to be circle: %#v", g.Objects[0].Attributes.Shape.Value) t.Fatalf("expected g.Objects[0].Shape.Value to be circle: %#v", g.Objects[0].Shape.Value)
} }
}, },
@ -65,8 +65,8 @@ x: {
t.Fatalf("expected g.Objects[0].ID to be x: %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be x: %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Style.Opacity.Value != "0.4" { if g.Objects[0].Style.Opacity.Value != "0.4" {
t.Fatalf("expected g.Objects[0].Attributes.Style.Opacity.Value to be 0.4: %#v", g.Objects[0].Attributes.Style.Opacity.Value) t.Fatalf("expected g.Objects[0].Style.Opacity.Value to be 0.4: %#v", g.Objects[0].Style.Opacity.Value)
} }
}, },
@ -102,14 +102,14 @@ x: {
if g.Objects[0].ID != "hey" { if g.Objects[0].ID != "hey" {
t.Fatalf("expected g.Objects[0].ID to be 'hey': %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be 'hey': %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Shape.Value != d2target.ShapeHexagon { if g.Objects[0].Shape.Value != d2target.ShapeHexagon {
t.Fatalf("expected g.Objects[0].Attributes.Shape.Value to be hexagon: %#v", g.Objects[0].Attributes.Shape.Value) t.Fatalf("expected g.Objects[0].Shape.Value to be hexagon: %#v", g.Objects[0].Shape.Value)
} }
if g.Objects[0].Attributes.Width.Value != "200" { if g.Objects[0].WidthAttr.Value != "200" {
t.Fatalf("expected g.Objects[0].Attributes.Width.Value to be 200: %#v", g.Objects[0].Attributes.Width.Value) t.Fatalf("expected g.Objects[0].Width.Value to be 200: %#v", g.Objects[0].WidthAttr.Value)
} }
if g.Objects[0].Attributes.Height.Value != "230" { if g.Objects[0].HeightAttr.Value != "230" {
t.Fatalf("expected g.Objects[0].Attributes.Height.Value to be 230: %#v", g.Objects[0].Attributes.Height.Value) t.Fatalf("expected g.Objects[0].Height.Value to be 230: %#v", g.Objects[0].HeightAttr.Value)
} }
}, },
}, },
@ -121,7 +121,7 @@ x: {
} }
`, `,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, "200", g.Objects[0].Attributes.Top.Value) tassert.Equal(t, "200", g.Objects[0].Top.Value)
}, },
}, },
{ {
@ -160,13 +160,13 @@ d2/testdata/d2compiler/TestCompile/equal_dimensions_on_circle.d2:4:2: width and
if g.Objects[0].ID != "hey" { if g.Objects[0].ID != "hey" {
t.Fatalf("expected ID to be 'hey': %#v", g.Objects[0]) t.Fatalf("expected ID to be 'hey': %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Shape.Value != d2target.ShapeCircle { if g.Objects[0].Shape.Value != d2target.ShapeCircle {
t.Fatalf("expected Attributes.Shape.Value to be circle: %#v", g.Objects[0].Attributes.Shape.Value) t.Fatalf("expected Attributes.Shape.Value to be circle: %#v", g.Objects[0].Shape.Value)
} }
if g.Objects[0].Attributes.Width != nil { if g.Objects[0].WidthAttr != nil {
t.Fatalf("expected Attributes.Width to be nil: %#v", g.Objects[0].Attributes.Width) t.Fatalf("expected Attributes.Width to be nil: %#v", g.Objects[0].WidthAttr)
} }
if g.Objects[0].Attributes.Height == nil { if g.Objects[0].HeightAttr == nil {
t.Fatalf("Attributes.Height is nil") t.Fatalf("Attributes.Height is nil")
} }
}, },
@ -237,7 +237,7 @@ containers: {
} }
`, `,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
if g.Objects[0].Attributes.Icon == nil { if g.Objects[0].Icon == nil {
t.Fatal("Attribute icon is nil") t.Fatal("Attribute icon is nil")
} }
}, },
@ -326,7 +326,7 @@ containers: {
if len(g.Objects) != 1 { if len(g.Objects) != 1 {
t.Fatalf("expected 1 objects: %#v", g.Objects) t.Fatalf("expected 1 objects: %#v", g.Objects)
} }
if g.Objects[0].Attributes.Style.StrokeWidth.Value != "0" { if g.Objects[0].Style.StrokeWidth.Value != "0" {
t.Fatalf("unexpected") t.Fatalf("unexpected")
} }
}, },
@ -442,8 +442,8 @@ y: "But it's real. And if it's real it can be affected ... we may not be able"
if len(g.Root.ChildrenArray) != 2 { if len(g.Root.ChildrenArray) != 2 {
t.Fatalf("expected 2 objects at the root: %#v", len(g.Root.ChildrenArray)) t.Fatalf("expected 2 objects at the root: %#v", len(g.Root.ChildrenArray))
} }
if g.Objects[1].Attributes.Label.Value != "But it's real. And if it's real it can be affected ... we may not be able" { if g.Objects[1].Label.Value != "But it's real. And if it's real it can be affected ... we may not be able" {
t.Fatalf("expected g.Objects[1].Label.Value to be last value: %#v", g.Objects[1].Attributes.Label.Value) t.Fatalf("expected g.Objects[1].Label.Value to be last value: %#v", g.Objects[1].Label.Value)
} }
}, },
}, },
@ -470,8 +470,8 @@ x: {
if len(g.Root.ChildrenArray) != 2 { if len(g.Root.ChildrenArray) != 2 {
t.Fatalf("expected 2 objects at the root: %#v", len(g.Root.ChildrenArray)) t.Fatalf("expected 2 objects at the root: %#v", len(g.Root.ChildrenArray))
} }
if g.Objects[0].Attributes.Label.Value != "All we are given is possibilities -- to make ourselves one thing or another." { if g.Objects[0].Label.Value != "All we are given is possibilities -- to make ourselves one thing or another." {
t.Fatalf("expected g.Objects[0].Label.Value to be last value: %#v", g.Objects[0].Attributes.Label.Value) t.Fatalf("expected g.Objects[0].Label.Value to be last value: %#v", g.Objects[0].Label.Value)
} }
}, },
}, },
@ -626,11 +626,11 @@ x: {
if g.Edges[1].Dst.ID != "b" { if g.Edges[1].Dst.ID != "b" {
t.Fatalf("expected g.Edges[1].Dst.ID to be b: %#v", g.Edges[1]) t.Fatalf("expected g.Edges[1].Dst.ID to be b: %#v", g.Edges[1])
} }
if g.Edges[0].Attributes.Label.Value != "Can you imagine how life could be improved if we could do away with" { if g.Edges[0].Label.Value != "Can you imagine how life could be improved if we could do away with" {
t.Fatalf("unexpected g.Edges[0].Label: %#v", g.Edges[0].Attributes.Label) t.Fatalf("unexpected g.Edges[0].Label: %#v", g.Edges[0].Label)
} }
if g.Edges[1].Attributes.Label.Value != "Well, it's garish, ugly, and derelicts have used it for a toilet." { if g.Edges[1].Label.Value != "Well, it's garish, ugly, and derelicts have used it for a toilet." {
t.Fatalf("unexpected g.Edges[1].Label: %#v", g.Edges[1].Attributes.Label) t.Fatalf("unexpected g.Edges[1].Label: %#v", g.Edges[1].Label)
} }
}, },
}, },
@ -656,8 +656,8 @@ x: {
if g.Edges[0].Dst.ID != "b" { if g.Edges[0].Dst.ID != "b" {
t.Fatalf("expected g.Edges[0].Dst.ID to be b: %#v", g.Edges[0]) t.Fatalf("expected g.Edges[0].Dst.ID to be b: %#v", g.Edges[0])
} }
if g.Edges[0].Attributes.Label.Value != "Well, it's garish, ugly, and derelicts have used it for a toilet." { if g.Edges[0].Label.Value != "Well, it's garish, ugly, and derelicts have used it for a toilet." {
t.Fatalf("unexpected g.Edges[0].Label: %#v", g.Edges[0].Attributes.Label) t.Fatalf("unexpected g.Edges[0].Label: %#v", g.Edges[0].Label)
} }
}, },
}, },
@ -756,11 +756,11 @@ x -> y -> z: "The kids will love our inflatable slides"
t.Fatalf("expected g.Edges[1].Dst.ID to be y: %#v", g.Edges[1]) t.Fatalf("expected g.Edges[1].Dst.ID to be y: %#v", g.Edges[1])
} }
if g.Edges[0].Attributes.Label.Value != "The kids will love our inflatable slides" { if g.Edges[0].Label.Value != "The kids will love our inflatable slides" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label: %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label: %#v", g.Edges[0].Label.Value)
} }
if g.Edges[1].Attributes.Label.Value != "The kids will love our inflatable slides" { if g.Edges[1].Label.Value != "The kids will love our inflatable slides" {
t.Fatalf("unexpected g.Edges[1].Attributes.Label: %#v", g.Edges[1].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[1].Label: %#v", g.Edges[1].Label.Value)
} }
}, },
}, },
@ -797,8 +797,8 @@ x -> y: one
if !g.Edges[0].DstArrow { if !g.Edges[0].DstArrow {
t.Fatalf("expected g.Edges[0].DstArrow to be true: %#v", g.Edges[0].DstArrow) t.Fatalf("expected g.Edges[0].DstArrow to be true: %#v", g.Edges[0].DstArrow)
} }
if g.Edges[0].Attributes.Label.Value != "two" { if g.Edges[0].Label.Value != "two" {
t.Fatalf("expected g.Edges[0].Attributes.Label to be two: %#v", g.Edges[0].Attributes.Label) t.Fatalf("expected g.Edges[0].Label to be two: %#v", g.Edges[0].Label)
} }
}, },
}, },
@ -840,8 +840,8 @@ b: {
if !g.Edges[0].DstArrow { if !g.Edges[0].DstArrow {
t.Fatalf("expected g.Edges[0].DstArrow to be true: %#v", g.Edges[0].DstArrow) t.Fatalf("expected g.Edges[0].DstArrow to be true: %#v", g.Edges[0].DstArrow)
} }
if g.Edges[0].Attributes.Label.Value != "two" { if g.Edges[0].Label.Value != "two" {
t.Fatalf("expected g.Edges[0].Attributes.Label to be two: %#v", g.Edges[0].Attributes.Label) t.Fatalf("expected g.Edges[0].Label to be two: %#v", g.Edges[0].Label)
} }
}, },
}, },
@ -883,8 +883,8 @@ b.(x -> y)[0]: two
if !g.Edges[0].DstArrow { if !g.Edges[0].DstArrow {
t.Fatalf("expected g.Edges[0].DstArrow to be true: %#v", g.Edges[0].DstArrow) t.Fatalf("expected g.Edges[0].DstArrow to be true: %#v", g.Edges[0].DstArrow)
} }
if g.Edges[0].Attributes.Label.Value != "two" { if g.Edges[0].Label.Value != "two" {
t.Fatalf("expected g.Edges[0].Attributes.Label to be two: %#v", g.Edges[0].Attributes.Label) t.Fatalf("expected g.Edges[0].Label to be two: %#v", g.Edges[0].Label)
} }
}, },
}, },
@ -936,8 +936,8 @@ x -> y: {
if g.Edges[0].Dst.ID != "y" { if g.Edges[0].Dst.ID != "y" {
t.Fatalf("expected g.Edges[0].Dst.ID to be y: %#v", g.Edges[0]) t.Fatalf("expected g.Edges[0].Dst.ID to be y: %#v", g.Edges[0])
} }
if g.Edges[0].Attributes.Label.Value != "Space: the final frontier. These are the voyages of the starship Enterprise." { if g.Edges[0].Label.Value != "Space: the final frontier. These are the voyages of the starship Enterprise." {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -950,8 +950,8 @@ x -> y: {
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Label.Value != "asdf" { if g.Edges[0].Label.Value != "asdf" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -972,7 +972,7 @@ x -> y: {
t.Fatalf("expected 2 objects: %#v", g.Objects) t.Fatalf("expected 2 objects: %#v", g.Objects)
} }
assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value) assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
assert.String(t, "", g.Edges[0].Attributes.Shape.Value) assert.String(t, "", g.Edges[0].Shape.Value)
// Make sure the DSL didn't change. this is a regression test where it did // Make sure the DSL didn't change. this is a regression test where it did
exp := `x -> y: { exp := `x -> y: {
source-arrowhead: { source-arrowhead: {
@ -1025,9 +1025,9 @@ x -> y: {
assert.String(t, "Reisner's Rule of Conceptual Inertia", g.Edges[0].SrcArrowhead.Label.Value) assert.String(t, "Reisner's Rule of Conceptual Inertia", g.Edges[0].SrcArrowhead.Label.Value)
assert.String(t, "QOTD", g.Edges[0].DstArrowhead.Label.Value) assert.String(t, "QOTD", g.Edges[0].DstArrowhead.Label.Value)
assert.String(t, "true", g.Edges[0].DstArrowhead.Style.Filled.Value) assert.String(t, "true", g.Edges[0].DstArrowhead.Style.Filled.Value)
assert.String(t, "", g.Edges[0].Attributes.Shape.Value) assert.String(t, "", g.Edges[0].Shape.Value)
assert.String(t, "", g.Edges[0].Attributes.Label.Value) assert.String(t, "", g.Edges[0].Label.Value)
assert.JSON(t, nil, g.Edges[0].Attributes.Style.Filled) assert.JSON(t, nil, g.Edges[0].Style.Filled)
}, },
}, },
{ {
@ -1044,7 +1044,7 @@ x -> y: {
t.Fatalf("expected 2 objects: %#v", g.Objects) t.Fatalf("expected 2 objects: %#v", g.Objects)
} }
assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value) assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
assert.String(t, "", g.Edges[0].Attributes.Shape.Value) assert.String(t, "", g.Edges[0].Shape.Value)
}, },
}, },
{ {
@ -1061,7 +1061,7 @@ x -> y: {
t.Fatalf("expected 2 objects: %#v", g.Objects) t.Fatalf("expected 2 objects: %#v", g.Objects)
} }
assert.String(t, "triangle", g.Edges[0].SrcArrowhead.Shape.Value) assert.String(t, "triangle", g.Edges[0].SrcArrowhead.Shape.Value)
assert.String(t, "", g.Edges[0].Attributes.Shape.Value) assert.String(t, "", g.Edges[0].Shape.Value)
}, },
}, },
{ {
@ -1087,7 +1087,7 @@ x -> y: {
t.Fatalf("expected 2 objects: %#v", g.Objects) t.Fatalf("expected 2 objects: %#v", g.Objects)
} }
assert.String(t, "yo", g.Edges[0].SrcArrowhead.Label.Value) assert.String(t, "yo", g.Edges[0].SrcArrowhead.Label.Value)
assert.String(t, "", g.Edges[0].Attributes.Label.Value) assert.String(t, "", g.Edges[0].Label.Value)
}, },
}, },
{ {
@ -1106,7 +1106,7 @@ x -> y: {
t.Fatalf("expected 2 objects: %#v", g.Objects) t.Fatalf("expected 2 objects: %#v", g.Objects)
} }
assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value) assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
assert.String(t, "", g.Edges[0].Attributes.Shape.Value) assert.String(t, "", g.Edges[0].Shape.Value)
}, },
}, },
{ {
@ -1128,7 +1128,7 @@ x -> y: {
} }
assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value) assert.String(t, "diamond", g.Edges[0].SrcArrowhead.Shape.Value)
assert.String(t, "diamond", g.Edges[0].DstArrowhead.Shape.Value) assert.String(t, "diamond", g.Edges[0].DstArrowhead.Shape.Value)
assert.String(t, "", g.Edges[0].Attributes.Shape.Value) assert.String(t, "", g.Edges[0].Shape.Value)
}, },
}, },
{ {
@ -1143,8 +1143,8 @@ x -> y: {
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Animated.Value != "true" { if g.Edges[0].Style.Animated.Value != "true" {
t.Fatalf("Edges[0].Attributes.Style.Animated.Value: %#v", g.Edges[0].Attributes.Style.Animated.Value) t.Fatalf("Edges[0].Style.Animated.Value: %#v", g.Edges[0].Style.Animated.Value)
} }
}, },
}, },
@ -1201,11 +1201,11 @@ x -> y -> z: {
if len(g.Edges) != 2 { if len(g.Edges) != 2 {
t.Fatalf("expected 2 edge: %#v", g.Edges) t.Fatalf("expected 2 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Label.Value != "Space: the final frontier. These are the voyages of the starship Enterprise." { if g.Edges[0].Label.Value != "Space: the final frontier. These are the voyages of the starship Enterprise." {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
if g.Edges[1].Attributes.Label.Value != "Space: the final frontier. These are the voyages of the starship Enterprise." { if g.Edges[1].Label.Value != "Space: the final frontier. These are the voyages of the starship Enterprise." {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[1].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[1].Label.Value)
} }
}, },
}, },
@ -1226,8 +1226,8 @@ x -> y
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Label.Value != "Space: the final frontier. These are the voyages of the starship Enterprise." { if g.Edges[0].Label.Value != "Space: the final frontier. These are the voyages of the starship Enterprise." {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1249,8 +1249,8 @@ x -> y: {
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatalf("unexpected g.Edges[0].Attributes.Style.Opacity.Value: %#v", g.Edges[0].Attributes.Style.Opacity.Value) t.Fatalf("unexpected g.Edges[0].Style.Opacity.Value: %#v", g.Edges[0].Style.Opacity.Value)
} }
}, },
}, },
@ -1270,11 +1270,11 @@ x -> y: {
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatalf("unexpected g.Edges[0].Attributes.Style.Opacity.Value: %#v", g.Edges[0].Attributes.Style.Opacity.Value) t.Fatalf("unexpected g.Edges[0].Style.Opacity.Value: %#v", g.Edges[0].Style.Opacity.Value)
} }
if g.Edges[0].Attributes.Label.Value != "" { if g.Edges[0].Label.Value != "" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1293,11 +1293,11 @@ x -> y
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatalf("unexpected g.Edges[0].Attributes.Style.Opacity.Value: %#v", g.Edges[0].Attributes.Style.Opacity.Value) t.Fatalf("unexpected g.Edges[0].Style.Opacity.Value: %#v", g.Edges[0].Style.Opacity.Value)
} }
if g.Edges[0].Attributes.Label.Value != "" { if g.Edges[0].Label.Value != "" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1317,11 +1317,11 @@ x -> y
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatalf("unexpected g.Edges[0].Attributes.Style.Opacity.Value: %#v", g.Edges[0].Attributes.Style.Opacity.Value) t.Fatalf("unexpected g.Edges[0].Style.Opacity.Value: %#v", g.Edges[0].Style.Opacity.Value)
} }
if g.Edges[0].Attributes.Label.Value != "" { if g.Edges[0].Label.Value != "" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1342,11 +1342,11 @@ x.(a -> b)[0].style.opacity: 0.4
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatalf("unexpected g.Edges[0].Attributes.Style.Opacity.Value: %#v", g.Edges[0].Attributes.Style.Opacity.Value) t.Fatalf("unexpected g.Edges[0].Style.Opacity.Value: %#v", g.Edges[0].Style.Opacity.Value)
} }
if g.Edges[0].Attributes.Label.Value != "" { if g.Edges[0].Label.Value != "" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1367,11 +1367,11 @@ x: {
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatalf("unexpected g.Edges[0].Attributes.Style.Opacity.Value: %#v", g.Edges[0].Attributes.Style.Opacity.Value) t.Fatalf("unexpected g.Edges[0].Style.Opacity.Value: %#v", g.Edges[0].Style.Opacity.Value)
} }
if g.Edges[0].Attributes.Label.Value != "" { if g.Edges[0].Label.Value != "" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1396,11 +1396,11 @@ x: {
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatalf("unexpected g.Edges[0].Attributes.Style.Opacity.Value: %#v", g.Edges[0].Attributes.Style.Opacity.Value) t.Fatalf("unexpected g.Edges[0].Style.Opacity.Value: %#v", g.Edges[0].Style.Opacity.Value)
} }
if g.Edges[0].Attributes.Label.Value != "" { if g.Edges[0].Label.Value != "" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1423,11 +1423,11 @@ x: {
if len(g.Edges) != 1 { if len(g.Edges) != 1 {
t.Fatalf("expected 1 edge: %#v", g.Edges) t.Fatalf("expected 1 edge: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatalf("unexpected g.Edges[0].Attributes.Style.Opacity.Value: %#v", g.Edges[0].Attributes.Style.Opacity.Value) t.Fatalf("unexpected g.Edges[0].Style.Opacity.Value: %#v", g.Edges[0].Style.Opacity.Value)
} }
if g.Edges[0].Attributes.Label.Value != "" { if g.Edges[0].Label.Value != "" {
t.Fatalf("unexpected g.Edges[0].Attributes.Label.Value : %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("unexpected g.Edges[0].Label.Value : %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1452,8 +1452,8 @@ x -> y: {
if len(g.Objects) != 1 { if len(g.Objects) != 1 {
t.Fatal(g.Objects) t.Fatal(g.Objects)
} }
if g.Objects[0].Attributes.Link.Value != "https://google.com" { if g.Objects[0].Link.Value != "https://google.com" {
t.Fatal(g.Objects[0].Attributes.Link.Value) t.Fatal(g.Objects[0].Link.Value)
} }
}, },
}, },
@ -1465,8 +1465,8 @@ x -> y: {
t.Fatal(g.Objects) t.Fatal(g.Objects)
} }
if g.Objects[0].Attributes.Tooltip.Value != "https://google.com" { if g.Objects[0].Tooltip.Value != "https://google.com" {
t.Fatal(g.Objects[0].Attributes.Tooltip.Value) t.Fatal(g.Objects[0].Tooltip.Value)
} }
}, },
}, },
@ -1482,12 +1482,12 @@ x -> y: {
if len(g.Objects) != 1 { if len(g.Objects) != 1 {
t.Fatal(g.Objects) t.Fatal(g.Objects)
} }
if g.Objects[0].Attributes.Link.Value != "https://google.com" { if g.Objects[0].Link.Value != "https://google.com" {
t.Fatal(g.Objects[0].Attributes.Link.Value) t.Fatal(g.Objects[0].Link.Value)
} }
if g.Objects[0].Attributes.Tooltip.Value != "hello world" { if g.Objects[0].Tooltip.Value != "hello world" {
t.Fatal(g.Objects[0].Attributes.Tooltip.Value) t.Fatal(g.Objects[0].Tooltip.Value)
} }
}, },
}, },
@ -1517,8 +1517,8 @@ b: {
if len(g.Objects) != 1 { if len(g.Objects) != 1 {
t.Fatal(g.Objects) t.Fatal(g.Objects)
} }
if g.Objects[0].Attributes.Link.Value != "Overview.Untitled board 7.zzzzz" { if g.Objects[0].Link.Value != "Overview.Untitled board 7.zzzzz" {
t.Fatal(g.Objects[0].Attributes.Link.Value) t.Fatal(g.Objects[0].Link.Value)
} }
}, },
}, },
@ -1603,20 +1603,20 @@ y
if len(g.Objects) != 2 { if len(g.Objects) != 2 {
t.Fatal(g.Objects) t.Fatal(g.Objects)
} }
if g.Objects[0].Attributes.NearKey == nil { if g.Objects[0].NearKey == nil {
t.Fatal("missing near key") t.Fatal("missing near key")
} }
if g.Objects[0].Attributes.Icon.Path != "orange" { if g.Objects[0].Icon.Path != "orange" {
t.Fatal(g.Objects[0].Attributes.Icon) t.Fatal(g.Objects[0].Icon)
} }
if g.Objects[0].Attributes.Style.Opacity.Value != "0.5" { if g.Objects[0].Style.Opacity.Value != "0.5" {
t.Fatal(g.Objects[0].Attributes.Style.Opacity) t.Fatal(g.Objects[0].Style.Opacity)
} }
if g.Objects[0].Attributes.Style.Stroke.Value != "red" { if g.Objects[0].Style.Stroke.Value != "red" {
t.Fatal(g.Objects[0].Attributes.Style.Stroke) t.Fatal(g.Objects[0].Style.Stroke)
} }
if g.Objects[0].Attributes.Style.Fill.Value != "green" { if g.Objects[0].Style.Fill.Value != "green" {
t.Fatal(g.Objects[0].Attributes.Style.Fill) t.Fatal(g.Objects[0].Style.Fill)
} }
}, },
}, },
@ -1696,7 +1696,7 @@ y -> x.style
} }
assert.String(t, `"b\nb"`, g.Objects[0].ID) assert.String(t, `"b\nb"`, g.Objects[0].ID)
assert.String(t, `b assert.String(t, `b
b`, g.Objects[0].Attributes.Label.Value) b`, g.Objects[0].Label.Value)
}, },
}, },
{ {
@ -1708,7 +1708,7 @@ b`, g.Objects[0].Attributes.Label.Value)
t.Fatal(g.Objects) t.Fatal(g.Objects)
} }
assert.String(t, "b\rb", g.Objects[0].ID) assert.String(t, "b\rb", g.Objects[0].ID)
assert.String(t, "b\rb", g.Objects[0].Attributes.Label.Value) assert.String(t, "b\rb", g.Objects[0].Label.Value)
}, },
}, },
{ {
@ -1730,8 +1730,8 @@ b`, g.Objects[0].Attributes.Label.Value)
if len(g.Objects[0].Class.Methods) != 0 { if len(g.Objects[0].Class.Methods) != 0 {
t.Fatal(len(g.Objects[0].Class.Methods)) t.Fatal(len(g.Objects[0].Class.Methods))
} }
if g.Objects[0].Attributes.Style.Opacity.Value != "0.4" { if g.Objects[0].Style.Opacity.Value != "0.4" {
t.Fatal(g.Objects[0].Attributes.Style.Opacity.Value) t.Fatal(g.Objects[0].Style.Opacity.Value)
} }
}, },
}, },
@ -1751,8 +1751,8 @@ b`, g.Objects[0].Attributes.Label.Value)
if len(g.Objects[0].SQLTable.Columns) != 1 { if len(g.Objects[0].SQLTable.Columns) != 1 {
t.Fatal(len(g.Objects[0].SQLTable.Columns)) t.Fatal(len(g.Objects[0].SQLTable.Columns))
} }
if g.Objects[0].Attributes.Style.Opacity.Value != "0.4" { if g.Objects[0].Style.Opacity.Value != "0.4" {
t.Fatal(g.Objects[0].Attributes.Style.Opacity.Value) t.Fatal(g.Objects[0].Style.Opacity.Value)
} }
}, },
}, },
@ -1775,8 +1775,8 @@ b`, g.Objects[0].Attributes.Label.Value)
if len(g.Objects[0].SQLTable.Columns) != 1 { if len(g.Objects[0].SQLTable.Columns) != 1 {
t.Fatal(len(g.Objects[0].SQLTable.Columns)) t.Fatal(len(g.Objects[0].SQLTable.Columns))
} }
if g.Objects[0].Attributes.Style.Opacity.Value != "0.4" { if g.Objects[0].Style.Opacity.Value != "0.4" {
t.Fatal(g.Objects[0].Attributes.Style.Opacity.Value) t.Fatal(g.Objects[0].Style.Opacity.Value)
} }
}, },
}, },
@ -1796,7 +1796,7 @@ x.y -> a.b: {
} }
`, `,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, "true", g.Edges[0].Attributes.Style.Animated.Value) tassert.Equal(t, "true", g.Edges[0].Style.Animated.Value)
}, },
}, },
{ {
@ -1901,7 +1901,7 @@ dst.id <-> src.dst_id
} }
`, `,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
assert.String(t, "sequence_diagram", g.Objects[0].Attributes.Shape.Value) assert.String(t, "sequence_diagram", g.Objects[0].Shape.Value)
}, },
}, },
{ {
@ -1948,7 +1948,7 @@ b
text: `shape: sequence_diagram text: `shape: sequence_diagram
`, `,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
assert.String(t, "sequence_diagram", g.Root.Attributes.Shape.Value) assert.String(t, "sequence_diagram", g.Root.Shape.Value)
}, },
}, },
{ {
@ -2028,7 +2028,7 @@ ok: {
text: `direction: right`, text: `direction: right`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
assert.String(t, "right", g.Root.Attributes.Direction.Value) assert.String(t, "right", g.Root.Direction.Value)
}, },
}, },
{ {
@ -2036,7 +2036,7 @@ ok: {
text: `x`, text: `x`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
assert.String(t, "", g.Objects[0].Attributes.Direction.Value) assert.String(t, "", g.Objects[0].Direction.Value)
}, },
}, },
{ {
@ -2046,7 +2046,7 @@ ok: {
direction: left direction: left
}`, }`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
assert.String(t, "left", g.Objects[0].Attributes.Direction.Value) assert.String(t, "left", g.Objects[0].Direction.Value)
}, },
}, },
{ {
@ -2057,7 +2057,7 @@ ok: {
constraint: BIZ constraint: BIZ
}`, }`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
assert.String(t, "bar", g.Objects[0].Attributes.Label.Value) assert.String(t, "bar", g.Objects[0].Label.Value)
}, },
}, },
{ {
@ -2151,7 +2151,7 @@ layers: {
} }
}`, }`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, "root.layers.x", g.Objects[0].Attributes.Link.Value) tassert.Equal(t, "root.layers.x", g.Objects[0].Link.Value)
}, },
}, },
{ {
@ -2171,8 +2171,8 @@ scenarios: {
} }
}`, }`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, "root.layers.cat", g.Objects[0].Attributes.Link.Value) tassert.Equal(t, "root.layers.cat", g.Objects[0].Link.Value)
tassert.Equal(t, "root.layers.cat", g.Scenarios[0].Objects[0].Attributes.Link.Value) tassert.Equal(t, "root.layers.cat", g.Scenarios[0].Objects[0].Link.Value)
}, },
}, },
{ {
@ -2205,7 +2205,7 @@ layers: {
} }
}`, }`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, "root.layers.x.layers.x", g.Objects[0].Attributes.Link.Value) tassert.Equal(t, "root.layers.x.layers.x", g.Objects[0].Link.Value)
}, },
}, },
{ {
@ -2219,7 +2219,7 @@ layers: {
} }
}`, }`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, "root.layers.x", g.Objects[1].Attributes.Link.Value) tassert.Equal(t, "root.layers.x", g.Objects[1].Link.Value)
}, },
}, },
{ {
@ -2237,9 +2237,9 @@ layers: {
} }
}`, }`,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.NotNil(t, g.Layers[0].Layers[0].Objects[0].Attributes.Link.Value) tassert.NotNil(t, g.Layers[0].Layers[0].Objects[0].Link.Value)
tassert.Equal(t, "root.layers.x", g.Layers[0].Layers[0].Objects[0].Attributes.Link.Value) tassert.Equal(t, "root.layers.x", g.Layers[0].Layers[0].Objects[0].Link.Value)
tassert.Equal(t, "root.layers.x", g.Layers[0].Layers[0].Objects[1].Attributes.Link.Value) tassert.Equal(t, "root.layers.x", g.Layers[0].Layers[0].Objects[1].Link.Value)
}, },
}, },
{ {
@ -2289,7 +2289,7 @@ obj {
} }
`, `,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, "200", g.Objects[0].Attributes.GridRows.Value) tassert.Equal(t, "200", g.Objects[0].GridRows.Value)
}, },
}, },
{ {
@ -2362,16 +2362,16 @@ nostar -> 1star: { class: path }
`, `,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, 3, len(g.Objects)) tassert.Equal(t, 3, len(g.Objects))
tassert.Equal(t, "dragon_ball", g.Objects[0].Attributes.Classes[0]) tassert.Equal(t, "dragon_ball", g.Objects[0].Classes[0])
tassert.Equal(t, "", g.Objects[0].Attributes.Label.Value) tassert.Equal(t, "", g.Objects[0].Label.Value)
// Class field overrides primary // Class field overrides primary
tassert.Equal(t, "", g.Objects[1].Attributes.Label.Value) tassert.Equal(t, "", g.Objects[1].Label.Value)
tassert.Equal(t, "**", g.Objects[2].Attributes.Label.Value) tassert.Equal(t, "**", g.Objects[2].Label.Value)
tassert.Equal(t, "orange", g.Objects[0].Attributes.Style.Fill.Value) tassert.Equal(t, "orange", g.Objects[0].Style.Fill.Value)
tassert.Equal(t, "red", g.Objects[1].Attributes.Style.Fill.Value) tassert.Equal(t, "red", g.Objects[1].Style.Fill.Value)
tassert.Equal(t, "4", g.Edges[0].Attributes.Style.StrokeWidth.Value) tassert.Equal(t, "4", g.Edges[0].Style.StrokeWidth.Value)
tassert.Equal(t, "then", g.Edges[0].Attributes.Label.Value) tassert.Equal(t, "then", g.Edges[0].Label.Value)
}, },
}, },
{ {
@ -2386,7 +2386,7 @@ classes.x.shape: diamond
`, `,
assertions: func(t *testing.T, g *d2graph.Graph) { assertions: func(t *testing.T, g *d2graph.Graph) {
tassert.Equal(t, 1, len(g.Objects)) tassert.Equal(t, 1, len(g.Objects))
tassert.Equal(t, "diamond", g.Objects[0].Attributes.Shape.Value) tassert.Equal(t, "diamond", g.Objects[0].Shape.Value)
}, },
}, },
{ {

View file

@ -42,10 +42,10 @@ func Export(ctx context.Context, g *d2graph.Graph, fontFamily *d2fonts.FontFamil
func applyTheme(shape *d2target.Shape, obj *d2graph.Object, theme *d2themes.Theme) { func applyTheme(shape *d2target.Shape, obj *d2graph.Object, theme *d2themes.Theme) {
shape.Stroke = obj.GetStroke(shape.StrokeDash) shape.Stroke = obj.GetStroke(shape.StrokeDash)
shape.Fill = obj.GetFill() shape.Fill = obj.GetFill()
if obj.Attributes.Shape.Value == d2target.ShapeText { if obj.Shape.Value == d2target.ShapeText {
shape.Color = color.N1 shape.Color = color.N1
} }
if obj.Attributes.Shape.Value == d2target.ShapeSQLTable || obj.Attributes.Shape.Value == d2target.ShapeClass { if obj.Shape.Value == d2target.ShapeSQLTable || obj.Shape.Value == d2target.ShapeClass {
shape.PrimaryAccentColor = color.B2 shape.PrimaryAccentColor = color.B2
shape.SecondaryAccentColor = color.AA2 shape.SecondaryAccentColor = color.AA2
shape.NeutralAccentColor = color.N2 shape.NeutralAccentColor = color.N2
@ -72,64 +72,64 @@ func applyTheme(shape *d2target.Shape, obj *d2graph.Object, theme *d2themes.Them
} }
func applyStyles(shape *d2target.Shape, obj *d2graph.Object) { func applyStyles(shape *d2target.Shape, obj *d2graph.Object) {
if obj.Attributes.Style.Opacity != nil { if obj.Style.Opacity != nil {
shape.Opacity, _ = strconv.ParseFloat(obj.Attributes.Style.Opacity.Value, 64) shape.Opacity, _ = strconv.ParseFloat(obj.Style.Opacity.Value, 64)
} }
if obj.Attributes.Style.StrokeDash != nil { if obj.Style.StrokeDash != nil {
shape.StrokeDash, _ = strconv.ParseFloat(obj.Attributes.Style.StrokeDash.Value, 64) shape.StrokeDash, _ = strconv.ParseFloat(obj.Style.StrokeDash.Value, 64)
} }
if obj.Attributes.Style.Fill != nil { if obj.Style.Fill != nil {
shape.Fill = obj.Attributes.Style.Fill.Value shape.Fill = obj.Style.Fill.Value
} else if obj.Attributes.Shape.Value == d2target.ShapeText { } else if obj.Shape.Value == d2target.ShapeText {
shape.Fill = "transparent" shape.Fill = "transparent"
} }
if obj.Attributes.Style.FillPattern != nil { if obj.Style.FillPattern != nil {
shape.FillPattern = obj.Attributes.Style.FillPattern.Value shape.FillPattern = obj.Style.FillPattern.Value
} }
if obj.Attributes.Style.Stroke != nil { if obj.Style.Stroke != nil {
shape.Stroke = obj.Attributes.Style.Stroke.Value shape.Stroke = obj.Style.Stroke.Value
} }
if obj.Attributes.Style.StrokeWidth != nil { if obj.Style.StrokeWidth != nil {
shape.StrokeWidth, _ = strconv.Atoi(obj.Attributes.Style.StrokeWidth.Value) shape.StrokeWidth, _ = strconv.Atoi(obj.Style.StrokeWidth.Value)
} }
if obj.Attributes.Style.Shadow != nil { if obj.Style.Shadow != nil {
shape.Shadow, _ = strconv.ParseBool(obj.Attributes.Style.Shadow.Value) shape.Shadow, _ = strconv.ParseBool(obj.Style.Shadow.Value)
} }
if obj.Attributes.Style.ThreeDee != nil { if obj.Style.ThreeDee != nil {
shape.ThreeDee, _ = strconv.ParseBool(obj.Attributes.Style.ThreeDee.Value) shape.ThreeDee, _ = strconv.ParseBool(obj.Style.ThreeDee.Value)
} }
if obj.Attributes.Style.Multiple != nil { if obj.Style.Multiple != nil {
shape.Multiple, _ = strconv.ParseBool(obj.Attributes.Style.Multiple.Value) shape.Multiple, _ = strconv.ParseBool(obj.Style.Multiple.Value)
} }
if obj.Attributes.Style.BorderRadius != nil { if obj.Style.BorderRadius != nil {
shape.BorderRadius, _ = strconv.Atoi(obj.Attributes.Style.BorderRadius.Value) shape.BorderRadius, _ = strconv.Atoi(obj.Style.BorderRadius.Value)
} }
if obj.Attributes.Style.FontColor != nil { if obj.Style.FontColor != nil {
shape.Color = obj.Attributes.Style.FontColor.Value shape.Color = obj.Style.FontColor.Value
} }
if obj.Attributes.Style.Italic != nil { if obj.Style.Italic != nil {
shape.Italic, _ = strconv.ParseBool(obj.Attributes.Style.Italic.Value) shape.Italic, _ = strconv.ParseBool(obj.Style.Italic.Value)
} }
if obj.Attributes.Style.Bold != nil { if obj.Style.Bold != nil {
shape.Bold, _ = strconv.ParseBool(obj.Attributes.Style.Bold.Value) shape.Bold, _ = strconv.ParseBool(obj.Style.Bold.Value)
} }
if obj.Attributes.Style.Underline != nil { if obj.Style.Underline != nil {
shape.Underline, _ = strconv.ParseBool(obj.Attributes.Style.Underline.Value) shape.Underline, _ = strconv.ParseBool(obj.Style.Underline.Value)
} }
if obj.Attributes.Style.Font != nil { if obj.Style.Font != nil {
shape.FontFamily = obj.Attributes.Style.Font.Value shape.FontFamily = obj.Style.Font.Value
} }
if obj.Attributes.Style.DoubleBorder != nil { if obj.Style.DoubleBorder != nil {
shape.DoubleBorder, _ = strconv.ParseBool(obj.Attributes.Style.DoubleBorder.Value) shape.DoubleBorder, _ = strconv.ParseBool(obj.Style.DoubleBorder.Value)
} }
} }
func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape { func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape {
shape := d2target.BaseShape() shape := d2target.BaseShape()
shape.SetType(obj.Attributes.Shape.Value) shape.SetType(obj.Shape.Value)
shape.ID = obj.AbsID() shape.ID = obj.AbsID()
shape.Classes = obj.Attributes.Classes shape.Classes = obj.Classes
shape.ZIndex = obj.ZIndex shape.ZIndex = obj.ZIndex
shape.Level = int(obj.Level()) shape.Level = int(obj.Level())
shape.Pos = d2target.NewPoint(int(obj.TopLeft.X), int(obj.TopLeft.Y)) shape.Pos = d2target.NewPoint(int(obj.TopLeft.X), int(obj.TopLeft.Y))
@ -155,10 +155,10 @@ func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape {
shape.Color = text.GetColor(shape.Italic) shape.Color = text.GetColor(shape.Italic)
applyStyles(shape, obj) applyStyles(shape, obj)
switch obj.Attributes.Shape.Value { switch obj.Shape.Value {
case d2target.ShapeCode, d2target.ShapeText: case d2target.ShapeCode, d2target.ShapeText:
shape.Language = obj.Attributes.Language shape.Language = obj.Language
shape.Label = obj.Attributes.Label.Value shape.Label = obj.Label.Value
case d2target.ShapeClass: case d2target.ShapeClass:
shape.Class = *obj.Class shape.Class = *obj.Class
// The label is the header for classes and tables, which is set in client to be 4 px larger than the object's set font size // The label is the header for classes and tables, which is set in client to be 4 px larger than the object's set font size
@ -178,13 +178,13 @@ func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape {
} }
} }
if obj.Attributes.Tooltip != nil { if obj.Tooltip != nil {
shape.Tooltip = obj.Attributes.Tooltip.Value shape.Tooltip = obj.Tooltip.Value
} }
if obj.Attributes.Link != nil { if obj.Link != nil {
shape.Link = obj.Attributes.Link.Value shape.Link = obj.Link.Value
} }
shape.Icon = obj.Attributes.Icon shape.Icon = obj.Icon
if obj.IconPosition != nil { if obj.IconPosition != nil {
shape.IconPosition = *obj.IconPosition shape.IconPosition = *obj.IconPosition
} }
@ -195,7 +195,7 @@ func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape {
func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection { func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection {
connection := d2target.BaseConnection() connection := d2target.BaseConnection()
connection.ID = edge.AbsID() connection.ID = edge.AbsID()
connection.Classes = edge.Attributes.Classes connection.Classes = edge.Classes
connection.ZIndex = edge.ZIndex connection.ZIndex = edge.ZIndex
text := edge.Text() text := edge.Text()
@ -236,60 +236,60 @@ func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection
if theme != nil && theme.SpecialRules.NoCornerRadius { if theme != nil && theme.SpecialRules.NoCornerRadius {
connection.BorderRadius = 0 connection.BorderRadius = 0
} }
if edge.Attributes.Style.BorderRadius != nil { if edge.Style.BorderRadius != nil {
connection.BorderRadius, _ = strconv.ParseFloat(edge.Attributes.Style.BorderRadius.Value, 64) connection.BorderRadius, _ = strconv.ParseFloat(edge.Style.BorderRadius.Value, 64)
} }
if edge.Attributes.Style.Opacity != nil { if edge.Style.Opacity != nil {
connection.Opacity, _ = strconv.ParseFloat(edge.Attributes.Style.Opacity.Value, 64) connection.Opacity, _ = strconv.ParseFloat(edge.Style.Opacity.Value, 64)
} }
if edge.Attributes.Style.StrokeDash != nil { if edge.Style.StrokeDash != nil {
connection.StrokeDash, _ = strconv.ParseFloat(edge.Attributes.Style.StrokeDash.Value, 64) connection.StrokeDash, _ = strconv.ParseFloat(edge.Style.StrokeDash.Value, 64)
} }
connection.Stroke = edge.GetStroke(connection.StrokeDash) connection.Stroke = edge.GetStroke(connection.StrokeDash)
if edge.Attributes.Style.Stroke != nil { if edge.Style.Stroke != nil {
connection.Stroke = edge.Attributes.Style.Stroke.Value connection.Stroke = edge.Style.Stroke.Value
} }
if edge.Attributes.Style.StrokeWidth != nil { if edge.Style.StrokeWidth != nil {
connection.StrokeWidth, _ = strconv.Atoi(edge.Attributes.Style.StrokeWidth.Value) connection.StrokeWidth, _ = strconv.Atoi(edge.Style.StrokeWidth.Value)
} }
if edge.Attributes.Style.Fill != nil { if edge.Style.Fill != nil {
connection.Fill = edge.Attributes.Style.Fill.Value connection.Fill = edge.Style.Fill.Value
} }
connection.FontSize = text.FontSize connection.FontSize = text.FontSize
if edge.Attributes.Style.FontSize != nil { if edge.Style.FontSize != nil {
connection.FontSize, _ = strconv.Atoi(edge.Attributes.Style.FontSize.Value) connection.FontSize, _ = strconv.Atoi(edge.Style.FontSize.Value)
} }
if edge.Attributes.Style.Animated != nil { if edge.Style.Animated != nil {
connection.Animated, _ = strconv.ParseBool(edge.Attributes.Style.Animated.Value) connection.Animated, _ = strconv.ParseBool(edge.Style.Animated.Value)
} }
if edge.Attributes.Tooltip != nil { if edge.Tooltip != nil {
connection.Tooltip = edge.Attributes.Tooltip.Value connection.Tooltip = edge.Tooltip.Value
} }
connection.Icon = edge.Attributes.Icon connection.Icon = edge.Icon
if edge.Attributes.Style.Italic != nil { if edge.Style.Italic != nil {
connection.Italic, _ = strconv.ParseBool(edge.Attributes.Style.Italic.Value) connection.Italic, _ = strconv.ParseBool(edge.Style.Italic.Value)
} }
connection.Color = text.GetColor(connection.Italic) connection.Color = text.GetColor(connection.Italic)
if edge.Attributes.Style.FontColor != nil { if edge.Style.FontColor != nil {
connection.Color = edge.Attributes.Style.FontColor.Value connection.Color = edge.Style.FontColor.Value
} }
if edge.Attributes.Style.Bold != nil { if edge.Style.Bold != nil {
connection.Bold, _ = strconv.ParseBool(edge.Attributes.Style.Bold.Value) connection.Bold, _ = strconv.ParseBool(edge.Style.Bold.Value)
} }
if theme != nil && theme.SpecialRules.Mono { if theme != nil && theme.SpecialRules.Mono {
connection.FontFamily = "mono" connection.FontFamily = "mono"
} }
if edge.Attributes.Style.Font != nil { if edge.Style.Font != nil {
connection.FontFamily = edge.Attributes.Style.Font.Value connection.FontFamily = edge.Style.Font.Value
} }
connection.Label = text.Text connection.Label = text.Text
connection.LabelWidth = text.Dimensions.Width connection.LabelWidth = text.Dimensions.Width

View file

@ -56,10 +56,9 @@ type Graph struct {
func NewGraph() *Graph { func NewGraph() *Graph {
d := &Graph{} d := &Graph{}
d.Root = &Object{ d.Root = &Object{
Graph: d, Graph: d,
Parent: nil, Parent: nil,
Children: make(map[string]*Object), Children: make(map[string]*Object),
Attributes: &Attributes{},
} }
return d return d
} }
@ -90,11 +89,10 @@ type Object struct {
// IDVal: yes'" // IDVal: yes'"
// //
// ID allows joining on . naively and construct a valid D2 key path // ID allows joining on . naively and construct a valid D2 key path
ID string `json:"id"` ID string `json:"id"`
IDVal string `json:"id_val"` IDVal string `json:"id_val"`
Map *d2ast.Map `json:"-"` Map *d2ast.Map `json:"-"`
LabelDimensions d2target.TextDimensions `json:"label_dimensions"` References []Reference `json:"references,omitempty"`
References []Reference `json:"references,omitempty"`
*geo.Box `json:"box,omitempty"` *geo.Box `json:"box,omitempty"`
LabelPosition *string `json:"labelPosition,omitempty"` LabelPosition *string `json:"labelPosition,omitempty"`
@ -106,20 +104,22 @@ type Object struct {
Children map[string]*Object `json:"-"` Children map[string]*Object `json:"-"`
ChildrenArray []*Object `json:"-"` ChildrenArray []*Object `json:"-"`
Attributes *Attributes `json:"attributes,omitempty"` Attributes `json:"attributes"`
ZIndex int `json:"zIndex"` ZIndex int `json:"zIndex"`
} }
type Attributes struct { type Attributes struct {
Label Scalar `json:"label"` Label Scalar `json:"label"`
LabelDimensions d2target.TextDimensions `json:"labelDimensions"`
Style Style `json:"style"` Style Style `json:"style"`
Icon *url.URL `json:"icon,omitempty"` Icon *url.URL `json:"icon,omitempty"`
Tooltip *Scalar `json:"tooltip,omitempty"` Tooltip *Scalar `json:"tooltip,omitempty"`
Link *Scalar `json:"link,omitempty"` Link *Scalar `json:"link,omitempty"`
Width *Scalar `json:"width,omitempty"` WidthAttr *Scalar `json:"width,omitempty"`
Height *Scalar `json:"height,omitempty"` HeightAttr *Scalar `json:"height,omitempty"`
Top *Scalar `json:"top,omitempty"` Top *Scalar `json:"top,omitempty"`
Left *Scalar `json:"left,omitempty"` Left *Scalar `json:"left,omitempty"`
@ -412,7 +412,7 @@ func (l ContainerLevel) LabelSize() int {
func (obj *Object) GetFill() string { func (obj *Object) GetFill() string {
level := int(obj.Level()) level := int(obj.Level())
shape := obj.Attributes.Shape.Value shape := obj.Shape.Value
if strings.EqualFold(shape, d2target.ShapeSQLTable) || strings.EqualFold(shape, d2target.ShapeClass) { if strings.EqualFold(shape, d2target.ShapeSQLTable) || strings.EqualFold(shape, d2target.ShapeClass) {
return color.N1 return color.N1
@ -491,7 +491,7 @@ func (obj *Object) GetFill() string {
} }
func (obj *Object) GetStroke(dashGapSize interface{}) string { func (obj *Object) GetStroke(dashGapSize interface{}) string {
shape := obj.Attributes.Shape.Value shape := obj.Shape.Value
if strings.EqualFold(shape, d2target.ShapeCode) || if strings.EqualFold(shape, d2target.ShapeCode) ||
strings.EqualFold(shape, d2target.ShapeText) { strings.EqualFold(shape, d2target.ShapeText) {
return color.N1 return color.N1
@ -518,10 +518,10 @@ func (obj *Object) IsContainer() bool {
} }
func (obj *Object) HasOutsideBottomLabel() bool { func (obj *Object) HasOutsideBottomLabel() bool {
if obj == nil || obj.Attributes == nil { if obj == nil {
return false return false
} }
switch obj.Attributes.Shape.Value { switch obj.Shape.Value {
case d2target.ShapeImage, d2target.ShapePerson: case d2target.ShapeImage, d2target.ShapePerson:
return true return true
default: default:
@ -530,14 +530,14 @@ func (obj *Object) HasOutsideBottomLabel() bool {
} }
func (obj *Object) HasLabel() bool { func (obj *Object) HasLabel() bool {
if obj == nil || obj.Attributes == nil { if obj == nil {
return false return false
} }
switch obj.Attributes.Shape.Value { switch obj.Shape.Value {
case d2target.ShapeText, d2target.ShapeClass, d2target.ShapeSQLTable, d2target.ShapeCode: case d2target.ShapeText, d2target.ShapeClass, d2target.ShapeSQLTable, d2target.ShapeCode:
return false return false
default: default:
return obj.Attributes.Label.Value != "" return obj.Label.Value != ""
} }
} }
@ -556,12 +556,12 @@ func (obj *Object) AbsIDArray() []string {
} }
func (obj *Object) Text() *d2target.MText { func (obj *Object) Text() *d2target.MText {
isBold := !obj.IsContainer() && obj.Attributes.Shape.Value != "text" isBold := !obj.IsContainer() && obj.Shape.Value != "text"
isItalic := false isItalic := false
if obj.Attributes.Style.Bold != nil && obj.Attributes.Style.Bold.Value == "true" { if obj.Style.Bold != nil && obj.Style.Bold.Value == "true" {
isBold = true isBold = true
} }
if obj.Attributes.Style.Italic != nil && obj.Attributes.Style.Italic.Value == "true" { if obj.Style.Italic != nil && obj.Style.Italic.Value == "true" {
isItalic = true isItalic = true
} }
fontSize := d2fonts.FONT_SIZE_M fontSize := d2fonts.FONT_SIZE_M
@ -571,14 +571,14 @@ func (obj *Object) Text() *d2target.MText {
} }
if obj.OuterSequenceDiagram() == nil { if obj.OuterSequenceDiagram() == nil {
if obj.IsContainer() && obj.Attributes.Shape.Value != "text" { if obj.IsContainer() && obj.Shape.Value != "text" {
fontSize = obj.Level().LabelSize() fontSize = obj.Level().LabelSize()
} }
} else { } else {
isBold = false isBold = false
} }
if obj.Attributes.Style.FontSize != nil { if obj.Style.FontSize != nil {
fontSize, _ = strconv.Atoi(obj.Attributes.Style.FontSize.Value) fontSize, _ = strconv.Atoi(obj.Style.FontSize.Value)
} }
// Class and Table objects have Label set to header // Class and Table objects have Label set to header
if obj.Class != nil || obj.SQLTable != nil { if obj.Class != nil || obj.SQLTable != nil {
@ -588,12 +588,12 @@ func (obj *Object) Text() *d2target.MText {
isBold = false isBold = false
} }
return &d2target.MText{ return &d2target.MText{
Text: obj.Attributes.Label.Value, Text: obj.Label.Value,
FontSize: fontSize, FontSize: fontSize,
IsBold: isBold, IsBold: isBold,
IsItalic: isItalic, IsItalic: isItalic,
Language: obj.Attributes.Language, Language: obj.Language,
Shape: obj.Attributes.Shape.Value, Shape: obj.Shape.Value,
Dimensions: obj.LabelDimensions, Dimensions: obj.LabelDimensions,
} }
@ -608,7 +608,7 @@ func (obj *Object) newObject(id string) *Object {
child := &Object{ child := &Object{
ID: id, ID: id,
IDVal: idval, IDVal: idval,
Attributes: &Attributes{ Attributes: Attributes{
Label: Scalar{ Label: Scalar{
Value: idval, Value: idval,
}, },
@ -786,7 +786,7 @@ func (obj *Object) FindEdges(mk *d2ast.Key) ([]*Edge, bool) {
func (obj *Object) ensureChildEdge(ida []string) *Object { func (obj *Object) ensureChildEdge(ida []string) *Object {
for i := range ida { for i := range ida {
switch obj.Attributes.Shape.Value { switch obj.Shape.Value {
case d2target.ShapeClass, d2target.ShapeSQLTable: case d2target.ShapeClass, d2target.ShapeSQLTable:
// This will only be called for connecting edges where we want to truncate to the // This will only be called for connecting edges where we want to truncate to the
// container. // container.
@ -865,23 +865,23 @@ func (obj *Object) AppendReferences(ida []string, ref Reference, unresolvedObj *
} }
func (obj *Object) GetLabelSize(mtexts []*d2target.MText, ruler *textmeasure.Ruler, fontFamily *d2fonts.FontFamily) (*d2target.TextDimensions, error) { func (obj *Object) GetLabelSize(mtexts []*d2target.MText, ruler *textmeasure.Ruler, fontFamily *d2fonts.FontFamily) (*d2target.TextDimensions, error) {
shapeType := strings.ToLower(obj.Attributes.Shape.Value) shapeType := strings.ToLower(obj.Shape.Value)
if obj.Attributes.Style.Font != nil { if obj.Style.Font != nil {
f := d2fonts.D2_FONT_TO_FAMILY[obj.Attributes.Style.Font.Value] f := d2fonts.D2_FONT_TO_FAMILY[obj.Style.Font.Value]
fontFamily = &f fontFamily = &f
} }
var dims *d2target.TextDimensions var dims *d2target.TextDimensions
switch shapeType { switch shapeType {
case d2target.ShapeText: case d2target.ShapeText:
if obj.Attributes.Language == "latex" { if obj.Language == "latex" {
width, height, err := d2latex.Measure(obj.Text().Text) width, height, err := d2latex.Measure(obj.Text().Text)
if err != nil { if err != nil {
return nil, err return nil, err
} }
dims = d2target.NewTextDimensions(width, height) dims = d2target.NewTextDimensions(width, height)
} else if obj.Attributes.Language != "" { } else if obj.Language != "" {
var err error var err error
dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text(), fontFamily) dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text(), fontFamily)
if err != nil { if err != nil {
@ -898,7 +898,7 @@ func (obj *Object) GetLabelSize(mtexts []*d2target.MText, ruler *textmeasure.Rul
dims = GetTextDimensions(mtexts, ruler, obj.Text(), fontFamily) dims = GetTextDimensions(mtexts, ruler, obj.Text(), fontFamily)
} }
if shapeType == d2target.ShapeSQLTable && obj.Attributes.Label.Value == "" { if shapeType == d2target.ShapeSQLTable && obj.Label.Value == "" {
// measure with placeholder text to determine height // measure with placeholder text to determine height
placeholder := *obj.Text() placeholder := *obj.Text()
placeholder.Text = "Table" placeholder.Text = "Table"
@ -927,7 +927,7 @@ func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.R
labelDims.Height += INNER_LABEL_PADDING labelDims.Height += INNER_LABEL_PADDING
} }
switch strings.ToLower(obj.Attributes.Shape.Value) { switch strings.ToLower(obj.Shape.Value) {
default: default:
return d2target.NewTextDimensions(labelDims.Width, labelDims.Height), nil return d2target.NewTextDimensions(labelDims.Width, labelDims.Height), nil
@ -938,8 +938,8 @@ func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.R
maxWidth := go2.Max(12, labelDims.Width) maxWidth := go2.Max(12, labelDims.Width)
fontSize := d2fonts.FONT_SIZE_L fontSize := d2fonts.FONT_SIZE_L
if obj.Attributes.Style.FontSize != nil { if obj.Style.FontSize != nil {
fontSize, _ = strconv.Atoi(obj.Attributes.Style.FontSize.Value) fontSize, _ = strconv.Atoi(obj.Style.FontSize.Value)
} }
for _, f := range obj.Class.Fields { for _, f := range obj.Class.Fields {
@ -984,8 +984,8 @@ func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.R
constraintWidth := 0 constraintWidth := 0
colFontSize := d2fonts.FONT_SIZE_L colFontSize := d2fonts.FONT_SIZE_L
if obj.Attributes.Style.FontSize != nil { if obj.Style.FontSize != nil {
colFontSize, _ = strconv.Atoi(obj.Attributes.Style.FontSize.Value) colFontSize, _ = strconv.Atoi(obj.Style.FontSize.Value)
} }
for i := range obj.SQLTable.Columns { for i := range obj.SQLTable.Columns {
@ -1031,7 +1031,7 @@ func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.R
func (obj *Object) OuterNearContainer() *Object { func (obj *Object) OuterNearContainer() *Object {
for obj != nil { for obj != nil {
if obj.Attributes.NearKey != nil { if obj.NearKey != nil {
return obj return obj
} }
obj = obj.Parent obj = obj.Parent
@ -1048,9 +1048,8 @@ type Edge struct {
SrcTableColumnIndex *int `json:"srcTableColumnIndex,omitempty"` SrcTableColumnIndex *int `json:"srcTableColumnIndex,omitempty"`
DstTableColumnIndex *int `json:"dstTableColumnIndex,omitempty"` DstTableColumnIndex *int `json:"dstTableColumnIndex,omitempty"`
LabelDimensions d2target.TextDimensions `json:"label_dimensions"` LabelPosition *string `json:"labelPosition,omitempty"`
LabelPosition *string `json:"labelPosition,omitempty"` LabelPercentage *float64 `json:"labelPercentage,omitempty"`
LabelPercentage *float64 `json:"labelPercentage,omitempty"`
IsCurve bool `json:"isCurve"` IsCurve bool `json:"isCurve"`
Route []*geo.Point `json:"route,omitempty"` Route []*geo.Point `json:"route,omitempty"`
@ -1064,7 +1063,7 @@ type Edge struct {
DstArrowhead *Attributes `json:"dstArrowhead,omitempty"` DstArrowhead *Attributes `json:"dstArrowhead,omitempty"`
References []EdgeReference `json:"references,omitempty"` References []EdgeReference `json:"references,omitempty"`
Attributes *Attributes `json:"attributes,omitempty"` Attributes `json:"attributes,omitempty"`
ZIndex int `json:"zIndex"` ZIndex int `json:"zIndex"`
} }
@ -1104,15 +1103,15 @@ func (e *Edge) ArrowString() string {
func (e *Edge) Text() *d2target.MText { func (e *Edge) Text() *d2target.MText {
fontSize := d2fonts.FONT_SIZE_M fontSize := d2fonts.FONT_SIZE_M
if e.Attributes.Style.FontSize != nil { if e.Style.FontSize != nil {
fontSize, _ = strconv.Atoi(e.Attributes.Style.FontSize.Value) fontSize, _ = strconv.Atoi(e.Style.FontSize.Value)
} }
isBold := false isBold := false
if e.Attributes.Style.Bold != nil { if e.Style.Bold != nil {
isBold, _ = strconv.ParseBool(e.Attributes.Style.Bold.Value) isBold, _ = strconv.ParseBool(e.Style.Bold.Value)
} }
return &d2target.MText{ return &d2target.MText{
Text: e.Attributes.Label.Value, Text: e.Label.Value,
FontSize: fontSize, FontSize: fontSize,
IsBold: isBold, IsBold: isBold,
IsItalic: true, IsItalic: true,
@ -1160,7 +1159,7 @@ func (obj *Object) Connect(srcID, dstID []string, srcArrow, dstArrow bool, label
} }
e := &Edge{ e := &Edge{
Attributes: &Attributes{ Attributes: Attributes{
Label: Scalar{ Label: Scalar{
Value: label, Value: label,
}, },
@ -1179,7 +1178,7 @@ func (obj *Object) Connect(srcID, dstID []string, srcArrow, dstArrow bool, label
} }
func addSQLTableColumnIndices(e *Edge, srcID, dstID []string, obj, src, dst *Object) { func addSQLTableColumnIndices(e *Edge, srcID, dstID []string, obj, src, dst *Object) {
if src.Attributes.Shape.Value == d2target.ShapeSQLTable { if src.Shape.Value == d2target.ShapeSQLTable {
if src == dst { if src == dst {
// Ignore edge to column inside table. // Ignore edge to column inside table.
return return
@ -1197,7 +1196,7 @@ func addSQLTableColumnIndices(e *Edge, srcID, dstID []string, obj, src, dst *Obj
} }
} }
} }
if dst.Attributes.Shape.Value == d2target.ShapeSQLTable { if dst.Shape.Value == d2target.ShapeSQLTable {
objAbsID := obj.AbsIDArray() objAbsID := obj.AbsIDArray()
dstAbsID := dst.AbsIDArray() dstAbsID := dst.AbsIDArray()
if len(objAbsID)+len(dstID) > len(dstAbsID) { if len(objAbsID)+len(dstID) > len(dstAbsID) {
@ -1344,16 +1343,16 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
var desiredWidth int var desiredWidth int
var desiredHeight int var desiredHeight int
if obj.Attributes.Width != nil { if obj.WidthAttr != nil {
desiredWidth, _ = strconv.Atoi(obj.Attributes.Width.Value) desiredWidth, _ = strconv.Atoi(obj.WidthAttr.Value)
} }
if obj.Attributes.Height != nil { if obj.HeightAttr != nil {
desiredHeight, _ = strconv.Atoi(obj.Attributes.Height.Value) desiredHeight, _ = strconv.Atoi(obj.HeightAttr.Value)
} }
dslShape := strings.ToLower(obj.Attributes.Shape.Value) dslShape := strings.ToLower(obj.Shape.Value)
if obj.Attributes.Label.Value == "" && if obj.Label.Value == "" &&
dslShape != d2target.ShapeImage && dslShape != d2target.ShapeImage &&
dslShape != d2target.ShapeSQLTable && dslShape != d2target.ShapeSQLTable &&
dslShape != d2target.ShapeClass { dslShape != d2target.ShapeClass {
@ -1379,12 +1378,12 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
continue continue
} }
if g.Theme != nil && g.Theme.SpecialRules.CapsLock && !strings.EqualFold(obj.Attributes.Shape.Value, d2target.ShapeCode) { if g.Theme != nil && g.Theme.SpecialRules.CapsLock && !strings.EqualFold(obj.Shape.Value, d2target.ShapeCode) {
if obj.Attributes.Language != "latex" && !obj.Attributes.Style.NoneTextTransform() { if obj.Language != "latex" && !obj.Style.NoneTextTransform() {
obj.Attributes.Label.Value = strings.ToUpper(obj.Attributes.Label.Value) obj.Label.Value = strings.ToUpper(obj.Label.Value)
} }
} }
obj.Attributes.ApplyTextTransform() obj.ApplyTextTransform()
labelDims, err := obj.GetLabelSize(mtexts, ruler, fontFamily) labelDims, err := obj.GetLabelSize(mtexts, ruler, fontFamily)
if err != nil { if err != nil {
@ -1394,7 +1393,7 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
// if there is a desired width or height, fit to content box without inner label padding for smallest minimum size // if there is a desired width or height, fit to content box without inner label padding for smallest minimum size
withInnerLabelPadding := desiredWidth == 0 && desiredHeight == 0 && withInnerLabelPadding := desiredWidth == 0 && desiredHeight == 0 &&
dslShape != d2target.ShapeText && obj.Attributes.Label.Value != "" dslShape != d2target.ShapeText && obj.Label.Value != ""
defaultDims, err := obj.GetDefaultSize(mtexts, ruler, fontFamily, *labelDims, withInnerLabelPadding) defaultDims, err := obj.GetDefaultSize(mtexts, ruler, fontFamily, *labelDims, withInnerLabelPadding)
if err != nil { if err != nil {
return err return err
@ -1426,7 +1425,7 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
} }
// give shapes with icons extra padding to fit their label // give shapes with icons extra padding to fit their label
if obj.Attributes.Icon != nil { if obj.Icon != nil {
labelHeight := float64(labelDims.Height + INNER_LABEL_PADDING) labelHeight := float64(labelDims.Height + INNER_LABEL_PADDING)
// Evenly pad enough to fit label above icon // Evenly pad enough to fit label above icon
if desiredWidth == 0 { if desiredWidth == 0 {
@ -1440,10 +1439,10 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
switch shapeType { switch shapeType {
case shape.TABLE_TYPE, shape.CLASS_TYPE, shape.CODE_TYPE, shape.IMAGE_TYPE: case shape.TABLE_TYPE, shape.CLASS_TYPE, shape.CODE_TYPE, shape.IMAGE_TYPE:
default: default:
if obj.Attributes.Link != nil { if obj.Link != nil {
paddingX += 32 paddingX += 32
} }
if obj.Attributes.Tooltip != nil { if obj.Tooltip != nil {
paddingX += 32 paddingX += 32
} }
} }
@ -1489,18 +1488,18 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
edge.MinHeight += dims.Height + 5 edge.MinHeight += dims.Height + 5
} }
if edge.Attributes.Label.Value == "" { if edge.Label.Value == "" {
continue continue
} }
if g.Theme != nil && g.Theme.SpecialRules.CapsLock && !edge.Attributes.Style.NoneTextTransform() { if g.Theme != nil && g.Theme.SpecialRules.CapsLock && !edge.Style.NoneTextTransform() {
edge.Attributes.Label.Value = strings.ToUpper(edge.Attributes.Label.Value) edge.Label.Value = strings.ToUpper(edge.Label.Value)
} }
edge.Attributes.ApplyTextTransform() edge.ApplyTextTransform()
usedFont := fontFamily usedFont := fontFamily
if edge.Attributes.Style.Font != nil { if edge.Style.Font != nil {
f := d2fonts.D2_FONT_TO_FAMILY[edge.Attributes.Style.Font.Value] f := d2fonts.D2_FONT_TO_FAMILY[edge.Style.Font.Value]
usedFont = &f usedFont = &f
} }
@ -1522,11 +1521,11 @@ func (g *Graph) Texts() []*d2target.MText {
capsLock := g.Theme != nil && g.Theme.SpecialRules.CapsLock capsLock := g.Theme != nil && g.Theme.SpecialRules.CapsLock
for _, obj := range g.Objects { for _, obj := range g.Objects {
if obj.Attributes.Label.Value != "" { if obj.Label.Value != "" {
obj.Attributes.ApplyTextTransform() obj.ApplyTextTransform()
text := obj.Text() text := obj.Text()
if capsLock && !strings.EqualFold(obj.Attributes.Shape.Value, d2target.ShapeCode) { if capsLock && !strings.EqualFold(obj.Shape.Value, d2target.ShapeCode) {
if obj.Attributes.Language != "latex" && !obj.Attributes.Style.NoneTextTransform() { if obj.Language != "latex" && !obj.Style.NoneTextTransform() {
text.Text = strings.ToUpper(text.Text) text.Text = strings.ToUpper(text.Text)
} }
} }
@ -1534,8 +1533,8 @@ func (g *Graph) Texts() []*d2target.MText {
} }
if obj.Class != nil { if obj.Class != nil {
fontSize := d2fonts.FONT_SIZE_L fontSize := d2fonts.FONT_SIZE_L
if obj.Attributes.Style.FontSize != nil { if obj.Style.FontSize != nil {
fontSize, _ = strconv.Atoi(obj.Attributes.Style.FontSize.Value) fontSize, _ = strconv.Atoi(obj.Style.FontSize.Value)
} }
for _, field := range obj.Class.Fields { for _, field := range obj.Class.Fields {
texts = appendTextDedup(texts, field.Text(fontSize)) texts = appendTextDedup(texts, field.Text(fontSize))
@ -1545,8 +1544,8 @@ func (g *Graph) Texts() []*d2target.MText {
} }
} else if obj.SQLTable != nil { } else if obj.SQLTable != nil {
colFontSize := d2fonts.FONT_SIZE_L colFontSize := d2fonts.FONT_SIZE_L
if obj.Attributes.Style.FontSize != nil { if obj.Style.FontSize != nil {
colFontSize, _ = strconv.Atoi(obj.Attributes.Style.FontSize.Value) colFontSize, _ = strconv.Atoi(obj.Style.FontSize.Value)
} }
for _, column := range obj.SQLTable.Columns { for _, column := range obj.SQLTable.Columns {
for _, t := range column.Texts(colFontSize) { for _, t := range column.Texts(colFontSize) {
@ -1556,10 +1555,10 @@ func (g *Graph) Texts() []*d2target.MText {
} }
} }
for _, edge := range g.Edges { for _, edge := range g.Edges {
if edge.Attributes.Label.Value != "" { if edge.Label.Value != "" {
edge.Attributes.ApplyTextTransform() edge.ApplyTextTransform()
text := edge.Text() text := edge.Text()
if capsLock && !edge.Attributes.Style.NoneTextTransform() { if capsLock && !edge.Style.NoneTextTransform() {
text.Text = strings.ToUpper(text.Text) text.Text = strings.ToUpper(text.Text)
} }
texts = appendTextDedup(texts, text) texts = appendTextDedup(texts, text)

View file

@ -1,8 +1,8 @@
package d2graph package d2graph
func (obj *Object) IsGridDiagram() bool { func (obj *Object) IsGridDiagram() bool {
return obj != nil && obj.Attributes != nil && return obj != nil &&
(obj.Attributes.GridRows != nil || obj.Attributes.GridColumns != nil) (obj.GridRows != nil || obj.GridColumns != nil)
} }
func (obj *Object) ClosestGridDiagram() *Object { func (obj *Object) ClosestGridDiagram() *Object {

View file

@ -3,7 +3,7 @@ package d2graph
import "oss.terrastruct.com/d2/d2target" import "oss.terrastruct.com/d2/d2target"
func (obj *Object) IsSequenceDiagram() bool { func (obj *Object) IsSequenceDiagram() bool {
return obj != nil && obj.Attributes != nil && obj.Attributes.Shape.Value == d2target.ShapeSequenceDiagram return obj != nil && obj.Shape.Value == d2target.ShapeSequenceDiagram
} }
func (obj *Object) OuterSequenceDiagram() *Object { func (obj *Object) OuterSequenceDiagram() *Object {

View file

@ -265,73 +265,51 @@ func CompareSerializedObject(obj, other *Object) error {
} }
} }
if obj.Attributes != nil && other.Attributes == nil { if d2target.IsShape(obj.Shape.Value) != d2target.IsShape(other.Shape.Value) {
return fmt.Errorf("other should have attributes") return fmt.Errorf(
} else if obj.Attributes == nil && other.Attributes != nil { "shapes differ: obj=%s, other=%s",
return fmt.Errorf("other should not have attributes") obj.Shape.Value,
} else if obj.Attributes != nil { other.Shape.Value,
if d2target.IsShape(obj.Attributes.Shape.Value) != d2target.IsShape(other.Attributes.Shape.Value) { )
return fmt.Errorf(
"shapes differ: obj=%s, other=%s",
obj.Attributes.Shape.Value,
other.Attributes.Shape.Value,
)
}
if obj.Attributes.Icon == nil && other.Attributes.Icon != nil {
return fmt.Errorf("other does not have an icon")
} else if obj.Attributes.Icon != nil && other.Attributes.Icon == nil {
return fmt.Errorf("obj does not have an icon")
}
if obj.Attributes.Direction.Value != other.Attributes.Direction.Value {
return fmt.Errorf(
"directions differ: obj=%s, other=%s",
obj.Attributes.Direction.Value,
other.Attributes.Direction.Value,
)
}
if obj.Attributes.Label.Value != other.Attributes.Label.Value {
return fmt.Errorf(
"labels differ: obj=%s, other=%s",
obj.Attributes.Label.Value,
other.Attributes.Label.Value,
)
}
if obj.Attributes.NearKey != nil {
if other.Attributes.NearKey == nil {
return fmt.Errorf("other does not have near")
}
objKey := strings.Join(Key(obj.Attributes.NearKey), ".")
deserKey := strings.Join(Key(other.Attributes.NearKey), ".")
if objKey != deserKey {
return fmt.Errorf(
"near differs: obj=%s, other=%s",
objKey,
deserKey,
)
}
} else if other.Attributes.NearKey != nil {
return fmt.Errorf("other should not have near")
}
} }
if obj.SQLTable == nil && other.SQLTable != nil { if obj.Icon == nil && other.Icon != nil {
return fmt.Errorf("other is not a sql table") return fmt.Errorf("other does not have an icon")
} else if obj.SQLTable != nil && other.SQLTable == nil { } else if obj.Icon != nil && other.Icon == nil {
return fmt.Errorf("obj is not a sql table") return fmt.Errorf("obj does not have an icon")
} }
if obj.SQLTable != nil { if obj.Direction.Value != other.Direction.Value {
if len(obj.SQLTable.Columns) != len(other.SQLTable.Columns) { return fmt.Errorf(
"directions differ: obj=%s, other=%s",
obj.Direction.Value,
other.Direction.Value,
)
}
if obj.Label.Value != other.Label.Value {
return fmt.Errorf(
"labels differ: obj=%s, other=%s",
obj.Label.Value,
other.Label.Value,
)
}
if obj.NearKey != nil {
if other.NearKey == nil {
return fmt.Errorf("other does not have near")
}
objKey := strings.Join(Key(obj.NearKey), ".")
deserKey := strings.Join(Key(other.NearKey), ".")
if objKey != deserKey {
return fmt.Errorf( return fmt.Errorf(
"table columns count differ: obj=%d, other=%d", "near differs: obj=%s, other=%s",
len(obj.SQLTable.Columns), objKey,
len(other.SQLTable.Columns), deserKey,
) )
} }
} else if other.NearKey != nil {
return fmt.Errorf("other should not have near")
} }
if obj.LabelDimensions.Width != other.LabelDimensions.Width { if obj.LabelDimensions.Width != other.LabelDimensions.Width {
@ -350,6 +328,22 @@ func CompareSerializedObject(obj, other *Object) error {
) )
} }
if obj.SQLTable == nil && other.SQLTable != nil {
return fmt.Errorf("other is not a sql table")
} else if obj.SQLTable != nil && other.SQLTable == nil {
return fmt.Errorf("obj is not a sql table")
}
if obj.SQLTable != nil {
if len(obj.SQLTable.Columns) != len(other.SQLTable.Columns) {
return fmt.Errorf(
"table columns count differ: obj=%d, other=%d",
len(obj.SQLTable.Columns),
len(other.SQLTable.Columns),
)
}
}
return nil return nil
} }
@ -410,11 +404,11 @@ func CompareSerializedEdge(edge, other *Edge) error {
) )
} }
if edge.Attributes.Label.Value != other.Attributes.Label.Value { if edge.Label.Value != other.Label.Value {
return fmt.Errorf( return fmt.Errorf(
"labels differ: edge=%s, other=%s", "labels differ: edge=%s, other=%s",
edge.Attributes.Label.Value, edge.Label.Value,
other.Attributes.Label.Value, other.Label.Value,
) )
} }

View file

@ -93,7 +93,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
}, },
} }
isHorizontal := false isHorizontal := false
switch g.Root.Attributes.Direction.Value { switch g.Root.Direction.Value {
case "down": case "down":
rootAttrs.rankdir = "TB" rootAttrs.rankdir = "TB"
case "right": case "right":
@ -118,9 +118,9 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
maxContainerLabelHeight = go2.Max(maxContainerLabelHeight, obj.LabelDimensions.Height+label.PADDING) maxContainerLabelHeight = go2.Max(maxContainerLabelHeight, obj.LabelDimensions.Height+label.PADDING)
} }
if obj.Attributes.Icon != nil && obj.Attributes.Shape.Value != d2target.ShapeImage { if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(obj.Width), float64(obj.Height)) contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(obj.Width), float64(obj.Height))
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Attributes.Shape.Value] shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Shape.Value]
s := shape.NewShape(shapeType, contentBox) s := shape.NewShape(shapeType, contentBox)
iconSize := d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft)) iconSize := d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft))
// Since dagre container labels are pushed up, we don't want a child container to collide // Since dagre container labels are pushed up, we don't want a child container to collide
@ -161,7 +161,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
height := obj.Height height := obj.Height
if obj.HasLabel() { if obj.HasLabel() {
if obj.HasOutsideBottomLabel() || obj.Attributes.Icon != nil { if obj.HasOutsideBottomLabel() || obj.Icon != nil {
height += float64(obj.LabelDimensions.Height) + label.PADDING height += float64(obj.LabelDimensions.Height) + label.PADDING
} }
if len(obj.ChildrenArray) > 0 { if len(obj.ChildrenArray) > 0 {
@ -189,7 +189,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
// We want to leave some gap between multiple edges // We want to leave some gap between multiple edges
if numEdges > 1 { if numEdges > 1 {
switch g.Root.Attributes.Direction.Value { switch g.Root.Direction.Value {
case "down", "up", "": case "down", "up", "":
width += EDGE_LABEL_GAP width += EDGE_LABEL_GAP
case "left", "right": case "left", "right":
@ -242,13 +242,13 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter)) obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
// remove the extra height we added to the node when passing to dagre // remove the extra height we added to the node when passing to dagre
obj.Height -= float64(obj.LabelDimensions.Height) + label.PADDING obj.Height -= float64(obj.LabelDimensions.Height) + label.PADDING
} else if obj.Attributes.Icon != nil { } else if obj.Icon != nil {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
} else { } else {
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
} }
} }
if obj.Attributes.Icon != nil { if obj.Icon != nil {
if len(obj.ChildrenArray) > 0 { if len(obj.ChildrenArray) > 0 {
obj.IconPosition = go2.Pointer(string(label.OutsideTopLeft)) obj.IconPosition = go2.Pointer(string(label.OutsideTopLeft))
obj.LabelPosition = go2.Pointer(string(label.OutsideTopRight)) obj.LabelPosition = go2.Pointer(string(label.OutsideTopRight))
@ -453,15 +453,15 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
} }
} }
srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Attributes.Shape.Value)], edge.Src.Box) srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box)
dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Attributes.Shape.Value)], edge.Dst.Box) dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box)
// trace the edge to the specific shape's border // trace the edge to the specific shape's border
points[startIndex] = shape.TraceToShapeBorder(srcShape, start, points[startIndex+1]) points[startIndex] = shape.TraceToShapeBorder(srcShape, start, points[startIndex+1])
// if an edge to a container runs into its label, stop the edge at the label instead // if an edge to a container runs into its label, stop the edge at the label instead
overlapsContainerLabel := false overlapsContainerLabel := false
if edge.Dst.IsContainer() && edge.Dst.Attributes.Label.Value != "" && !dstShape.Is(shape.TEXT_TYPE) { if edge.Dst.IsContainer() && edge.Dst.Label.Value != "" && !dstShape.Is(shape.TEXT_TYPE) {
// assumes LabelPosition, LabelWidth, LabelHeight are all set if there is a label // assumes LabelPosition, LabelWidth, LabelHeight are all set if there is a label
labelWidth := float64(edge.Dst.LabelDimensions.Width) labelWidth := float64(edge.Dst.LabelDimensions.Width)
labelHeight := float64(edge.Dst.LabelDimensions.Height) labelHeight := float64(edge.Dst.LabelDimensions.Height)
@ -514,7 +514,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
edge.Route = path edge.Route = path
// compile needs to assign edge label positions // compile needs to assign edge label positions
if edge.Attributes.Label.Value != "" { if edge.Label.Value != "" {
edge.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) edge.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
} }
} }

View file

@ -159,7 +159,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
}, },
}, },
} }
switch g.Root.Attributes.Direction.Value { switch g.Root.Direction.Value {
case "down": case "down":
elkGraph.LayoutOptions.Direction = "DOWN" elkGraph.LayoutOptions.Direction = "DOWN"
case "up": case "up":
@ -198,7 +198,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
} }
} }
if incoming >= 2 || outgoing >= 2 { if incoming >= 2 || outgoing >= 2 {
switch g.Root.Attributes.Direction.Value { switch g.Root.Direction.Value {
case "right", "left": case "right", "left":
obj.Height = math.Max(obj.Height, math.Max(incoming, outgoing)*port_spacing) obj.Height = math.Max(obj.Height, math.Max(incoming, outgoing)*port_spacing)
default: default:
@ -209,7 +209,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
height := obj.Height height := obj.Height
width := obj.Width width := obj.Width
if obj.HasLabel() { if obj.HasLabel() {
if obj.HasOutsideBottomLabel() || obj.Attributes.Icon != nil { if obj.HasOutsideBottomLabel() || obj.Icon != nil {
height += float64(obj.LabelDimensions.Height) + label.PADDING height += float64(obj.LabelDimensions.Height) + label.PADDING
} }
width = go2.Max(width, float64(obj.LabelDimensions.Width)) width = go2.Max(width, float64(obj.LabelDimensions.Width))
@ -256,7 +256,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
n.Height += 100 + float64(labelHeight) n.Height += 100 + float64(labelHeight)
n.Width += 100 n.Width += 100
contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(n.Width), float64(n.Height)) contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(n.Width), float64(n.Height))
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Attributes.Shape.Value] shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Shape.Value]
s := shape.NewShape(shapeType, contentBox) s := shape.NewShape(shapeType, contentBox)
paddingTop := n.Height - s.GetInnerBox().Height paddingTop := n.Height - s.GetInnerBox().Height
@ -264,7 +264,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
n.Width -= 100 n.Width -= 100
iconHeight := 0 iconHeight := 0
if obj.Attributes.Icon != nil && obj.Attributes.Shape.Value != d2target.ShapeImage { if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
iconHeight = d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft)) + label.PADDING*2 iconHeight = d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft)) + label.PADDING*2
} }
@ -283,7 +283,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
if obj.HasLabel() { if obj.HasLabel() {
n.Labels = append(n.Labels, &ELKLabel{ n.Labels = append(n.Labels, &ELKLabel{
Text: obj.Attributes.Label.Value, Text: obj.Label.Value,
Width: float64(obj.LabelDimensions.Width), Width: float64(obj.LabelDimensions.Width),
Height: float64(obj.LabelDimensions.Height), Height: float64(obj.LabelDimensions.Height),
}) })
@ -303,9 +303,9 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
Sources: []string{edge.Src.AbsID()}, Sources: []string{edge.Src.AbsID()},
Targets: []string{edge.Dst.AbsID()}, Targets: []string{edge.Dst.AbsID()},
} }
if edge.Attributes.Label.Value != "" { if edge.Label.Value != "" {
e.Labels = append(e.Labels, &ELKLabel{ e.Labels = append(e.Labels, &ELKLabel{
Text: edge.Attributes.Label.Value, Text: edge.Label.Value,
Width: float64(edge.LabelDimensions.Width), Width: float64(edge.LabelDimensions.Width),
Height: float64(edge.LabelDimensions.Height), Height: float64(edge.LabelDimensions.Height),
LayoutOptions: &elkOpts{ LayoutOptions: &elkOpts{
@ -397,13 +397,13 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
} else if obj.HasOutsideBottomLabel() { } else if obj.HasOutsideBottomLabel() {
obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter)) obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
obj.Height -= float64(obj.LabelDimensions.Height) + label.PADDING obj.Height -= float64(obj.LabelDimensions.Height) + label.PADDING
} else if obj.Attributes.Icon != nil { } else if obj.Icon != nil {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
} else { } else {
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
} }
} }
if obj.Attributes.Icon != nil { if obj.Icon != nil {
if len(obj.ChildrenArray) > 0 { if len(obj.ChildrenArray) > 0 {
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft)) obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
obj.LabelPosition = go2.Pointer(string(label.InsideTopRight)) obj.LabelPosition = go2.Pointer(string(label.InsideTopRight))
@ -444,14 +444,14 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
} }
startIndex, endIndex := 0, len(points)-1 startIndex, endIndex := 0, len(points)-1
srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Attributes.Shape.Value)], edge.Src.Box) srcShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Src.Shape.Value)], edge.Src.Box)
dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Attributes.Shape.Value)], edge.Dst.Box) dstShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(edge.Dst.Shape.Value)], edge.Dst.Box)
// trace the edge to the specific shape's border // trace the edge to the specific shape's border
points[startIndex] = shape.TraceToShapeBorder(srcShape, points[startIndex], points[startIndex+1]) points[startIndex] = shape.TraceToShapeBorder(srcShape, points[startIndex], points[startIndex+1])
points[endIndex] = shape.TraceToShapeBorder(dstShape, points[endIndex], points[endIndex-1]) points[endIndex] = shape.TraceToShapeBorder(dstShape, points[endIndex], points[endIndex-1])
if edge.Attributes.Label.Value != "" { if edge.Label.Value != "" {
edge.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) edge.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
} }
@ -519,7 +519,7 @@ func deleteBends(g *d2graph.Graph) {
newStart = geo.NewPoint(end.X, start.Y) newStart = geo.NewPoint(end.X, start.Y)
} }
endpointShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(endpoint.Attributes.Shape.Value)], endpoint.Box) endpointShape := shape.NewShape(d2target.DSL_SHAPE_TO_SHAPE_TYPE[strings.ToLower(endpoint.Shape.Value)], endpoint.Box)
newStart = shape.TraceToShapeBorder(endpointShape, newStart, end) newStart = shape.TraceToShapeBorder(endpointShape, newStart, end)
// Check that the new segment doesn't collide with anything new // Check that the new segment doesn't collide with anything new

View file

@ -32,11 +32,11 @@ func newGridDiagram(root *d2graph.Object) *gridDiagram {
horizontalGap: DEFAULT_GAP, horizontalGap: DEFAULT_GAP,
} }
if root.Attributes.GridRows != nil { if root.GridRows != nil {
gd.rows, _ = strconv.Atoi(root.Attributes.GridRows.Value) gd.rows, _ = strconv.Atoi(root.GridRows.Value)
} }
if root.Attributes.GridColumns != nil { if root.GridColumns != nil {
gd.columns, _ = strconv.Atoi(root.Attributes.GridColumns.Value) gd.columns, _ = strconv.Atoi(root.GridColumns.Value)
} }
if gd.rows != 0 && gd.columns != 0 { if gd.rows != 0 && gd.columns != 0 {
@ -47,7 +47,7 @@ func newGridDiagram(root *d2graph.Object) *gridDiagram {
// . │ g h i │ │ c f i │ // . │ g h i │ │ c f i │
// . └───────┘ └───────┘ // . └───────┘ └───────┘
// if keyword rows is first, make it row-directed, if columns is first it is column-directed // if keyword rows is first, make it row-directed, if columns is first it is column-directed
if root.Attributes.GridRows.MapKey.Range.Before(root.Attributes.GridColumns.MapKey.Range) { if root.GridRows.MapKey.Range.Before(root.GridColumns.MapKey.Range) {
gd.rowDirected = true gd.rowDirected = true
} }
@ -84,15 +84,15 @@ func newGridDiagram(root *d2graph.Object) *gridDiagram {
} }
// grid gap sets both, but can be overridden // grid gap sets both, but can be overridden
if root.Attributes.GridGap != nil { if root.GridGap != nil {
gd.verticalGap, _ = strconv.Atoi(root.Attributes.GridGap.Value) gd.verticalGap, _ = strconv.Atoi(root.GridGap.Value)
gd.horizontalGap = gd.verticalGap gd.horizontalGap = gd.verticalGap
} }
if root.Attributes.VerticalGap != nil { if root.VerticalGap != nil {
gd.verticalGap, _ = strconv.Atoi(root.Attributes.VerticalGap.Value) gd.verticalGap, _ = strconv.Atoi(root.VerticalGap.Value)
} }
if root.Attributes.HorizontalGap != nil { if root.HorizontalGap != nil {
gd.horizontalGap, _ = strconv.Atoi(root.Attributes.HorizontalGap.Value) gd.horizontalGap, _ = strconv.Atoi(root.HorizontalGap.Value)
} }
return &gd return &gd

View file

@ -123,7 +123,7 @@ func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*gridDiagram, error) {
// position labels and icons // position labels and icons
for _, o := range gd.objects { for _, o := range gd.objects {
if o.Attributes.Icon != nil { if o.Icon != nil {
o.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) o.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter)) o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
} else { } else {

View file

@ -33,7 +33,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, constantNearGraphs []*d2graph
for _, processCenters := range []bool{true, false} { for _, processCenters := range []bool{true, false} {
for _, tempGraph := range constantNearGraphs { for _, tempGraph := range constantNearGraphs {
obj := tempGraph.Root.ChildrenArray[0] obj := tempGraph.Root.ChildrenArray[0]
if processCenters == strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "-center") { if processCenters == strings.Contains(d2graph.Key(obj.NearKey)[0], "-center") {
prevX, prevY := obj.TopLeft.X, obj.TopLeft.Y prevX, prevY := obj.TopLeft.X, obj.TopLeft.Y
obj.TopLeft = geo.NewPoint(place(obj)) obj.TopLeft = geo.NewPoint(place(obj))
dx, dy := obj.TopLeft.X-prevX, obj.TopLeft.Y-prevY dx, dy := obj.TopLeft.X-prevX, obj.TopLeft.Y-prevY
@ -56,7 +56,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, constantNearGraphs []*d2graph
} }
for _, tempGraph := range constantNearGraphs { for _, tempGraph := range constantNearGraphs {
obj := tempGraph.Root.ChildrenArray[0] obj := tempGraph.Root.ChildrenArray[0]
if processCenters == strings.Contains(d2graph.Key(obj.Attributes.NearKey)[0], "-center") { if processCenters == strings.Contains(d2graph.Key(obj.NearKey)[0], "-center") {
// The z-index for constant nears does not matter, as it will not collide // The z-index for constant nears does not matter, as it will not collide
g.Objects = append(g.Objects, tempGraph.Objects...) g.Objects = append(g.Objects, tempGraph.Objects...)
if obj.Parent.Children == nil { if obj.Parent.Children == nil {
@ -78,7 +78,7 @@ func place(obj *d2graph.Object) (float64, float64) {
w := br.X - tl.X w := br.X - tl.X
h := br.Y - tl.Y h := br.Y - tl.Y
nearKeyStr := d2graph.Key(obj.Attributes.NearKey)[0] nearKeyStr := d2graph.Key(obj.NearKey)[0]
var x, y float64 var x, y float64
switch nearKeyStr { switch nearKeyStr {
case "top-left": case "top-left":
@ -139,14 +139,14 @@ func place(obj *d2graph.Object) (float64, float64) {
func WithoutConstantNears(ctx context.Context, g *d2graph.Graph) (constantNearGraphs []*d2graph.Graph) { func WithoutConstantNears(ctx context.Context, g *d2graph.Graph) (constantNearGraphs []*d2graph.Graph) {
for i := 0; i < len(g.Objects); i++ { for i := 0; i < len(g.Objects); i++ {
obj := g.Objects[i] obj := g.Objects[i]
if obj.Attributes.NearKey == nil { if obj.NearKey == nil {
continue continue
} }
_, isKey := g.Root.HasChild(d2graph.Key(obj.Attributes.NearKey)) _, isKey := g.Root.HasChild(d2graph.Key(obj.NearKey))
if isKey { if isKey {
continue continue
} }
_, isConst := d2graph.NearConstants[d2graph.Key(obj.Attributes.NearKey)[0]] _, isConst := d2graph.NearConstants[d2graph.Key(obj.NearKey)[0]]
if isConst { if isConst {
descendantObjects, edges := pluckObjAndEdges(g, obj) descendantObjects, edges := pluckObjAndEdges(g, obj)
@ -217,10 +217,10 @@ func boundingBox(g *d2graph.Graph) (tl, br *geo.Point) {
y2 := math.Inf(-1) y2 := math.Inf(-1)
for _, obj := range g.Objects { for _, obj := range g.Objects {
if obj.Attributes.NearKey != nil { if obj.NearKey != nil {
// Top left should not be MORE top than top-center // Top left should not be MORE top than top-center
// But it should go more left if top-center label extends beyond bounds of diagram // But it should go more left if top-center label extends beyond bounds of diagram
switch d2graph.Key(obj.Attributes.NearKey)[0] { switch d2graph.Key(obj.NearKey)[0] {
case "top-center", "bottom-center": case "top-center", "bottom-center":
x1 = math.Min(x1, obj.TopLeft.X) x1 = math.Min(x1, obj.TopLeft.X)
x2 = math.Max(x2, obj.TopLeft.X+obj.Width) x2 = math.Max(x2, obj.TopLeft.X+obj.Width)
@ -236,7 +236,7 @@ func boundingBox(g *d2graph.Graph) (tl, br *geo.Point) {
y1 = math.Min(y1, obj.TopLeft.Y) y1 = math.Min(y1, obj.TopLeft.Y)
x2 = math.Max(x2, obj.TopLeft.X+obj.Width) x2 = math.Max(x2, obj.TopLeft.X+obj.Width)
y2 = math.Max(y2, obj.TopLeft.Y+obj.Height) y2 = math.Max(y2, obj.TopLeft.Y+obj.Height)
if obj.Attributes.Label.Value != "" && obj.LabelPosition != nil { if obj.Label.Value != "" && obj.LabelPosition != nil {
labelPosition := label.Position(*obj.LabelPosition) labelPosition := label.Position(*obj.LabelPosition)
if labelPosition.IsOutside() { if labelPosition.IsOutside() {
labelTL := labelPosition.GetPointOnBox(obj.Box, label.PADDING, float64(obj.LabelDimensions.Width), float64(obj.LabelDimensions.Height)) labelTL := labelPosition.GetPointOnBox(obj.Box, label.PADDING, float64(obj.LabelDimensions.Width), float64(obj.LabelDimensions.Height))

View file

@ -54,7 +54,7 @@ func WithoutSequenceDiagrams(ctx context.Context, g *d2graph.Graph) (map[string]
if len(obj.ChildrenArray) == 0 { if len(obj.ChildrenArray) == 0 {
continue continue
} }
if obj.Attributes.Shape.Value != d2target.ShapeSequenceDiagram { if obj.Shape.Value != d2target.ShapeSequenceDiagram {
queue = append(queue, obj.ChildrenArray...) queue = append(queue, obj.ChildrenArray...)
continue continue
} }

View file

@ -180,7 +180,7 @@ b -> a.t2`
g, err := d2compiler.Compile("", strings.NewReader(input), nil) g, err := d2compiler.Compile("", strings.NewReader(input), nil)
assert.Nil(t, err) assert.Nil(t, err)
g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram} g.Root.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram}
a, has := g.Root.HasChild([]string{"a"}) a, has := g.Root.HasChild([]string{"a"})
assert.True(t, has) assert.True(t, has)
@ -217,14 +217,14 @@ b -> a.t2`
}) })
// check properties // check properties
assert.Equal(t, strings.ToLower(shape.PERSON_TYPE), strings.ToLower(a.Attributes.Shape.Value)) assert.Equal(t, strings.ToLower(shape.PERSON_TYPE), strings.ToLower(a.Shape.Value))
if a_t1.Attributes.Label.Value != "" { if a_t1.Label.Value != "" {
t.Fatalf("expected no label for span, got %s", a_t1.Attributes.Label.Value) t.Fatalf("expected no label for span, got %s", a_t1.Label.Value)
} }
if a_t1.Attributes.Shape.Value != shape.SQUARE_TYPE { if a_t1.Shape.Value != shape.SQUARE_TYPE {
t.Fatalf("expected square shape for span, got %s", a_t1.Attributes.Shape.Value) t.Fatalf("expected square shape for span, got %s", a_t1.Shape.Value)
} }
if a_t1.Height != b_t1.Height { if a_t1.Height != b_t1.Height {
@ -323,7 +323,7 @@ container -> c: edge 1
c := g.Root.EnsureChild([]string{"c"}) c := g.Root.EnsureChild([]string{"c"})
c.Box = geo.NewBox(nil, 100, 100) c.Box = geo.NewBox(nil, 100, 100)
c.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSquare} c.Shape = d2graph.Scalar{Value: d2target.ShapeSquare}
layoutFn := func(ctx context.Context, g *d2graph.Graph) error { layoutFn := func(ctx context.Context, g *d2graph.Graph) error {
if len(g.Objects) != 2 { if len(g.Objects) != 2 {
@ -378,7 +378,7 @@ container -> c: edge 1
func TestSelfEdges(t *testing.T) { func TestSelfEdges(t *testing.T) {
g := d2graph.NewGraph() g := d2graph.NewGraph()
g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram} g.Root.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram}
n1 := g.Root.EnsureChild([]string{"n1"}) n1 := g.Root.EnsureChild([]string{"n1"})
n1.Box = geo.NewBox(nil, 100, 100) n1.Box = geo.NewBox(nil, 100, 100)
@ -387,7 +387,7 @@ func TestSelfEdges(t *testing.T) {
Src: n1, Src: n1,
Dst: n1, Dst: n1,
Index: 0, Index: 0,
Attributes: &d2graph.Attributes{ Attributes: d2graph.Attributes{
Label: d2graph.Scalar{Value: "left to right"}, Label: d2graph.Scalar{Value: "left to right"},
}, },
}, },
@ -414,10 +414,10 @@ func TestSelfEdges(t *testing.T) {
func TestSequenceToDescendant(t *testing.T) { func TestSequenceToDescendant(t *testing.T) {
g := d2graph.NewGraph() g := d2graph.NewGraph()
g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram} g.Root.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram}
a := g.Root.EnsureChild([]string{"a"}) a := g.Root.EnsureChild([]string{"a"})
a.Box = geo.NewBox(nil, 100, 100) a.Box = geo.NewBox(nil, 100, 100)
a.Attributes = &d2graph.Attributes{ a.Attributes = d2graph.Attributes{
Shape: d2graph.Scalar{Value: shape.PERSON_TYPE}, Shape: d2graph.Scalar{Value: shape.PERSON_TYPE},
} }
a_t1 := a.EnsureChild([]string{"t1"}) a_t1 := a.EnsureChild([]string{"t1"})
@ -425,15 +425,13 @@ func TestSequenceToDescendant(t *testing.T) {
g.Edges = []*d2graph.Edge{ g.Edges = []*d2graph.Edge{
{ {
Src: a, Src: a,
Dst: a_t1, Dst: a_t1,
Index: 0, Index: 0,
Attributes: &d2graph.Attributes{},
}, { }, {
Src: a_t1, Src: a_t1,
Dst: a, Dst: a,
Index: 0, Index: 0,
Attributes: &d2graph.Attributes{},
}, },
} }

View file

@ -110,7 +110,7 @@ func newSequenceDiagram(objects []*d2graph.Object, messages []*d2graph.Edge) (*s
sd.objectRank[actor] = rank sd.objectRank[actor] = rank
if actor.Width < MIN_ACTOR_WIDTH { if actor.Width < MIN_ACTOR_WIDTH {
dslShape := strings.ToLower(actor.Attributes.Shape.Value) dslShape := strings.ToLower(actor.Shape.Value)
switch dslShape { switch dslShape {
case d2target.ShapePerson, d2target.ShapeOval, d2target.ShapeSquare, d2target.ShapeCircle: case d2target.ShapePerson, d2target.ShapeOval, d2target.ShapeSquare, d2target.ShapeCircle:
// scale shape up to min width uniformly // scale shape up to min width uniformly
@ -131,7 +131,7 @@ func newSequenceDiagram(objects []*d2graph.Object, messages []*d2graph.Edge) (*s
// edge groups are children of actors with no edges and children edges // edge groups are children of actors with no edges and children edges
if child.IsSequenceDiagramNote() { if child.IsSequenceDiagramNote() {
sd.verticalIndices[child.AbsID()] = getObjEarliestLineNum(child) sd.verticalIndices[child.AbsID()] = getObjEarliestLineNum(child)
child.Attributes.Shape = d2graph.Scalar{Value: shape.PAGE_TYPE} child.Shape = d2graph.Scalar{Value: shape.PAGE_TYPE}
sd.notes = append(sd.notes, child) sd.notes = append(sd.notes, child)
sd.objectRank[child] = rank sd.objectRank[child] = rank
child.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) child.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
@ -139,8 +139,8 @@ func newSequenceDiagram(objects []*d2graph.Object, messages []*d2graph.Edge) (*s
} else { } else {
// spans have no labels // spans have no labels
// TODO why not? Spans should be able to // TODO why not? Spans should be able to
child.Attributes.Label = d2graph.Scalar{Value: ""} child.Label = d2graph.Scalar{Value: ""}
child.Attributes.Shape = d2graph.Scalar{Value: shape.SQUARE_TYPE} child.Shape = d2graph.Scalar{Value: shape.SQUARE_TYPE}
sd.spans = append(sd.spans, child) sd.spans = append(sd.spans, child)
sd.objectRank[child] = rank sd.objectRank[child] = rank
} }
@ -390,15 +390,15 @@ func (sd *sequenceDiagram) addLifelineEdges() {
StrokeDash: &d2graph.Scalar{Value: fmt.Sprintf("%d", LIFELINE_STROKE_DASH)}, StrokeDash: &d2graph.Scalar{Value: fmt.Sprintf("%d", LIFELINE_STROKE_DASH)},
StrokeWidth: &d2graph.Scalar{Value: fmt.Sprintf("%d", LIFELINE_STROKE_WIDTH)}, StrokeWidth: &d2graph.Scalar{Value: fmt.Sprintf("%d", LIFELINE_STROKE_WIDTH)},
} }
if actor.Attributes.Style.StrokeDash != nil { if actor.Style.StrokeDash != nil {
style.StrokeDash = &d2graph.Scalar{Value: actor.Attributes.Style.StrokeDash.Value} style.StrokeDash = &d2graph.Scalar{Value: actor.Style.StrokeDash.Value}
} }
if actor.Attributes.Style.Stroke != nil { if actor.Style.Stroke != nil {
style.Stroke = &d2graph.Scalar{Value: actor.Attributes.Style.Stroke.Value} style.Stroke = &d2graph.Scalar{Value: actor.Style.Stroke.Value}
} }
sd.lifelines = append(sd.lifelines, &d2graph.Edge{ sd.lifelines = append(sd.lifelines, &d2graph.Edge{
Attributes: &d2graph.Attributes{Style: style}, Attributes: d2graph.Attributes{Style: style},
Src: actor, Src: actor,
SrcArrow: false, SrcArrow: false,
Dst: &d2graph.Object{ Dst: &d2graph.Object{
@ -581,7 +581,7 @@ func (sd *sequenceDiagram) routeMessages() error {
} }
messageOffset += sd.yStep messageOffset += sd.yStep
if message.Attributes.Label.Value != "" { if message.Label.Value != "" {
message.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter)) message.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
} }
} }

View file

@ -159,12 +159,12 @@ func _set(g *d2graph.Graph, key string, tag, value *string) error {
} }
} }
if obj.Attributes.Label.MapKey != nil && obj.Map == nil && (!found || reserved || len(mk.Edges) > 0) { if obj.Label.MapKey != nil && obj.Map == nil && (!found || reserved || len(mk.Edges) > 0) {
obj.Map = &d2ast.Map{ obj.Map = &d2ast.Map{
Range: d2ast.MakeRange(",1:0:0-1:0:0"), Range: d2ast.MakeRange(",1:0:0-1:0:0"),
} }
obj.Attributes.Label.MapKey.Primary = obj.Attributes.Label.MapKey.Value.ScalarBox() obj.Label.MapKey.Primary = obj.Label.MapKey.Value.ScalarBox()
obj.Attributes.Label.MapKey.Value = d2ast.MakeValueBox(obj.Map) obj.Label.MapKey.Value = d2ast.MakeValueBox(obj.Map)
scope = obj.Map scope = obj.Map
mk.Key.Path = mk.Key.Path[toSkip-1:] mk.Key.Path = mk.Key.Path[toSkip-1:]
@ -247,10 +247,10 @@ func _set(g *d2graph.Graph, key string, tag, value *string) error {
if n.MapKey.Key.Path[0].Unbox().ScalarString() == mk.Key.Path[toSkip-1].Unbox().ScalarString() { if n.MapKey.Key.Path[0].Unbox().ScalarString() == mk.Key.Path[toSkip-1].Unbox().ScalarString() {
scope = n.MapKey.Value.Map scope = n.MapKey.Value.Map
if mk.Key.Path[0].Unbox().ScalarString() == "source-arrowhead" && edge.SrcArrowhead != nil { if mk.Key.Path[0].Unbox().ScalarString() == "source-arrowhead" && edge.SrcArrowhead != nil {
attrs = edge.SrcArrowhead attrs = *edge.SrcArrowhead
} }
if mk.Key.Path[0].Unbox().ScalarString() == "target-arrowhead" && edge.DstArrowhead != nil { if mk.Key.Path[0].Unbox().ScalarString() == "target-arrowhead" && edge.DstArrowhead != nil {
attrs = edge.DstArrowhead attrs = *edge.DstArrowhead
} }
reservedKey = mk.Key.Path[0].Unbox().ScalarString() reservedKey = mk.Key.Path[0].Unbox().ScalarString()
mk.Key.Path = mk.Key.Path[1:] mk.Key.Path = mk.Key.Path[1:]
@ -295,13 +295,13 @@ func _set(g *d2graph.Graph, key string, tag, value *string) error {
return nil return nil
} }
case "width": case "width":
if attrs.Width != nil && attrs.Width.MapKey != nil { if attrs.WidthAttr != nil && attrs.WidthAttr.MapKey != nil {
attrs.Width.MapKey.SetScalar(mk.Value.ScalarBox()) attrs.WidthAttr.MapKey.SetScalar(mk.Value.ScalarBox())
return nil return nil
} }
case "height": case "height":
if attrs.Height != nil && attrs.Height.MapKey != nil { if attrs.HeightAttr != nil && attrs.HeightAttr.MapKey != nil {
attrs.Height.MapKey.SetScalar(mk.Value.ScalarBox()) attrs.HeightAttr.MapKey.SetScalar(mk.Value.ScalarBox())
return nil return nil
} }
case "top": case "top":
@ -340,12 +340,13 @@ func _set(g *d2graph.Graph, key string, tag, value *string) error {
return nil return nil
} }
case "source-arrowhead", "target-arrowhead": case "source-arrowhead", "target-arrowhead":
var arrowhead *d2graph.Attributes
if reservedKey == "source-arrowhead" { if reservedKey == "source-arrowhead" {
attrs = edge.SrcArrowhead arrowhead = edge.SrcArrowhead
} else { } else {
attrs = edge.DstArrowhead arrowhead = edge.DstArrowhead
} }
if attrs != nil { if arrowhead != nil {
if reservedTargetKey == "" { if reservedTargetKey == "" {
if len(mk.Key.Path[reservedIndex:]) != 2 { if len(mk.Key.Path[reservedIndex:]) != 2 {
return errors.New("malformed style setting, expected 2 part path") return errors.New("malformed style setting, expected 2 part path")
@ -354,13 +355,13 @@ func _set(g *d2graph.Graph, key string, tag, value *string) error {
} }
switch reservedTargetKey { switch reservedTargetKey {
case "shape": case "shape":
if attrs.Shape.MapKey != nil { if arrowhead.Shape.MapKey != nil {
attrs.Shape.MapKey.SetScalar(mk.Value.ScalarBox()) arrowhead.Shape.MapKey.SetScalar(mk.Value.ScalarBox())
return nil return nil
} }
case "label": case "label":
if attrs.Label.MapKey != nil { if arrowhead.Label.MapKey != nil {
attrs.Label.MapKey.SetScalar(mk.Value.ScalarBox()) arrowhead.Label.MapKey.SetScalar(mk.Value.ScalarBox())
return nil return nil
} }
} }
@ -664,7 +665,7 @@ func renameConflictsToParent(g *d2graph.Graph, key *d2ast.KeyPath) (*d2graph.Gra
if !ok { if !ok {
return g, nil return g, nil
} }
if obj.Attributes.Shape.Value == d2target.ShapeSQLTable || obj.Attributes.Shape.Value == d2target.ShapeClass { if obj.Shape.Value == d2target.ShapeSQLTable || obj.Shape.Value == d2target.ShapeClass {
return g, nil return g, nil
} }
@ -961,7 +962,7 @@ func deleteObject(g *d2graph.Graph, key *d2ast.KeyPath, obj *d2graph.Object) (*d
isSpecial := isReserved || x.Unbox().ScalarString() == "_" isSpecial := isReserved || x.Unbox().ScalarString() == "_"
return !isSpecial return !isSpecial
}) })
if obj.Attributes.Shape.Value == d2target.ShapeSQLTable || obj.Attributes.Shape.Value == d2target.ShapeClass { if obj.Shape.Value == d2target.ShapeSQLTable || obj.Shape.Value == d2target.ShapeClass {
deleteFromMap(ref.Scope, ref.MapKey) deleteFromMap(ref.Scope, ref.MapKey)
} else if len(withoutSpecial) == 0 { } else if len(withoutSpecial) == 0 {
hoistRefChildren(g, key, ref) hoistRefChildren(g, key, ref)
@ -990,7 +991,7 @@ func deleteObject(g *d2graph.Graph, key *d2ast.KeyPath, obj *d2graph.Object) (*d
} else if ref.InEdge() { } else if ref.InEdge() {
edge := ref.MapKey.Edges[ref.MapKeyEdgeIndex] edge := ref.MapKey.Edges[ref.MapKeyEdgeIndex]
if obj.Attributes.Shape.Value == d2target.ShapeSQLTable || obj.Attributes.Shape.Value == d2target.ShapeClass { if obj.Shape.Value == d2target.ShapeSQLTable || obj.Shape.Value == d2target.ShapeClass {
if ref.MapKeyEdgeDest() { if ref.MapKeyEdgeDest() {
ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, edge.Src, true) ensureNode(g, refEdges, ref.ScopeObj, ref.Scope, ref.MapKey, edge.Src, true)
} else { } else {

View file

@ -50,11 +50,11 @@ func TestCreate(t *testing.T) {
if g.Objects[0].ID != "square" { if g.Objects[0].ID != "square" {
t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Label.MapKey.Value.Unbox() != nil { if g.Objects[0].Label.MapKey.Value.Unbox() != nil {
t.Fatalf("expected g.Objects[0].Attributes.Label.Node.Value.Unbox() == nil: %#v", g.Objects[0].Attributes.Label.MapKey.Value) t.Fatalf("expected g.Objects[0].Label.Node.Value.Unbox() == nil: %#v", g.Objects[0].Label.MapKey.Value)
} }
if d2format.Format(g.Objects[0].Attributes.Label.MapKey.Key) != "square" { if d2format.Format(g.Objects[0].Label.MapKey.Key) != "square" {
t.Fatalf("expected g.Objects[0].Attributes.Label.Node.Key to be square: %#v", g.Objects[0].Attributes.Label.MapKey.Key) t.Fatalf("expected g.Objects[0].Label.Node.Key to be square: %#v", g.Objects[0].Label.MapKey.Key)
} }
}, },
}, },
@ -92,11 +92,11 @@ x 2
if g.Objects[2].AbsID() != "b.c.square" { if g.Objects[2].AbsID() != "b.c.square" {
t.Fatalf("bad absolute ID: %#v", g.Objects[2].AbsID()) t.Fatalf("bad absolute ID: %#v", g.Objects[2].AbsID())
} }
if d2format.Format(g.Objects[2].Attributes.Label.MapKey.Key) != "b.c.square" { if d2format.Format(g.Objects[2].Label.MapKey.Key) != "b.c.square" {
t.Fatalf("bad mapkey: %#v", g.Objects[2].Attributes.Label.MapKey.Key) t.Fatalf("bad mapkey: %#v", g.Objects[2].Label.MapKey.Key)
} }
if g.Objects[2].Attributes.Label.MapKey.Value.Unbox() != nil { if g.Objects[2].Label.MapKey.Value.Unbox() != nil {
t.Fatalf("expected nil mapkey value: %#v", g.Objects[2].Attributes.Label.MapKey.Value) t.Fatalf("expected nil mapkey value: %#v", g.Objects[2].Label.MapKey.Value)
} }
}, },
}, },
@ -116,11 +116,11 @@ square 2
if g.Objects[1].ID != "square 2" { if g.Objects[1].ID != "square 2" {
t.Fatalf("expected g.Objects[1].ID to be square 2: %#v", g.Objects[1]) t.Fatalf("expected g.Objects[1].ID to be square 2: %#v", g.Objects[1])
} }
if g.Objects[1].Attributes.Label.MapKey.Value.Unbox() != nil { if g.Objects[1].Label.MapKey.Value.Unbox() != nil {
t.Fatalf("expected g.Objects[1].Attributes.Label.Node.Value.Unbox() == nil: %#v", g.Objects[1].Attributes.Label.MapKey.Value) t.Fatalf("expected g.Objects[1].Label.Node.Value.Unbox() == nil: %#v", g.Objects[1].Label.MapKey.Value)
} }
if d2format.Format(g.Objects[1].Attributes.Label.MapKey.Key) != "square 2" { if d2format.Format(g.Objects[1].Label.MapKey.Key) != "square 2" {
t.Fatalf("expected g.Objects[1].Attributes.Label.Node.Key to be square 2: %#v", g.Objects[1].Attributes.Label.MapKey.Key) t.Fatalf("expected g.Objects[1].Label.Node.Key to be square 2: %#v", g.Objects[1].Label.MapKey.Key)
} }
}, },
}, },
@ -160,11 +160,11 @@ x.y.z.square 2
if g.Objects[3].ID != "square" { if g.Objects[3].ID != "square" {
t.Fatalf("expected g.Objects[3].ID to be square: %#v", g.Objects[3]) t.Fatalf("expected g.Objects[3].ID to be square: %#v", g.Objects[3])
} }
if g.Objects[3].Attributes.Label.MapKey.Value.Unbox() != nil { if g.Objects[3].Label.MapKey.Value.Unbox() != nil {
t.Fatalf("expected g.Objects[3].Attributes.Label.Node.Value.Unbox() == nil: %#v", g.Objects[3].Attributes.Label.MapKey.Value) t.Fatalf("expected g.Objects[3].Label.Node.Value.Unbox() == nil: %#v", g.Objects[3].Label.MapKey.Value)
} }
if d2format.Format(g.Objects[3].Attributes.Label.MapKey.Key) != "square" { if d2format.Format(g.Objects[3].Label.MapKey.Key) != "square" {
t.Fatalf("expected g.Objects[3].Attributes.Label.Node.Key to be square: %#v", g.Objects[3].Attributes.Label.MapKey.Key) t.Fatalf("expected g.Objects[3].Label.Node.Key to be square: %#v", g.Objects[3].Label.MapKey.Key)
} }
}, },
}, },
@ -188,11 +188,11 @@ x.y.z.square 2
if g.Objects[4].ID != "square 2" { if g.Objects[4].ID != "square 2" {
t.Fatalf("expected g.Objects[4].ID to be square 2: %#v", g.Objects[4]) t.Fatalf("expected g.Objects[4].ID to be square 2: %#v", g.Objects[4])
} }
if g.Objects[4].Attributes.Label.MapKey.Value.Unbox() != nil { if g.Objects[4].Label.MapKey.Value.Unbox() != nil {
t.Fatalf("expected g.Objects[4].Attributes.Label.Node.Value.Unbox() == nil: %#v", g.Objects[4].Attributes.Label.MapKey.Value) t.Fatalf("expected g.Objects[4].Label.Node.Value.Unbox() == nil: %#v", g.Objects[4].Label.MapKey.Value)
} }
if d2format.Format(g.Objects[4].Attributes.Label.MapKey.Key) != "square 2" { if d2format.Format(g.Objects[4].Label.MapKey.Key) != "square 2" {
t.Fatalf("expected g.Objects[4].Attributes.Label.Node.Key to be square 2: %#v", g.Objects[4].Attributes.Label.MapKey.Key) t.Fatalf("expected g.Objects[4].Label.Node.Key to be square 2: %#v", g.Objects[4].Label.MapKey.Key)
} }
}, },
}, },
@ -234,8 +234,8 @@ x.y.z.square 2
if g.Objects[13].ID != "square 11" { if g.Objects[13].ID != "square 11" {
t.Fatalf("expected g.Objects[13].ID to be square 11: %#v", g.Objects[13]) t.Fatalf("expected g.Objects[13].ID to be square 11: %#v", g.Objects[13])
} }
if d2format.Format(g.Objects[13].Attributes.Label.MapKey.Key) != "square 11" { if d2format.Format(g.Objects[13].Label.MapKey.Key) != "square 11" {
t.Fatalf("expected g.Objects[13].Attributes.Label.Node.Key to be square 11: %#v", g.Objects[13].Attributes.Label.MapKey.Key) t.Fatalf("expected g.Objects[13].Label.Node.Key to be square 11: %#v", g.Objects[13].Label.MapKey.Key)
} }
}, },
}, },
@ -517,11 +517,11 @@ func TestSet(t *testing.T) {
if g.Objects[0].ID != "square" { if g.Objects[0].ID != "square" {
t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Label.MapKey.Value.Unbox() != nil { if g.Objects[0].Label.MapKey.Value.Unbox() != nil {
t.Fatalf("expected g.Objects[0].Attributes.Label.Node.Value.Unbox() == nil: %#v", g.Objects[0].Attributes.Label.MapKey.Value) t.Fatalf("expected g.Objects[0].Label.Node.Value.Unbox() == nil: %#v", g.Objects[0].Label.MapKey.Value)
} }
if d2format.Format(g.Objects[0].Attributes.Label.MapKey.Key) != "square" { if d2format.Format(g.Objects[0].Label.MapKey.Key) != "square" {
t.Fatalf("expected g.Objects[0].Attributes.Label.Node.Key to be square: %#v", g.Objects[0].Attributes.Label.MapKey.Key) t.Fatalf("expected g.Objects[0].Label.Node.Key to be square: %#v", g.Objects[0].Label.MapKey.Key)
} }
}, },
}, },
@ -546,8 +546,8 @@ func TestSet(t *testing.T) {
if g.Edges[0].Dst.ID != "y" { if g.Edges[0].Dst.ID != "y" {
t.Fatalf("expected g.Edges[0].Dst.ID == y: %#v", g.Edges[0].Dst.ID) t.Fatalf("expected g.Edges[0].Dst.ID == y: %#v", g.Edges[0].Dst.ID)
} }
if g.Edges[0].Attributes.Label.Value != "two" { if g.Edges[0].Label.Value != "two" {
t.Fatalf("expected g.Edges[0].Attributes.Label.Value == two: %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("expected g.Edges[0].Label.Value == two: %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -566,8 +566,8 @@ func TestSet(t *testing.T) {
if g.Objects[0].ID != "square" { if g.Objects[0].ID != "square" {
t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Shape.Value != d2target.ShapeSquare { if g.Objects[0].Shape.Value != d2target.ShapeSquare {
t.Fatalf("expected g.Objects[0].Attributes.Shape.Value == square: %#v", g.Objects[0].Attributes.Shape.Value) t.Fatalf("expected g.Objects[0].Shape.Value == square: %#v", g.Objects[0].Shape.Value)
} }
}, },
}, },
@ -586,8 +586,8 @@ func TestSet(t *testing.T) {
if g.Objects[0].ID != "square" { if g.Objects[0].ID != "square" {
t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Shape.Value != d2target.ShapeCircle { if g.Objects[0].Shape.Value != d2target.ShapeCircle {
t.Fatalf("expected g.Objects[0].Attributes.Shape.Value == circle: %#v", g.Objects[0].Attributes.Shape.Value) t.Fatalf("expected g.Objects[0].Shape.Value == circle: %#v", g.Objects[0].Shape.Value)
} }
}, },
}, },
@ -606,7 +606,7 @@ func TestSet(t *testing.T) {
if len(g.Objects) != 1 { if len(g.Objects) != 1 {
t.Fatalf("expected 1 object but got %#v", len(g.Objects)) t.Fatalf("expected 1 object but got %#v", len(g.Objects))
} }
f, err := strconv.ParseFloat(g.Objects[0].Attributes.Style.Opacity.Value, 64) f, err := strconv.ParseFloat(g.Objects[0].Style.Opacity.Value, 64)
if err != nil || f != 0.2 { if err != nil || f != 0.2 {
t.Fatalf("expected g.Objects[0].Map.Nodes[0].MapKey.Value.Number.Value.Float64() == 0.2: %#v", f) t.Fatalf("expected g.Objects[0].Map.Nodes[0].MapKey.Value.Number.Value.Float64() == 0.2: %#v", f)
} }
@ -652,7 +652,7 @@ func TestSet(t *testing.T) {
if len(g.AST.Nodes[0].MapKey.Value.Map.Nodes) != 1 { if len(g.AST.Nodes[0].MapKey.Value.Map.Nodes) != 1 {
t.Fatalf("expected 1 node within square but got %v", len(g.AST.Nodes[0].MapKey.Value.Map.Nodes)) t.Fatalf("expected 1 node within square but got %v", len(g.AST.Nodes[0].MapKey.Value.Map.Nodes))
} }
f, err := strconv.ParseFloat(g.Objects[0].Attributes.Style.Opacity.Value, 64) f, err := strconv.ParseFloat(g.Objects[0].Style.Opacity.Value, 64)
if err != nil || f != 0.2 { if err != nil || f != 0.2 {
t.Fatal(err, f) t.Fatal(err, f)
} }
@ -670,7 +670,7 @@ func TestSet(t *testing.T) {
if len(g.AST.Nodes) != 1 { if len(g.AST.Nodes) != 1 {
t.Fatal(g.AST) t.Fatal(g.AST)
} }
f, err := strconv.ParseFloat(g.Objects[0].Attributes.Style.Opacity.Value, 64) f, err := strconv.ParseFloat(g.Objects[0].Style.Opacity.Value, 64)
if err != nil || f != 0.2 { if err != nil || f != 0.2 {
t.Fatal(err, f) t.Fatal(err, f)
} }
@ -689,7 +689,7 @@ square.style.opacity: 0.2
if len(g.AST.Nodes) != 2 { if len(g.AST.Nodes) != 2 {
t.Fatal(g.AST) t.Fatal(g.AST)
} }
f, err := strconv.ParseFloat(g.Objects[0].Attributes.Style.Opacity.Value, 64) f, err := strconv.ParseFloat(g.Objects[0].Style.Opacity.Value, 64)
if err != nil || f != 0.2 { if err != nil || f != 0.2 {
t.Fatal(err, f) t.Fatal(err, f)
} }
@ -877,8 +877,8 @@ square.style.opacity: 0.2
if g.Objects[0].ID != "square" { if g.Objects[0].ID != "square" {
t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Shape.Value == d2target.ShapeSquare { if g.Objects[0].Shape.Value == d2target.ShapeSquare {
t.Fatalf("expected g.Objects[0].Attributes.Shape.Value == square: %#v", g.Objects[0].Attributes.Shape.Value) t.Fatalf("expected g.Objects[0].Shape.Value == square: %#v", g.Objects[0].Shape.Value)
} }
}, },
}, },
@ -897,8 +897,8 @@ square.style.opacity: 0.2
if g.Objects[0].ID != "square" { if g.Objects[0].ID != "square" {
t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0]) t.Fatalf("expected g.Objects[0].ID to be square: %#v", g.Objects[0])
} }
if g.Objects[0].Attributes.Shape.Value == d2target.ShapeSquare { if g.Objects[0].Shape.Value == d2target.ShapeSquare {
t.Fatalf("expected g.Objects[0].Attributes.Shape.Value == square: %#v", g.Objects[0].Attributes.Shape.Value) t.Fatalf("expected g.Objects[0].Shape.Value == square: %#v", g.Objects[0].Shape.Value)
} }
}, },
}, },
@ -920,8 +920,8 @@ square.style.opacity: 0.2
if g.Objects[0].ID != "square" { if g.Objects[0].ID != "square" {
t.Fatal(g.Objects[0]) t.Fatal(g.Objects[0])
} }
if g.Objects[0].Attributes.Label.Value == "I am deeply CONCERNED and I want something GOOD for BREAKFAST!" { if g.Objects[0].Label.Value == "I am deeply CONCERNED and I want something GOOD for BREAKFAST!" {
t.Fatal(g.Objects[0].Attributes.Label.Value) t.Fatal(g.Objects[0].Label.Value)
} }
}, },
}, },
@ -1036,8 +1036,8 @@ z: {
if len(g.Edges) != 2 { if len(g.Edges) != 2 {
t.Fatalf("expected 2 edges: %#v", g.Edges) t.Fatalf("expected 2 edges: %#v", g.Edges)
} }
if g.Edges[0].Attributes.Label.Value != "two" { if g.Edges[0].Label.Value != "two" {
t.Fatalf("expected g.Edges[0].Attributes.Label.Value == two: %#v", g.Edges[0].Attributes.Label.Value) t.Fatalf("expected g.Edges[0].Label.Value == two: %#v", g.Edges[0].Label.Value)
} }
}, },
}, },
@ -1054,8 +1054,8 @@ z: {
if len(g.Objects) != 1 { if len(g.Objects) != 1 {
t.Fatal(g.Objects) t.Fatal(g.Objects)
} }
if g.Objects[0].Attributes.Icon.String() != "https://icons.terrastruct.com/essentials/087-menu.svg" { if g.Objects[0].Icon.String() != "https://icons.terrastruct.com/essentials/087-menu.svg" {
t.Fatal(g.Objects[0].Attributes.Icon.String()) t.Fatal(g.Objects[0].Icon.String())
} }
}, },
}, },
@ -1133,7 +1133,7 @@ z: {
assert.JSON(t, 3, len(g.Objects)) assert.JSON(t, 3, len(g.Objects))
assert.JSON(t, 1, len(g.Edges)) assert.JSON(t, 1, len(g.Edges))
assert.JSON(t, "q", g.Edges[0].Src.ID) assert.JSON(t, "q", g.Edges[0].Src.ID)
assert.JSON(t, "0.4", g.Edges[0].Attributes.Style.Opacity.Value) assert.JSON(t, "0.4", g.Edges[0].Style.Opacity.Value)
}, },
}, },
{ {
@ -1309,8 +1309,8 @@ a.b -> a.c: {style.animated: true}
if g.Edges[0].Src.ID != "q" { if g.Edges[0].Src.ID != "q" {
t.Fatal(g.Edges[0].Src.ID) t.Fatal(g.Edges[0].Src.ID)
} }
if g.Edges[0].Attributes.Style.Opacity.Value != "0.4" { if g.Edges[0].Style.Opacity.Value != "0.4" {
t.Fatal(g.Edges[0].Attributes.Style.Opacity.Value) t.Fatal(g.Edges[0].Style.Opacity.Value)
} }
}, },
}, },

View file

@ -33,19 +33,19 @@ func FeatureSupportCheck(info *PluginInfo, g *d2graph.Graph) error {
} }
for _, obj := range g.Objects { for _, obj := range g.Objects {
if obj.Attributes.Top != nil || obj.Attributes.Left != nil { if obj.Top != nil || obj.Left != nil {
if _, ok := featureMap[TOP_LEFT]; !ok { if _, ok := featureMap[TOP_LEFT]; !ok {
return fmt.Errorf(`Object "%s" has attribute "top" and/or "left" set, but layout engine "%s" does not support locked positions.`, obj.AbsID(), info.Name) return fmt.Errorf(`Object "%s" has attribute "top" and/or "left" set, but layout engine "%s" does not support locked positions.`, obj.AbsID(), info.Name)
} }
} }
if (obj.Attributes.Width != nil || obj.Attributes.Height != nil) && len(obj.ChildrenArray) > 0 { if (obj.WidthAttr != nil || obj.HeightAttr != nil) && len(obj.ChildrenArray) > 0 {
if _, ok := featureMap[CONTAINER_DIMENSIONS]; !ok { if _, ok := featureMap[CONTAINER_DIMENSIONS]; !ok {
return fmt.Errorf(`Object "%s" has attribute "width" and/or "height" set, but layout engine "%s" does not support dimensions set on containers.`, obj.AbsID(), info.Name) return fmt.Errorf(`Object "%s" has attribute "width" and/or "height" set, but layout engine "%s" does not support dimensions set on containers.`, obj.AbsID(), info.Name)
} }
} }
if obj.Attributes.NearKey != nil { if obj.NearKey != nil {
_, isKey := g.Root.HasChild(d2graph.Key(obj.Attributes.NearKey)) _, isKey := g.Root.HasChild(d2graph.Key(obj.NearKey))
if isKey { if isKey {
if _, ok := featureMap[NEAR_OBJECT]; !ok { if _, ok := featureMap[NEAR_OBJECT]; !ok {
return fmt.Errorf(`Object "%s" has "near" set to another object, but layout engine "%s" only supports constant values for "near".`, obj.AbsID(), info.Name) return fmt.Errorf(`Object "%s" has "near" set to another object, but layout engine "%s" only supports constant values for "near".`, obj.AbsID(), info.Name)