layout with grids
This commit is contained in:
parent
5b22382cfd
commit
d139eeadad
11 changed files with 493 additions and 4 deletions
|
|
@ -362,6 +362,32 @@ func (c *compiler) compileReserved(attrs *d2graph.Attributes, f *d2ir.Field) {
|
||||||
}
|
}
|
||||||
attrs.Constraint.Value = scalar.ScalarString()
|
attrs.Constraint.Value = scalar.ScalarString()
|
||||||
attrs.Constraint.MapKey = f.LastPrimaryKey()
|
attrs.Constraint.MapKey = f.LastPrimaryKey()
|
||||||
|
case "rows":
|
||||||
|
v, err := strconv.Atoi(scalar.ScalarString())
|
||||||
|
if err != nil {
|
||||||
|
c.errorf(scalar, "non-integer rows %#v: %s", scalar.ScalarString(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v < 0 {
|
||||||
|
c.errorf(scalar, "rows must be a non-negative integer: %#v", scalar.ScalarString())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
attrs.Rows = &d2graph.Scalar{}
|
||||||
|
attrs.Rows.Value = scalar.ScalarString()
|
||||||
|
attrs.Rows.MapKey = f.LastPrimaryKey()
|
||||||
|
case "columns":
|
||||||
|
v, err := strconv.Atoi(scalar.ScalarString())
|
||||||
|
if err != nil {
|
||||||
|
c.errorf(scalar, "non-integer columns %#v: %s", scalar.ScalarString(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if v < 0 {
|
||||||
|
c.errorf(scalar, "columns must be a non-negative integer: %#v", scalar.ScalarString())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
attrs.Columns = &d2graph.Scalar{}
|
||||||
|
attrs.Columns.Value = scalar.ScalarString()
|
||||||
|
attrs.Columns.MapKey = f.LastPrimaryKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
if attrs.Link != nil && attrs.Tooltip != nil {
|
if attrs.Link != nil && attrs.Tooltip != nil {
|
||||||
|
|
|
||||||
|
|
@ -2268,6 +2268,26 @@ obj {
|
||||||
`,
|
`,
|
||||||
expErr: `d2/testdata/d2compiler/TestCompile/near_near_const.d2:7:8: near keys cannot be set to an object with a constant near key`,
|
expErr: `d2/testdata/d2compiler/TestCompile/near_near_const.d2:7:8: near keys cannot be set to an object with a constant near key`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "grid",
|
||||||
|
text: `hey: {
|
||||||
|
rows: 200
|
||||||
|
columns: 230
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
assertions: func(t *testing.T, g *d2graph.Graph) {
|
||||||
|
tassert.Equal(t, "200", g.Objects[0].Attributes.Rows.Value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grid_negative",
|
||||||
|
text: `hey: {
|
||||||
|
rows: 200
|
||||||
|
columns: -200
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
expErr: `d2/testdata/d2compiler/TestCompile/grid_negative.d2:3:11: columns must be a non-negative integer: "-200"`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"oss.terrastruct.com/d2/d2compiler"
|
"oss.terrastruct.com/d2/d2compiler"
|
||||||
"oss.terrastruct.com/d2/d2exporter"
|
"oss.terrastruct.com/d2/d2exporter"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
|
"oss.terrastruct.com/d2/d2layouts/d2grid"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||||
"oss.terrastruct.com/d2/d2target"
|
"oss.terrastruct.com/d2/d2target"
|
||||||
"oss.terrastruct.com/d2/lib/geo"
|
"oss.terrastruct.com/d2/lib/geo"
|
||||||
|
|
@ -231,7 +232,7 @@ func run(t *testing.T, tc testCase) {
|
||||||
err = g.SetDimensions(nil, ruler, nil)
|
err = g.SetDimensions(nil, ruler, nil)
|
||||||
assert.JSON(t, nil, err)
|
assert.JSON(t, nil, err)
|
||||||
|
|
||||||
err = d2sequence.Layout(ctx, g, d2dagrelayout.DefaultLayout)
|
err = d2sequence.Layout(ctx, g, d2grid.Layout(ctx, g, d2dagrelayout.DefaultLayout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package d2graph
|
package d2graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
|
@ -67,6 +68,8 @@ func (g *Graph) RootBoard() *Graph {
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LayoutGraph func(context.Context, *Graph) error
|
||||||
|
|
||||||
// TODO consider having different Scalar types
|
// TODO consider having different Scalar types
|
||||||
// Right now we'll hold any types in Value and just convert, e.g. floats
|
// Right now we'll hold any types in Value and just convert, e.g. floats
|
||||||
type Scalar struct {
|
type Scalar struct {
|
||||||
|
|
@ -129,6 +132,9 @@ type Attributes struct {
|
||||||
|
|
||||||
Direction Scalar `json:"direction"`
|
Direction Scalar `json:"direction"`
|
||||||
Constraint Scalar `json:"constraint"`
|
Constraint Scalar `json:"constraint"`
|
||||||
|
|
||||||
|
Rows *Scalar `json:"rows,omitempty"`
|
||||||
|
Columns *Scalar `json:"columns,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO references at the root scope should have their Scope set to root graph AST
|
// TODO references at the root scope should have their Scope set to root graph AST
|
||||||
|
|
@ -1534,6 +1540,8 @@ var SimpleReservedKeywords = map[string]struct{}{
|
||||||
"direction": {},
|
"direction": {},
|
||||||
"top": {},
|
"top": {},
|
||||||
"left": {},
|
"left": {},
|
||||||
|
"rows": {},
|
||||||
|
"columns": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReservedKeywordHolders are reserved keywords that are meaningless on its own and exist solely to hold a set of reserved keywords
|
// ReservedKeywordHolders are reserved keywords that are meaningless on its own and exist solely to hold a set of reserved keywords
|
||||||
|
|
|
||||||
6
d2graph/grid.go
Normal file
6
d2graph/grid.go
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package d2graph
|
||||||
|
|
||||||
|
func (obj *Object) IsGrid() bool {
|
||||||
|
return obj != nil && obj.Attributes != nil && len(obj.ChildrenArray) != 0 &&
|
||||||
|
(obj.Attributes.Rows != nil || obj.Attributes.Columns != nil)
|
||||||
|
}
|
||||||
223
d2layouts/d2grid/layout.go
Normal file
223
d2layouts/d2grid/layout.go
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
package d2grid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
|
"oss.terrastruct.com/d2/lib/geo"
|
||||||
|
"oss.terrastruct.com/d2/lib/label"
|
||||||
|
"oss.terrastruct.com/util-go/go2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CONTAINER_PADDING = 60.
|
||||||
|
const HORIZONTAL_PAD = 40.
|
||||||
|
const VERTICAL_PAD = 40.
|
||||||
|
|
||||||
|
type grid struct {
|
||||||
|
root *d2graph.Object
|
||||||
|
nodes []*d2graph.Object
|
||||||
|
rows int
|
||||||
|
columns int
|
||||||
|
|
||||||
|
width float64
|
||||||
|
height float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGrid(root *d2graph.Object) *grid {
|
||||||
|
g := grid{root: root, nodes: root.ChildrenArray}
|
||||||
|
if root.Attributes.Rows != nil {
|
||||||
|
g.rows, _ = strconv.Atoi(root.Attributes.Rows.Value)
|
||||||
|
}
|
||||||
|
if root.Attributes.Columns != nil {
|
||||||
|
g.columns, _ = strconv.Atoi(root.Attributes.Columns.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute exact row/column count based on values entered
|
||||||
|
// TODO consider making this based on node dimensions
|
||||||
|
if g.rows == 0 {
|
||||||
|
// set rows based on number of columns
|
||||||
|
if g.columns == 0 {
|
||||||
|
// 0,0: put everything in one row
|
||||||
|
g.rows = 1
|
||||||
|
g.columns = len(g.nodes)
|
||||||
|
} else {
|
||||||
|
g.rows = len(g.nodes) / g.columns
|
||||||
|
if len(g.nodes)%g.columns != 0 {
|
||||||
|
g.rows++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if g.columns == 0 {
|
||||||
|
// set columns based on number of rows
|
||||||
|
g.columns = len(g.nodes) / g.rows
|
||||||
|
if len(g.nodes)%g.rows != 0 {
|
||||||
|
g.columns++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// rows and columns specified (add more rows if needed)
|
||||||
|
capacity := g.rows * g.columns
|
||||||
|
for capacity < len(g.nodes) {
|
||||||
|
g.rows++
|
||||||
|
capacity += g.columns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *grid) shift(dx, dy float64) {
|
||||||
|
for _, obj := range g.nodes {
|
||||||
|
obj.TopLeft.X += dx
|
||||||
|
obj.TopLeft.Y += dy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layout runs the grid layout on containers with rows/columns
|
||||||
|
// Note: children are not allowed edges or descendants
|
||||||
|
//
|
||||||
|
// 1. Traverse graph from root, skip objects with no rows/columns
|
||||||
|
// 2. Construct a grid with the container children
|
||||||
|
// 3. Remove the children from the main graph
|
||||||
|
// 4. Run grid layout
|
||||||
|
// 5. Set the resulting dimensions to the main graph shape
|
||||||
|
// 6. Run core layouts (without grid children)
|
||||||
|
// 7. Put grid children back in correct location
|
||||||
|
func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) d2graph.LayoutGraph {
|
||||||
|
return func(ctx context.Context, g *d2graph.Graph) error {
|
||||||
|
grids, objectOrder, err := withoutGrids(ctx, g)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.Root.IsGrid() {
|
||||||
|
g.Root.TopLeft = geo.NewPoint(0, 0)
|
||||||
|
} else if err := layout(ctx, g); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup(g, grids, objectOrder)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*grid, error) {
|
||||||
|
grid := newGrid(obj)
|
||||||
|
|
||||||
|
// position nodes
|
||||||
|
cursor := geo.NewPoint(0, 0)
|
||||||
|
maxWidth := 0.
|
||||||
|
for i := 0; i < grid.rows; i++ {
|
||||||
|
maxHeight := 0.
|
||||||
|
for j := 0; j < grid.columns; j++ {
|
||||||
|
n := grid.nodes[i*grid.columns+j]
|
||||||
|
n.TopLeft = cursor.Copy()
|
||||||
|
cursor.X += n.Width + HORIZONTAL_PAD
|
||||||
|
maxHeight = math.Max(maxHeight, n.Height)
|
||||||
|
}
|
||||||
|
maxWidth = math.Max(maxWidth, cursor.X-HORIZONTAL_PAD)
|
||||||
|
cursor.X = 0
|
||||||
|
cursor.Y += float64(maxHeight) + VERTICAL_PAD
|
||||||
|
}
|
||||||
|
grid.width = maxWidth
|
||||||
|
grid.height = cursor.Y - VERTICAL_PAD
|
||||||
|
|
||||||
|
// position labels and icons
|
||||||
|
for _, n := range grid.nodes {
|
||||||
|
if n.Attributes.Icon != nil {
|
||||||
|
n.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||||
|
n.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||||
|
} else {
|
||||||
|
n.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func withoutGrids(ctx context.Context, g *d2graph.Graph) (idToGrid map[string]*grid, objectOrder map[string]int, err error) {
|
||||||
|
toRemove := make(map[*d2graph.Object]struct{})
|
||||||
|
grids := make(map[string]*grid)
|
||||||
|
|
||||||
|
if len(g.Objects) > 0 {
|
||||||
|
queue := make([]*d2graph.Object, 1, len(g.Objects))
|
||||||
|
queue[0] = g.Root
|
||||||
|
for len(queue) > 0 {
|
||||||
|
obj := queue[0]
|
||||||
|
queue = queue[1:]
|
||||||
|
if len(obj.ChildrenArray) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !obj.IsGrid() {
|
||||||
|
queue = append(queue, obj.ChildrenArray...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
grid, err := layoutGrid(g, obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
obj.Children = make(map[string]*d2graph.Object)
|
||||||
|
obj.ChildrenArray = nil
|
||||||
|
obj.Box = geo.NewBox(nil, grid.width+CONTAINER_PADDING*2, grid.height+CONTAINER_PADDING*2)
|
||||||
|
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||||
|
grids[obj.AbsID()] = grid
|
||||||
|
|
||||||
|
for _, node := range grid.nodes {
|
||||||
|
toRemove[node] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
objectOrder = make(map[string]int)
|
||||||
|
layoutObjects := make([]*d2graph.Object, 0, len(toRemove))
|
||||||
|
for i, obj := range g.Objects {
|
||||||
|
objectOrder[obj.AbsID()] = i
|
||||||
|
if _, exists := toRemove[obj]; !exists {
|
||||||
|
layoutObjects = append(layoutObjects, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.Objects = layoutObjects
|
||||||
|
|
||||||
|
return grids, objectOrder, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup restores the graph after the core layout engine finishes
|
||||||
|
// - translating the grid to its position placed by the core layout engine
|
||||||
|
// - restore the children of the grid
|
||||||
|
// - sorts objects to their original graph order
|
||||||
|
func cleanup(g *d2graph.Graph, grids map[string]*grid, objectsOrder map[string]int) {
|
||||||
|
var objects []*d2graph.Object
|
||||||
|
if g.Root.IsGrid() {
|
||||||
|
objects = []*d2graph.Object{g.Root}
|
||||||
|
} else {
|
||||||
|
objects = g.Objects
|
||||||
|
}
|
||||||
|
for _, obj := range objects {
|
||||||
|
if _, exists := grids[obj.AbsID()]; !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||||
|
sd := grids[obj.AbsID()]
|
||||||
|
|
||||||
|
// shift the grid from (0, 0)
|
||||||
|
sd.shift(
|
||||||
|
obj.TopLeft.X+CONTAINER_PADDING,
|
||||||
|
obj.TopLeft.Y+CONTAINER_PADDING,
|
||||||
|
)
|
||||||
|
|
||||||
|
obj.Children = make(map[string]*d2graph.Object)
|
||||||
|
obj.ChildrenArray = make([]*d2graph.Object, 0)
|
||||||
|
for _, child := range sd.nodes {
|
||||||
|
obj.Children[child.ID] = child
|
||||||
|
obj.ChildrenArray = append(obj.ChildrenArray, child)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Objects = append(g.Objects, grids[obj.AbsID()].nodes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.SliceStable(g.Objects, func(i, j int) bool {
|
||||||
|
return objectsOrder[g.Objects[i].AbsID()] < objectsOrder[g.Objects[j].AbsID()]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -78,7 +78,7 @@ func WithoutSequenceDiagrams(ctx context.Context, g *d2graph.Graph) (map[string]
|
||||||
// 5. Set the resulting dimensions to the main graph shape
|
// 5. Set the resulting dimensions to the main graph shape
|
||||||
// 6. Run core layouts (still without sequence diagram innards)
|
// 6. Run core layouts (still without sequence diagram innards)
|
||||||
// 7. Put back sequence diagram innards in correct location
|
// 7. Put back sequence diagram innards in correct location
|
||||||
func Layout(ctx context.Context, g *d2graph.Graph, layout func(ctx context.Context, g *d2graph.Graph) error) error {
|
func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) error {
|
||||||
sequenceDiagrams, objectOrder, edgeOrder, err := WithoutSequenceDiagrams(ctx, g)
|
sequenceDiagrams, objectOrder, edgeOrder, err := WithoutSequenceDiagrams(ctx, g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"oss.terrastruct.com/d2/d2exporter"
|
"oss.terrastruct.com/d2/d2exporter"
|
||||||
"oss.terrastruct.com/d2/d2graph"
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
|
"oss.terrastruct.com/d2/d2layouts/d2grid"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2near"
|
"oss.terrastruct.com/d2/d2layouts/d2near"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||||
|
|
@ -70,7 +71,9 @@ func compile(ctx context.Context, g *d2graph.Graph, opts *CompileOptions) (*d2ta
|
||||||
|
|
||||||
constantNears := d2near.WithoutConstantNears(ctx, g)
|
constantNears := d2near.WithoutConstantNears(ctx, g)
|
||||||
|
|
||||||
err = d2sequence.Layout(ctx, g, coreLayout)
|
layoutWithGrids := d2grid.Layout(ctx, g, coreLayout)
|
||||||
|
|
||||||
|
err = d2sequence.Layout(ctx, g, layoutWithGrids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +113,7 @@ func compile(ctx context.Context, g *d2graph.Graph, opts *CompileOptions) (*d2ta
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLayout(opts *CompileOptions) (func(context.Context, *d2graph.Graph) error, error) {
|
func getLayout(opts *CompileOptions) (d2graph.LayoutGraph, error) {
|
||||||
if opts.Layout != nil {
|
if opts.Layout != nil {
|
||||||
return opts.Layout, nil
|
return opts.Layout, nil
|
||||||
} else if os.Getenv("D2_LAYOUT") == "dagre" {
|
} else if os.Getenv("D2_LAYOUT") == "dagre" {
|
||||||
|
|
|
||||||
|
|
@ -314,6 +314,16 @@ func _set(g *d2graph.Graph, key string, tag, value *string) error {
|
||||||
attrs.Left.MapKey.SetScalar(mk.Value.ScalarBox())
|
attrs.Left.MapKey.SetScalar(mk.Value.ScalarBox())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
case "rows":
|
||||||
|
if attrs.Rows != nil && attrs.Rows.MapKey != nil {
|
||||||
|
attrs.Rows.MapKey.SetScalar(mk.Value.ScalarBox())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "columns":
|
||||||
|
if attrs.Columns != nil && attrs.Columns.MapKey != nil {
|
||||||
|
attrs.Columns.MapKey.SetScalar(mk.Value.ScalarBox())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
case "source-arrowhead", "target-arrowhead":
|
case "source-arrowhead", "target-arrowhead":
|
||||||
if reservedKey == "source-arrowhead" {
|
if reservedKey == "source-arrowhead" {
|
||||||
attrs = edge.SrcArrowhead
|
attrs = edge.SrcArrowhead
|
||||||
|
|
|
||||||
180
testdata/d2compiler/TestCompile/grid.exp.json
generated
vendored
Normal file
180
testdata/d2compiler/TestCompile/grid.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
{
|
||||||
|
"graph": {
|
||||||
|
"name": "",
|
||||||
|
"isFolderOnly": false,
|
||||||
|
"ast": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,0:0:0-4:0:34",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"map_key": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,0:0:0-3:1:33",
|
||||||
|
"key": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,0:0:0-0:3:3",
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"unquoted_string": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,0:0:0-0:3:3",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "hey",
|
||||||
|
"raw_string": "hey"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"primary": {},
|
||||||
|
"value": {
|
||||||
|
"map": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,0:5:5-3:0:32",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"map_key": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,1:1:8-1:10:17",
|
||||||
|
"key": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,1:1:8-1:5:12",
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"unquoted_string": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,1:1:8-1:5:12",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "rows",
|
||||||
|
"raw_string": "rows"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"primary": {},
|
||||||
|
"value": {
|
||||||
|
"number": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,1:7:14-1:10:17",
|
||||||
|
"raw": "200",
|
||||||
|
"value": "200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"map_key": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,2:1:19-2:13:31",
|
||||||
|
"key": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,2:1:19-2:8:26",
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"unquoted_string": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,2:1:19-2:8:26",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "columns",
|
||||||
|
"raw_string": "columns"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"primary": {},
|
||||||
|
"value": {
|
||||||
|
"number": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,2:10:28-2:13:31",
|
||||||
|
"raw": "230",
|
||||||
|
"value": "230"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"id": "",
|
||||||
|
"id_val": "",
|
||||||
|
"label_dimensions": {
|
||||||
|
"width": 0,
|
||||||
|
"height": 0
|
||||||
|
},
|
||||||
|
"attributes": {
|
||||||
|
"label": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"style": {},
|
||||||
|
"near_key": null,
|
||||||
|
"shape": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"direction": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"constraint": {
|
||||||
|
"value": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zIndex": 0
|
||||||
|
},
|
||||||
|
"edges": null,
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"id": "hey",
|
||||||
|
"id_val": "hey",
|
||||||
|
"label_dimensions": {
|
||||||
|
"width": 0,
|
||||||
|
"height": 0
|
||||||
|
},
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,0:0:0-0:3:3",
|
||||||
|
"path": [
|
||||||
|
{
|
||||||
|
"unquoted_string": {
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid.d2,0:0:0-0:3:3",
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"string": "hey",
|
||||||
|
"raw_string": "hey"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"key_path_index": 0,
|
||||||
|
"map_key_edge_index": -1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributes": {
|
||||||
|
"label": {
|
||||||
|
"value": "hey"
|
||||||
|
},
|
||||||
|
"style": {},
|
||||||
|
"near_key": null,
|
||||||
|
"shape": {
|
||||||
|
"value": "rectangle"
|
||||||
|
},
|
||||||
|
"direction": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"constraint": {
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
"rows": {
|
||||||
|
"value": "200"
|
||||||
|
},
|
||||||
|
"columns": {
|
||||||
|
"value": "230"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zIndex": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"err": null
|
||||||
|
}
|
||||||
12
testdata/d2compiler/TestCompile/grid_negative.exp.json
generated
vendored
Normal file
12
testdata/d2compiler/TestCompile/grid_negative.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"graph": null,
|
||||||
|
"err": {
|
||||||
|
"ioerr": null,
|
||||||
|
"errs": [
|
||||||
|
{
|
||||||
|
"range": "d2/testdata/d2compiler/TestCompile/grid_negative.d2,2:10:28-2:14:32",
|
||||||
|
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_negative.d2:3:11: columns must be a non-negative integer: \"-200\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue