From 9c0f49829a14ba0a3b70b8769e4ea4d35c8d2534 Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Mon, 19 Jun 2023 17:33:41 -0700 Subject: [PATCH 01/12] add test --- d2ast/d2ast.go | 12 ++++ d2format/format.go | 40 +++++++++++- d2format/format_test.go | 133 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 182 insertions(+), 3 deletions(-) diff --git a/d2ast/d2ast.go b/d2ast/d2ast.go index 63a7d07f9..43348804d 100644 --- a/d2ast/d2ast.go +++ b/d2ast/d2ast.go @@ -760,6 +760,18 @@ func (mb MapNodeBox) Unbox() MapNode { } } +func (mb MapNodeBox) IsSpecialBoard() bool { + if mb.MapKey == nil || mb.MapKey.Key == nil { + return false + } + switch mb.MapKey.Key.Path[0].Unbox().ScalarString() { + case "layers", "scenarios", "steps": + return true + default: + return false + } +} + // ArrayNodeBox is used to box ArrayNode for JSON persistence. type ArrayNodeBox struct { Comment *Comment `json:"comment,omitempty"` diff --git a/d2format/format.go b/d2format/format.go index e979a6fea..82fd9daac 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -253,9 +253,38 @@ func (p *printer) _map(m *d2ast.Map) { } } - prev := d2ast.Node(m) + nodes := []d2ast.MapNodeBox{} + // extract out layer, scenario, and step nodes + layerNodes := []d2ast.MapNodeBox{} + scenarioNodes := []d2ast.MapNodeBox{} + stepNodes := []d2ast.MapNodeBox{} for i := 0; i < len(m.Nodes); i++ { - nb := m.Nodes[i] + node := m.Nodes[i] + if node.MapKey != nil && node.MapKey.Key != nil { + key := node.MapKey.Key + switch key.Path[0].Unbox().ScalarString() { + case "layers": + layerNodes = append(layerNodes, node) + case "scenarios": + scenarioNodes = append(scenarioNodes, node) + case "steps": + stepNodes = append(stepNodes, node) + default: + nodes = append(nodes, node) + } + } else { + nodes = append(nodes, node) + } + } + + // append layers, scenarios, and steps at the end + nodes = append(nodes, layerNodes...) + nodes = append(nodes, scenarioNodes...) + nodes = append(nodes, stepNodes...) + + prev := d2ast.Node(m) + for i := 0; i < len(nodes); i++ { + nb := nodes[i] n := nb.Unbox() // Handle inline comments. @@ -280,6 +309,13 @@ func (p *printer) _map(m *d2ast.Map) { p.sb.WriteString("; ") } + if m.IsFileMap() && nb.IsSpecialBoard() { + currString := p.sb.String() + // if the the character before the special board is not a newline, we add one + if currString[len(currString)-2:] != "\n\n" { + p.newline() + } + } p.node(n) prev = n } diff --git a/d2format/format_test.go b/d2format/format_test.go index d25a45241..6f0616db0 100644 --- a/d2format/format_test.go +++ b/d2format/format_test.go @@ -153,13 +153,13 @@ meow diagram: int {constraint: foreign_key} } + meow <- diagrams.id steps: { shape: sql_table id: {type: int; constraint: primary_key} representation: {type: jsonb} diagram: int {constraint: foreign_key} } - meow <- diagrams.id } D2 AST Parser: { @@ -617,6 +617,137 @@ y x <= y `, exp: `x <- = y +`, + }, + { + name: "layers_scenarios_steps_bottom_simple", + in: `a + +layers: { + b: { + scenarios: { + p: { + x + } + } + e + } +} + +g +`, + exp: `a + +g + +layers: { + b: { + e + scenarios: { + p: { + x + } + } + } +} +`, + }, + { + name: "layers_scenarios_steps_bottom_complex", + in: `a + +scenarios: { + + + + scenario-1: { + steps: { + step-1: { + Test + } + step-2 + } + non-step + } +} + +layers: { + Test super nested: { + base-layer + layers: { + layers: { + grand-child-layer: { + grand-child-board + } + } + layer-board + } + last-layer + } +} +b +steps: { + 1: { + step-1-content + } +} + + +scenarios: { + scenario-2: { + scenario-2-content + } +} + +c +d +`, + exp: `a + +b + +c +d + +layers: { + Test super nested: { + base-layer + + last-layer + layers: { + layer-board + layers: { + grand-child-layer: { + grand-child-board + } + } + } + } +} + +scenarios: { + scenario-1: { + non-step + steps: { + step-1: { + Test + } + step-2 + } + } +} + +scenarios: { + scenario-2: { + scenario-2-content + } +} + +steps: { + 1: { + step-1-content + } +} `, }, } From fdb73c14b2d0a657a96cb74090e35230813784ed Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Mon, 19 Jun 2023 17:39:05 -0700 Subject: [PATCH 02/12] rename + refactor --- d2ast/d2ast.go | 4 ++-- d2format/format.go | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/d2ast/d2ast.go b/d2ast/d2ast.go index 43348804d..c0b5eedf7 100644 --- a/d2ast/d2ast.go +++ b/d2ast/d2ast.go @@ -760,8 +760,8 @@ func (mb MapNodeBox) Unbox() MapNode { } } -func (mb MapNodeBox) IsSpecialBoard() bool { - if mb.MapKey == nil || mb.MapKey.Key == nil { +func (mb MapNodeBox) IsBoardNode() bool { + if mb.MapKey == nil || mb.MapKey.Key == nil || len(mb.MapKey.Key.Path) == 0 { return false } switch mb.MapKey.Key.Path[0].Unbox().ScalarString() { diff --git a/d2format/format.go b/d2format/format.go index 82fd9daac..102b085d7 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -260,7 +260,7 @@ func (p *printer) _map(m *d2ast.Map) { stepNodes := []d2ast.MapNodeBox{} for i := 0; i < len(m.Nodes); i++ { node := m.Nodes[i] - if node.MapKey != nil && node.MapKey.Key != nil { + if node.IsBoardNode() { key := node.MapKey.Key switch key.Path[0].Unbox().ScalarString() { case "layers": @@ -269,8 +269,6 @@ func (p *printer) _map(m *d2ast.Map) { scenarioNodes = append(scenarioNodes, node) case "steps": stepNodes = append(stepNodes, node) - default: - nodes = append(nodes, node) } } else { nodes = append(nodes, node) @@ -309,7 +307,7 @@ func (p *printer) _map(m *d2ast.Map) { p.sb.WriteString("; ") } - if m.IsFileMap() && nb.IsSpecialBoard() { + if m.IsFileMap() && nb.IsBoardNode() { currString := p.sb.String() // if the the character before the special board is not a newline, we add one if currString[len(currString)-2:] != "\n\n" { From 1407be30416132d019d42f5a00383ae76ec6285b Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Mon, 19 Jun 2023 17:39:38 -0700 Subject: [PATCH 03/12] remove unused new var --- d2format/format.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/d2format/format.go b/d2format/format.go index 102b085d7..f6e3cbf04 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -261,8 +261,7 @@ func (p *printer) _map(m *d2ast.Map) { for i := 0; i < len(m.Nodes); i++ { node := m.Nodes[i] if node.IsBoardNode() { - key := node.MapKey.Key - switch key.Path[0].Unbox().ScalarString() { + switch node.MapKey.Key.Path[0].Unbox().ScalarString() { case "layers": layerNodes = append(layerNodes, node) case "scenarios": From 7b79cfef46b64b09a68e684934a9c2c018d17d9b Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Mon, 19 Jun 2023 17:40:17 -0700 Subject: [PATCH 04/12] comment --- d2format/format.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2format/format.go b/d2format/format.go index f6e3cbf04..f56635240 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -308,7 +308,7 @@ func (p *printer) _map(m *d2ast.Map) { if m.IsFileMap() && nb.IsBoardNode() { currString := p.sb.String() - // if the the character before the special board is not a newline, we add one + // if the two characters before the special board is not a double newline, we add one if currString[len(currString)-2:] != "\n\n" { p.newline() } From 3a03d314a36f5fa3953a35cb55f53135ab4d97bc Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Mon, 19 Jun 2023 17:41:16 -0700 Subject: [PATCH 05/12] add to markdown --- ci/release/changelogs/next.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 0735e46d5..5ebefa915 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -8,6 +8,7 @@ - Watch mode browser uses an error favicon to easily indicate compiler errors. Thanks @sinyo-matu ! [#1240](https://github.com/terrastruct/d2/pull/1240) - Improves grid layout performance when there are many similarly sized shapes. [#1315](https://github.com/terrastruct/d2/pull/1315) - Connections and labels now are adjusted for shapes with `3d` or `multiple`. [#1340](https://github.com/terrastruct/d2/pull/1340) +- The autoformatter moves board declarations to the bottom of its scope. [#1424](https://github.com/terrastruct/d2/pull/1424) #### Bugfixes ⛑️ From 6fabd1b6340a1bec119553d8be5b6576fae39ab6 Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Mon, 19 Jun 2023 17:43:33 -0700 Subject: [PATCH 06/12] comment --- d2format/format.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2format/format.go b/d2format/format.go index f56635240..3f86dad4d 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -308,7 +308,7 @@ func (p *printer) _map(m *d2ast.Map) { if m.IsFileMap() && nb.IsBoardNode() { currString := p.sb.String() - // if the two characters before the special board is not a double newline, we add one + // if the two characters before the board node is not a double newline, we add one if currString[len(currString)-2:] != "\n\n" { p.newline() } From d349c206b005c044f008f3888b6ac61d451dcdb8 Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Mon, 19 Jun 2023 17:48:41 -0700 Subject: [PATCH 07/12] sub --- ci/sub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/sub b/ci/sub index 771c618df..5f8f9b685 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit 771c618dfa0372f8f1dd7975497151ed55223c60 +Subproject commit 5f8f9b6858a96583654d14397f7ea5ad7f0aea51 From d2cfa5eb1dc609ef7c3c92ee788e325c8cdc45d7 Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Mon, 19 Jun 2023 18:40:10 -0700 Subject: [PATCH 08/12] Update d2ast.go --- d2ast/d2ast.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2ast/d2ast.go b/d2ast/d2ast.go index 3398bda54..a5be3fa98 100644 --- a/d2ast/d2ast.go +++ b/d2ast/d2ast.go @@ -795,7 +795,7 @@ func (mb MapNodeBox) Unbox() MapNode { } func (mb MapNodeBox) IsBoardNode() bool { - if mb.MapKey == nil || mb.MapKey.Key == nil || len(mb.MapKey.Key.Path) == 0 { + if mb.MapKey == nil || mb.MapKey.Key == nil || len(mb.MapKey.Key.Path) != 1 { return false } switch mb.MapKey.Key.Path[0].Unbox().ScalarString() { From 2c13a97cddebf59cf78a7964afe2a2dff063389f Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Tue, 20 Jun 2023 11:29:46 -0700 Subject: [PATCH 09/12] refactor, fix tests, next --- ci/release/changelogs/next.md | 6 --- d2format/format.go | 78 ++++++++++++++++++++--------------- d2format/format_test.go | 36 +++++++++------- 3 files changed, 66 insertions(+), 54 deletions(-) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 14878ca73..da3a38d00 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -4,12 +4,6 @@ #### Improvements 🧹 -- Use shape specific sizing for grid containers [#1294](https://github.com/terrastruct/d2/pull/1294) -- Grid diagrams now support nested shapes or grid diagrams [#1309](https://github.com/terrastruct/d2/pull/1309) -- Grid diagrams will now also use `grid-gap`, `vertical-gap`, and `horizontal-gap` for padding [#1309](https://github.com/terrastruct/d2/pull/1309) -- Watch mode browser uses an error favicon to easily indicate compiler errors. Thanks @sinyo-matu ! [#1240](https://github.com/terrastruct/d2/pull/1240) -- Improves grid layout performance when there are many similarly sized shapes. [#1315](https://github.com/terrastruct/d2/pull/1315) -- Connections and labels now are adjusted for shapes with `3d` or `multiple`. [#1340](https://github.com/terrastruct/d2/pull/1340) - Display version on CLI help invocation [#1400](https://github.com/terrastruct/d2/pull/1400) - Improved readability of connection labels when they overlap another connection. [#447](https://github.com/terrastruct/d2/pull/447) - Error message when `shape` is given a composite [#1415](https://github.com/terrastruct/d2/pull/1415) diff --git a/d2format/format.go b/d2format/format.go index 5d5b71271..34b00410e 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -275,36 +275,15 @@ func (p *printer) _map(m *d2ast.Map) { } } - nodes := []d2ast.MapNodeBox{} - // extract out layer, scenario, and step nodes - layerNodes := []d2ast.MapNodeBox{} - scenarioNodes := []d2ast.MapNodeBox{} - stepNodes := []d2ast.MapNodeBox{} - for i := 0; i < len(m.Nodes); i++ { - node := m.Nodes[i] - if node.IsBoardNode() { - switch node.MapKey.Key.Path[0].Unbox().ScalarString() { - case "layers": - layerNodes = append(layerNodes, node) - case "scenarios": - scenarioNodes = append(scenarioNodes, node) - case "steps": - stepNodes = append(stepNodes, node) - } - } else { - nodes = append(nodes, node) - } - } - - // append layers, scenarios, and steps at the end - nodes = append(nodes, layerNodes...) - nodes = append(nodes, scenarioNodes...) - nodes = append(nodes, stepNodes...) - prev := d2ast.Node(m) - for i := 0; i < len(nodes); i++ { - nb := nodes[i] + for i := 0; i < len(m.Nodes); i++ { + nb := m.Nodes[i] n := nb.Unbox() + // skip board nodes as we'll write them at the end + if nb.IsBoardNode() { + prev = n + continue + } // Handle inline comments. if i > 0 && (nb.Comment != nil || nb.BlockComment != nil) { @@ -328,12 +307,43 @@ func (p *printer) _map(m *d2ast.Map) { p.sb.WriteString("; ") } - if m.IsFileMap() && nb.IsBoardNode() { - currString := p.sb.String() - // if the two characters before the board node is not a double newline, we add one - if currString[len(currString)-2:] != "\n\n" { - p.newline() - } + p.node(n) + prev = n + } + + // extract out layer, scenario, and step nodes + layerNodes := []d2ast.MapNodeBox{} + scenarioNodes := []d2ast.MapNodeBox{} + stepNodes := []d2ast.MapNodeBox{} + for i := 0; i < len(m.Nodes); i++ { + node := m.Nodes[i] + if !node.IsBoardNode() { + continue + } + switch node.MapKey.Key.Path[0].Unbox().ScalarString() { + case "layers": + layerNodes = append(layerNodes, node) + case "scenarios": + scenarioNodes = append(scenarioNodes, node) + case "steps": + stepNodes = append(stepNodes, node) + } + } + + boards := []d2ast.MapNodeBox{} + boards = append(boards, layerNodes...) + boards = append(boards, scenarioNodes...) + boards = append(boards, stepNodes...) + + // draw board nodes + for i := 0; i < len(boards); i++ { + n := boards[i].Unbox() + // if this board is the very first line of the file, don't add an extra indent + if n.GetRange().Start.Line != 0 { + p.newline() + } + if len(m.Nodes) > len(boards) { + p.newline() } p.node(n) prev = n diff --git a/d2format/format_test.go b/d2format/format_test.go index 7e034c7cb..de0ccd1cd 100644 --- a/d2format/format_test.go +++ b/d2format/format_test.go @@ -152,8 +152,8 @@ meow representation: {type: jsonb} diagram: int {constraint: foreign_key} } - meow <- diagrams.id + steps: { shape: sql_table id: {type: int; constraint: primary_key} @@ -661,28 +661,21 @@ x: @"x/../file" }, { name: "layers_scenarios_steps_bottom_simple", - in: `a - -layers: { + in: `layers: { b: { + e scenarios: { p: { x } } - e } } - -g `, - exp: `a - -g - -layers: { + exp: `layers: { b: { e + scenarios: { p: { x @@ -741,21 +734,35 @@ scenarios: { c d + +only-layers: { + layers: { + X + Y + } +} `, exp: `a - b c d +only-layers: { + layers: { + X + Y + } +} + layers: { Test super nested: { base-layer - last-layer + layers: { layer-board + layers: { grand-child-layer: { grand-child-board @@ -768,6 +775,7 @@ layers: { scenarios: { scenario-1: { non-step + steps: { step-1: { Test From 456799430d3e827a60dfce3e3d75bf46f182c0a8 Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Tue, 20 Jun 2023 11:40:24 -0700 Subject: [PATCH 10/12] refactor --- d2format/format.go | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/d2format/format.go b/d2format/format.go index 34b00410e..d0e455d83 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -275,12 +275,24 @@ func (p *printer) _map(m *d2ast.Map) { } } + layerNodes := []d2ast.MapNodeBox{} + scenarioNodes := []d2ast.MapNodeBox{} + stepNodes := []d2ast.MapNodeBox{} + prev := d2ast.Node(m) for i := 0; i < len(m.Nodes); i++ { nb := m.Nodes[i] n := nb.Unbox() - // skip board nodes as we'll write them at the end + // extract out layer, scenario, and step nodes and skip if nb.IsBoardNode() { + switch nb.MapKey.Key.Path[0].Unbox().ScalarString() { + case "layers": + layerNodes = append(layerNodes, nb) + case "scenarios": + scenarioNodes = append(scenarioNodes, nb) + case "steps": + stepNodes = append(stepNodes, nb) + } prev = n continue } @@ -311,25 +323,6 @@ func (p *printer) _map(m *d2ast.Map) { prev = n } - // extract out layer, scenario, and step nodes - layerNodes := []d2ast.MapNodeBox{} - scenarioNodes := []d2ast.MapNodeBox{} - stepNodes := []d2ast.MapNodeBox{} - for i := 0; i < len(m.Nodes); i++ { - node := m.Nodes[i] - if !node.IsBoardNode() { - continue - } - switch node.MapKey.Key.Path[0].Unbox().ScalarString() { - case "layers": - layerNodes = append(layerNodes, node) - case "scenarios": - scenarioNodes = append(scenarioNodes, node) - case "steps": - stepNodes = append(stepNodes, node) - } - } - boards := []d2ast.MapNodeBox{} boards = append(boards, layerNodes...) boards = append(boards, scenarioNodes...) From 4bd5642e6242939416454c99013acdaa1e2711fb Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Tue, 20 Jun 2023 13:07:34 -0700 Subject: [PATCH 11/12] fix and add tests --- d2format/format.go | 2 +- d2format/format_test.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/d2format/format.go b/d2format/format.go index d0e455d83..0a3a6f382 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -335,7 +335,7 @@ func (p *printer) _map(m *d2ast.Map) { if n.GetRange().Start.Line != 0 { p.newline() } - if len(m.Nodes) > len(boards) { + if i != 0 || len(m.Nodes) > len(boards) { p.newline() } p.node(n) diff --git a/d2format/format_test.go b/d2format/format_test.go index de0ccd1cd..889b01537 100644 --- a/d2format/format_test.go +++ b/d2format/format_test.go @@ -670,6 +670,9 @@ x: @"x/../file" } } } + steps: { + a + } } `, exp: `layers: { @@ -682,6 +685,10 @@ x: @"x/../file" } } } + + steps: { + a + } } `, }, @@ -740,6 +747,9 @@ only-layers: { X Y } + layers: { + Z + } } `, exp: `a @@ -753,6 +763,10 @@ only-layers: { X Y } + + layers: { + Z + } } layers: { From 13bf1fa6465957f5e2510756fe3ff488a2b370e6 Mon Sep 17 00:00:00 2001 From: Bernard Xie Date: Tue, 20 Jun 2023 13:09:17 -0700 Subject: [PATCH 12/12] comments --- d2format/format.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/d2format/format.go b/d2format/format.go index 0a3a6f382..dc7430ec2 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -331,10 +331,11 @@ func (p *printer) _map(m *d2ast.Map) { // draw board nodes for i := 0; i < len(boards); i++ { n := boards[i].Unbox() - // if this board is the very first line of the file, don't add an extra indent + // if this board is the very first line of the file, don't add an extra newline if n.GetRange().Start.Line != 0 { p.newline() } + // if scope only has boards, don't newline the first board if i != 0 || len(m.Nodes) > len(boards) { p.newline() }