diff --git a/d2chaos/d2chaos.go b/d2chaos/d2chaos.go index 7729454a8..c26ea3c80 100644 --- a/d2chaos/d2chaos.go +++ b/d2chaos/d2chaos.go @@ -18,10 +18,11 @@ import ( func GenDSL(maxi int) (_ string, err error) { gs := &dslGenState{ rand: mathrand.New(mathrand.NewSource(time.Now().UnixNano())), - g: d2graph.NewGraph(&d2ast.Map{}), + g: d2graph.NewGraph(), nodeShapes: make(map[string]string), nodeContainer: make(map[string]string), } + gs.g.AST = &d2ast.Map{} err = gs.gen(maxi) if err != nil { return "", err diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index aa0c78ab7..9e2e9742b 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -25,7 +25,8 @@ const INNER_LABEL_PADDING int = 5 const DEFAULT_SHAPE_PADDING = 100. type Graph struct { - AST *d2ast.Map `json:"ast"` + Name string `json:"name"` + AST *d2ast.Map `json:"ast"` Root *Object `json:"root"` Edges []*Edge `json:"edges"` @@ -36,10 +37,8 @@ type Graph struct { Steps []*Graph `json:"steps,omitempty"` } -func NewGraph(ast *d2ast.Map) *Graph { - d := &Graph{ - AST: ast, - } +func NewGraph() *Graph { + d := &Graph{} d.Root = &Object{ Graph: d, Parent: nil, @@ -552,6 +551,7 @@ func (obj *Object) HasEdge(mk *d2ast.Key) (*Edge, bool) { return nil, false } +// TODO: remove once not used anywhere func ResolveUnderscoreKey(ida []string, obj *Object) (resolvedObj *Object, resolvedIDA []string, _ error) { if len(ida) > 0 && !obj.IsSequenceDiagram() { objSD := obj.OuterSequenceDiagram() @@ -635,6 +635,12 @@ func (obj *Object) FindEdges(mk *d2ast.Key) ([]*Edge, bool) { // EnsureChild grabs the child by ids or creates it if it does not exist including all // intermediate nodes. func (obj *Object) EnsureChild(ids []string) *Object { + switch obj.Attributes.Shape.Value { + case d2target.ShapeClass, d2target.ShapeSQLTable: + // This will only be called for connecting edges where we want to truncate to the + // container. + return obj + } _, is := ReservedKeywordHolders[ids[0]] if len(ids) == 1 && !is { _, ok := ReservedKeywords[ids[0]] @@ -954,7 +960,7 @@ func (obj *Object) Connect(srcID, dstID []string, srcArrow, dstArrow bool, label return nil, errors.New("connections within sequence diagrams can connect only to other objects within the same sequence diagram") } - edge := &Edge{ + e := &Edge{ Attributes: &Attributes{ Label: Scalar{ Value: label, @@ -965,10 +971,40 @@ func (obj *Object) Connect(srcID, dstID []string, srcArrow, dstArrow bool, label Dst: dst, DstArrow: dstArrow, } - edge.initIndex() + e.initIndex() - obj.Graph.Edges = append(obj.Graph.Edges, edge) - return edge, nil + if src.Attributes.Shape.Value == d2target.ShapeSQLTable { + objAbsID := obj.AbsIDArray() + srcAbsID := src.AbsIDArray() + if len(objAbsID) + len(srcID) > len(srcAbsID) { + for i, d2col := range src.SQLTable.Columns { + if d2col.Name.Label == srcID[len(srcID)-1] { + d2col.Reference = dst.AbsID() + e.SrcTableColumnIndex = new(int) + *e.SrcTableColumnIndex = i + break + } + } + } + } + if dst.Attributes.Shape.Value == d2target.ShapeSQLTable { + objAbsID := obj.AbsIDArray() + dstAbsID := dst.AbsIDArray() + if len(objAbsID) + len(dstID) > len(dstAbsID) { + for i, d2col := range dst.SQLTable.Columns { + if d2col.Name.Label == dstID[len(dstID)-1] { + d2col.Reference = dst.AbsID() + e.DstTableColumnIndex = new(int) + *e.DstTableColumnIndex = i + break + } + } + } + } + + + obj.Graph.Edges = append(obj.Graph.Edges, e) + return e, nil } // TODO: Treat undirectional/bidirectional edge here and in HasEdge flipped. Same with diff --git a/d2ir/compile.go b/d2ir/compile.go index 52e77dbd3..18278ac97 100644 --- a/d2ir/compile.go +++ b/d2ir/compile.go @@ -37,9 +37,7 @@ func (c *compiler) compileScenarios(m *Map) { for _, sf := range scenarios.Fields { if sf.Map() == nil { - sf.Composite = &Map{ - parent: sf, - } + continue } base := m.CopyBase(sf) sf.Composite = Overlay(base, sf.Map()) @@ -59,9 +57,7 @@ func (c *compiler) compileSteps(m *Map) { } for i, sf := range steps.Fields { if sf.Map() == nil { - sf.Composite = &Map{ - parent: sf, - } + continue } var base *Map diff --git a/d2ir/d2ir.go b/d2ir/d2ir.go index 2d707d111..ca5a8b5ce 100644 --- a/d2ir/d2ir.go +++ b/d2ir/d2ir.go @@ -18,13 +18,21 @@ import ( // to indicate the offending AST node. type Node interface { node() - Copy(parent Node) Node + Copy(newParent Node) Node Parent() Node Primary() *Scalar Map() *Map - AST() d2ast.Node + ast() d2ast.Node fmt.Stringer + + LastRef() Reference + LastPrimaryKey() *d2ast.Key +} + +type Reference interface { + reference() + AST() d2ast.Node } var _ Node = &Scalar{} @@ -71,12 +79,20 @@ func (n *Map) Primary() *Scalar { return nil } func (n *Scalar) Map() *Map { return nil } func (n *Field) Map() *Map { + if n == nil { + return nil + } if n.Composite == nil { return nil } return n.Composite.Map() } -func (n *Edge) Map() *Map { return n.Map_ } +func (n *Edge) Map() *Map { + if n == nil { + return nil + } + return n.Map_ +} func (n *Array) Map() *Map { return nil } func (n *Map) Map() *Map { return n } @@ -87,22 +103,22 @@ func (n *Map) value() {} func (n *Array) composite() {} func (n *Map) composite() {} -func (n *Scalar) String() string { return d2format.Format(n.AST()) } -func (n *Field) String() string { return d2format.Format(n.AST()) } -func (n *Edge) String() string { return d2format.Format(n.AST()) } -func (n *Array) String() string { return d2format.Format(n.AST()) } -func (n *Map) String() string { return d2format.Format(n.AST()) } +func (n *Scalar) String() string { return d2format.Format(n.ast()) } +func (n *Field) String() string { return d2format.Format(n.ast()) } +func (n *Edge) String() string { return d2format.Format(n.ast()) } +func (n *Array) String() string { return d2format.Format(n.ast()) } +func (n *Map) String() string { return d2format.Format(n.ast()) } type Scalar struct { parent Node Value d2ast.Scalar `json:"value"` } -func (s *Scalar) Copy(newp Node) Node { +func (s *Scalar) Copy(newParent Node) Node { tmp := *s s = &tmp - s.parent = newp + s.parent = newParent return s } @@ -121,11 +137,11 @@ type Map struct { Edges []*Edge `json:"edges"` } -func (m *Map) Copy(newp Node) Node { +func (m *Map) Copy(newParent Node) Node { tmp := *m m = &tmp - m.parent = newp + m.parent = newParent pfields := m.Fields m.Fields = make([]*Field, 0, len(pfields)) for _, f := range pfields { @@ -139,11 +155,11 @@ func (m *Map) Copy(newp Node) Node { } // CopyBase copies the map m without layers/scenarios/steps. -func (m *Map) CopyBase(newp Node) *Map { +func (m *Map) CopyBase(newParent Node) *Map { layers := m.DeleteField("layers") scenarios := m.DeleteField("scenarios") steps := m.DeleteField("steps") - m2 := m.Copy(newp).(*Map) + m2 := m.Copy(newParent).(*Map) if layers != nil { m.Fields = append(m.Fields, layers) } @@ -161,6 +177,17 @@ func (m *Map) Root() bool { return m.parent == nil } +func (f *Map) LastRef() *MapReference { + if f.parent == nil { + return nil + } + return f.References[len(f.References)-1] +} + +func (f *Map) LastAST() d2ast.Node { + return f.LastRef().String +} + type LayerKind string const ( @@ -206,11 +233,11 @@ type Field struct { References []FieldReference `json:"references,omitempty"` } -func (f *Field) Copy(newp Node) Node { +func (f *Field) Copy(newParent Node) Node { tmp := *f f = &tmp - f.parent = newp.(*Map) + f.parent = newParent.(*Map) f.References = append([]FieldReference(nil), f.References...) if f.Primary_ != nil { f.Primary_ = f.Primary_.Copy(f).(*Scalar) @@ -221,6 +248,39 @@ func (f *Field) Copy(newp Node) Node { return f } +func (f *Field) LastPrimaryRef() *FieldReference { + inEdge := ParentEdge(f) != nil + for i := len(f.References) - 1; i >= 0; i-- { + fr := f.References[i] + if inEdge && len(fr.Context.Key.Edges) > 0 { + if fr.String == fr.Context.Key.EdgeKey.Path[len(fr.Context.Key.EdgeKey.Path)-1].Unbox() { + return fr + } + } else { + if fr.String == fr.Context.Key.Key.Path[len(fr.Context.Key.Key.Path)-1].Unbox() { + return fr + } + } + } + return nil +} + +func (f *Field) LastPrimaryKey() *d2ast.Key { + fr := f.LastModification() + if fr == nil { + return nil + } + return fr.Context.Key +} + +func (f *Field) LastRef() *FieldReference { + return f.References[len(f.References)-1] +} + +func (f *Field) LastAST() d2ast.Node { + return f.LastRef().String +} + type EdgeID struct { SrcPath []string `json:"src_path"` SrcArrow bool `json:"src_arrow"` @@ -339,11 +399,11 @@ type Edge struct { References []EdgeReference `json:"references,omitempty"` } -func (e *Edge) Copy(newp Node) Node { +func (e *Edge) Copy(newParent Node) Node { tmp := *e e = &tmp - e.parent = newp.(*Map) + e.parent = newParent.(*Map) e.References = append([]EdgeReference(nil), e.References...) if e.Primary_ != nil { e.Primary_ = e.Primary_.Copy(e).(*Scalar) @@ -354,16 +414,42 @@ func (e *Edge) Copy(newp Node) Node { return e } +func (e *Edge) LastPrimaryRef() *EdgeReference { + for i := len(e.References) - 1; i >= 0; i-- { + fr := e.References[i] + if fr.Context.Key.EdgeKey == nil { + return fr + } + } + return nil +} + +func (e *Edge) LastPrimaryKey() *d2ast.Key { + fr := f.LastModification() + if fr == nil { + return nil + } + return fr.Context.Key +} + +func (e *Edge) LastRef() *EdgeReference { + return e.References[len(e.References)-1] +} + +func (e *Edge) LastAST() d2ast.Node { + return e.LastRef().Context.Edge +} + type Array struct { parent Node Values []Value `json:"values"` } -func (a *Array) Copy(newp Node) Node { +func (a *Array) Copy(newParent Node) Node { tmp := *a a = &tmp - a.parent = newp + a.parent = newParent a.Values = append([]Value(nil), a.Values...) for i := range a.Values { a.Values[i] = a.Values[i].Copy(a).(Value) @@ -400,8 +486,8 @@ type EdgeReference struct { } type RefContext struct { - Key *d2ast.Key `json:"key"` Edge *d2ast.Edge `json:"edge"` + Key *d2ast.Key `json:"key"` Scope *d2ast.Map `json:"-"` } @@ -696,11 +782,11 @@ func (m *Map) CreateEdge(eid *EdgeID, refctx *RefContext) (*Edge, error) { return e, nil } -func (s *Scalar) AST() d2ast.Node { +func (s *Scalar) ast() d2ast.Node { return s.Value } -func (f *Field) AST() d2ast.Node { +func (f *Field) ast() d2ast.Node { k := &d2ast.Key{ Key: &d2ast.KeyPath{ Path: []*d2ast.StringBox{ @@ -710,16 +796,16 @@ func (f *Field) AST() d2ast.Node { } if f.Primary_ != nil { - k.Primary = d2ast.MakeValueBox(f.Primary_.AST().(d2ast.Value)).ScalarBox() + k.Primary = d2ast.MakeValueBox(f.Primary_.ast().(d2ast.Value)).ScalarBox() } if f.Composite != nil { - k.Value = d2ast.MakeValueBox(f.Composite.AST().(d2ast.Value)) + k.Value = d2ast.MakeValueBox(f.Composite.ast().(d2ast.Value)) } return k } -func (e *Edge) AST() d2ast.Node { +func (e *Edge) ast() d2ast.Node { astEdge := &d2ast.Edge{} astEdge.Src = d2ast.MakeKeyPath(e.ID.SrcPath) @@ -736,27 +822,27 @@ func (e *Edge) AST() d2ast.Node { } if e.Primary_ != nil { - k.Primary = d2ast.MakeValueBox(e.Primary_.AST().(d2ast.Value)).ScalarBox() + k.Primary = d2ast.MakeValueBox(e.Primary_.ast().(d2ast.Value)).ScalarBox() } if e.Map_ != nil { - k.Value = d2ast.MakeValueBox(e.Map_.AST().(*d2ast.Map)) + k.Value = d2ast.MakeValueBox(e.Map_.ast().(*d2ast.Map)) } return k } -func (a *Array) AST() d2ast.Node { +func (a *Array) ast() d2ast.Node { if a == nil { return nil } astArray := &d2ast.Array{} for _, av := range a.Values { - astArray.Nodes = append(astArray.Nodes, d2ast.MakeArrayNodeBox(av.AST().(d2ast.ArrayNode))) + astArray.Nodes = append(astArray.Nodes, d2ast.MakeArrayNodeBox(av.ast().(d2ast.ArrayNode))) } return astArray } -func (m *Map) AST() d2ast.Node { +func (m *Map) ast() d2ast.Node { if m == nil { return nil } @@ -767,10 +853,10 @@ func (m *Map) AST() d2ast.Node { astMap.Range = d2ast.MakeRange(",1:0:0-2:0:0") } for _, f := range m.Fields { - astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(f.AST().(d2ast.MapNode))) + astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(f.ast().(d2ast.MapNode))) } for _, e := range m.Edges { - astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(e.AST().(d2ast.MapNode))) + astMap.Nodes = append(astMap.Nodes, d2ast.MakeMapNodeBox(e.ast().(d2ast.MapNode))) } return astMap } diff --git a/d2layouts/d2sequence/layout_test.go b/d2layouts/d2sequence/layout_test.go index 77767f852..20220ce03 100644 --- a/d2layouts/d2sequence/layout_test.go +++ b/d2layouts/d2sequence/layout_test.go @@ -377,7 +377,7 @@ container -> c: edge 1 } func TestSelfEdges(t *testing.T) { - g := d2graph.NewGraph(nil) + g := d2graph.NewGraph() g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram} n1 := g.Root.EnsureChild([]string{"n1"}) n1.Box = geo.NewBox(nil, 100, 100) @@ -413,7 +413,7 @@ func TestSelfEdges(t *testing.T) { } func TestSequenceToDescendant(t *testing.T) { - g := d2graph.NewGraph(nil) + g := d2graph.NewGraph() g.Root.Attributes.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram} a := g.Root.EnsureChild([]string{"a"}) a.Box = geo.NewBox(nil, 100, 100)