Merge branch 'master' into png-img

This commit is contained in:
Alexander Wang 2022-11-29 14:47:00 -08:00
commit 7ba3a93217
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
33 changed files with 3447 additions and 191 deletions

6
.gitattributes vendored
View file

@ -1,3 +1,9 @@
# Leading ** is required.
**/testdata/**/*.json linguist-generated
go.mod linguist-generated
go.sum linguist-generated
d2layouts/d2dagrelayout/dagre.js linguist-vendored
d2layouts/d2elklayout/elk.js linguist-vendored
d2renderers/d2svg/github-markdown.css linguist-vendored
d2renderers/d2latex/mathjax.js linguist-vendored
d2renderers/d2latex/polyfills.js linguist-vendored

View file

@ -5,17 +5,7 @@ concurrency:
cancel-in-progress: true
jobs:
assert-linear:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- run: COLOR=1 ./make.sh assert-linear
env:
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
fmt:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -25,63 +15,7 @@ jobs:
with:
go-version-file: ./go.mod
cache: true
- run: COLOR=1 ./make.sh fmt
env:
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
gen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-go@v3
with:
go-version-file: ./go.mod
cache: true
- run: COLOR=1 ./make.sh gen
env:
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-go@v3
with:
go-version-file: ./go.mod
cache: true
- run: COLOR=1 ./make.sh lint
env:
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-go@v3
with:
go-version-file: ./go.mod
cache: true
- run: COLOR=1 ./make.sh build
env:
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-go@v3
with:
go-version-file: ./go.mod
cache: true
- run: COLOR=1 ./make.sh test
- run: COLOR=1 ./make.sh all race
env:
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
@ -90,22 +24,3 @@ jobs:
with:
name: d2chaos-test
path: ./d2chaos/out
race:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: actions/setup-go@v3
with:
go-version-file: ./go.mod
cache: true
- run: COLOR=1 ./make.sh race
env:
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
- uses: actions/upload-artifact@v3
if: always()
with:
name: d2chaos-race
path: ./d2chaos/out

View file

@ -10,7 +10,7 @@ concurrency:
cancel-in-progress: true
jobs:
all:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
@ -20,7 +20,7 @@ jobs:
with:
go-version-file: ./go.mod
cache: true
- run: CI_ALL=1 COLOR=1 ./make.sh
- run: COLOR=1 CI_FORCE=1 ./make.sh all race
env:
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}

View file

@ -2,9 +2,6 @@
.PHONY: all
all: fmt gen lint build test
ifdef CI
all: assert-linear
endif
.PHONY: fmt
fmt:
@ -24,6 +21,3 @@ test:
.PHONY: race
race:
prefix "$@" ./ci/test.sh --race ./...
.PHONY: assert-linear
assert-linear:
prefix "$@" ./ci/sub/assert_linear.sh

View file

@ -14,7 +14,7 @@ help() {
fi
cat <<EOF
usage: $arg0 [--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix /usr/local]
usage: $arg0 [-d|--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix /usr/local]
[--tala latest] [--force] [--uninstall]
install.sh automates the installation of D2 onto your system. It currently only supports
@ -26,7 +26,7 @@ If you pass --edge, it will clone the source, build a release and install from i
Flags:
--dry-run
-d, --dry-run
Pass to have install.sh show the install method and flags that will be used to install
without executing them. Very useful to understand what changes it will make to your system.
@ -101,7 +101,7 @@ main() {
help
return 0
;;
dry-run)
d|dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
@ -188,12 +188,16 @@ main() {
if [ -n "${UNINSTALL-}" ]; then
uninstall
if [ -n "${DRY_RUN-}" ]; then
log "Rerun without --dry-run to execute printed commands and perform uninstall."
FGCOLOR=3 bigheader "***********
Rerun without --dry-run to execute printed commands and perform install.
***********"
fi
else
install
if [ -n "${DRY_RUN-}" ]; then
log "Rerun without --dry-run to execute printed commands and perform install."
FGCOLOR=3 bigheader "***********
Rerun without --dry-run to execute printed commands and perform install.
***********"
fi
fi
}

View file

@ -1,14 +1,20 @@
#### Features 🚀
- Latex is now supported. See [docs](https://d2lang.com/tour/text) for more.
[#229](https://github.com/terrastruct/d2/pull/229)
- Arrowhead labels are now supported. [#182](https://github.com/terrastruct/d2/pull/182)
- `stroke-dash` on shapes is now supported. [#188](https://github.com/terrastruct/d2/issues/188)
- `font-color` is now supported on shapes and connections. [#215](https://github.com/terrastruct/d2/pull/215)
- Querying shapes and connections by ID is now supported in renders. [#218](https://github.com/terrastruct/d2/pull/218)
- [install.sh](./install.sh) now accepts `-d` as an alias for `--dry-run`.
[#266](https://github.com/terrastruct/d2/pull/266)
#### Improvements 🔧
- Local images can now be included, e.g. `icon: ./my_img.png`.
[#146](https://github.com/terrastruct/d2/issues/146)
- [install.sh](./install.sh) prints the dry run message more visibly.
[#266](https://github.com/terrastruct/d2/pull/266)
#### Bugfixes 🔴

View file

@ -62,7 +62,7 @@ should_color() {
_COLOR=1
return 0
else
printf '$COLOR must be 0, 1, false or true but got %s' "$COLOR" >&2
printf '$COLOR must be 0, 1, false or true but got %s\n' "$COLOR" >&2
fi
fi
@ -219,8 +219,9 @@ header() {
}
bigheader() {
set -- "$(echo "$*" | sed "s/^/ * /")"
logp "/**
* $1
$*
**/"
}

View file

@ -14,6 +14,7 @@ import (
"github.com/spf13/pflag"
"oss.terrastruct.com/d2"
"oss.terrastruct.com/d2/d2layouts/d2sequence"
"oss.terrastruct.com/d2/d2plugin"
"oss.terrastruct.com/d2/d2renderers/d2svg"
"oss.terrastruct.com/d2/d2renderers/textmeasure"
@ -189,8 +190,13 @@ func compile(ctx context.Context, ms *xmain.State, isWatching bool, plugin d2plu
return nil, err
}
layout := plugin.Layout
// TODO: remove, this is just a feature flag to test sequence diagrams as we work on them
if os.Getenv("D2_SEQUENCE") == "1" {
layout = d2sequence.Layout
}
d, err := d2.Compile(ctx, string(input), &d2.CompileOptions{
Layout: plugin.Layout,
Layout: layout,
Ruler: ruler,
ThemeID: themeID,
})

View file

@ -266,6 +266,8 @@ func (c *compiler) compileKey(obj *d2graph.Object, m *d2ast.Map, mk *d2ast.Key)
}, unresolvedObj)
} else if obj.Parent == nil {
// Top level reserved key set on root.
c.compileAttributes(&obj.Attributes, mk)
c.applyScalar(&obj.Attributes, reserved, mk.Value.ScalarBox())
return
}
@ -377,7 +379,7 @@ func (c *compiler) applyScalar(attrs *d2graph.Attributes, reserved string, box d
if ok {
attrs.Language = fullTag
}
if attrs.Language == "markdown" {
if attrs.Language == "markdown" || attrs.Language == "latex" {
attrs.Shape.Value = d2target.ShapeText
} else {
attrs.Shape.Value = d2target.ShapeCode
@ -548,12 +550,13 @@ func (c *compiler) compileFlatKey(k *d2ast.KeyPath) ([]string, string, bool) {
// TODO add more, e.g. C, bash
var ShortToFullLanguageAliases = map[string]string{
"md": "markdown",
"js": "javascript",
"go": "golang",
"py": "python",
"rb": "ruby",
"ts": "typescript",
"md": "markdown",
"tex": "latex",
"js": "javascript",
"go": "golang",
"py": "python",
"rb": "ruby",
"ts": "typescript",
}
var FullToShortLanguageAliases map[string]string

View file

@ -1501,6 +1501,26 @@ dst.id <-> src.dst_id
}
},
},
{
name: "basic_sequence",
text: `x: {
shape: sequence_diagram
}
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
diff.AssertStringEq(t, "sequence_diagram", g.Objects[0].Attributes.Shape.Value)
},
},
{
name: "root_sequence",
text: `shape: sequence_diagram
`,
assertions: func(t *testing.T, g *d2graph.Graph) {
diff.AssertStringEq(t, "sequence_diagram", g.Root.Attributes.Shape.Value)
},
},
}
for _, tc := range testCases {

View file

@ -11,6 +11,7 @@ import (
"oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2parser"
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex"
"oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/d2themes"
@ -833,10 +834,18 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
var dims *d2target.TextDimensions
var innerLabelPadding = 5
if obj.Attributes.Shape.Value == d2target.ShapeText {
var err error
dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text())
if err != nil {
return err
if obj.Attributes.Language == "latex" {
width, height, err := d2latex.Measure(obj.Text().Text)
if err != nil {
return err
}
dims = d2target.NewTextDimensions(width, height)
} else {
var err error
dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text())
if err != nil {
return err
}
}
innerLabelPadding = 0
} else {

View file

@ -0,0 +1,9 @@
package d2sequence
// leaves at least 25 units of space on the left/right when computing the space required between actors
const HORIZONTAL_PAD = 50.
const MIN_ACTOR_DISTANCE = 200.
// min vertical distance between edges
const MIN_EDGE_DISTANCE = 100.

View file

@ -0,0 +1,107 @@
package d2sequence
import (
"context"
"fmt"
"math"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/go2"
"oss.terrastruct.com/d2/lib/label"
)
func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
edgeYStep := MIN_EDGE_DISTANCE
actorXStep := MIN_ACTOR_DISTANCE
maxActorHeight := 0.
actorRank := make(map[*d2graph.Object]int)
for rank, actor := range g.Objects {
actorRank[actor] = rank
}
for _, edge := range g.Edges {
edgeYStep = math.Max(edgeYStep, float64(edge.LabelDimensions.Height)+HORIZONTAL_PAD)
maxActorHeight = math.Max(maxActorHeight, edge.Src.Height+HORIZONTAL_PAD)
maxActorHeight = math.Max(maxActorHeight, edge.Dst.Height+HORIZONTAL_PAD)
// ensures that long labels, spanning over multiple actors, don't make for large gaps between actors
// by distributing the label length across the actors rank difference
rankDiff := math.Abs(float64(actorRank[edge.Src]) - float64(actorRank[edge.Dst]))
distributedLabelWidth := float64(edge.LabelDimensions.Width) / rankDiff
actorXStep = math.Max(actorXStep, distributedLabelWidth+HORIZONTAL_PAD)
}
placeActors(g.Objects, maxActorHeight, actorXStep)
routeEdges(g.Edges, maxActorHeight, edgeYStep)
addLifelineEdges(g, g.Objects, edgeYStep)
return nil
}
// placeActors places actors bottom aligned, side by side
func placeActors(actors []*d2graph.Object, maxHeight, xStep float64) {
x := 0.
for _, actors := range actors {
yOffset := maxHeight - actors.Height
actors.TopLeft = geo.NewPoint(x, yOffset)
x += actors.Width + xStep
actors.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
}
}
// routeEdges routes horizontal edges from Src to Dst
func routeEdges(edgesInOrder []*d2graph.Edge, startY, yStep float64) {
edgeY := startY + yStep // in case the first edge has a tall label
for _, edge := range edgesInOrder {
start := edge.Src.Center()
start.Y = edgeY
end := edge.Dst.Center()
end.Y = edgeY
edge.Route = []*geo.Point{start, end}
edgeY += yStep
if edge.Attributes.Label.Value != "" {
isLeftToRight := edge.Src.TopLeft.X < edge.Dst.TopLeft.X
if isLeftToRight {
edge.LabelPosition = go2.Pointer(string(label.OutsideTopCenter))
} else {
edge.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
}
}
}
}
// addLifelineEdges adds a new edge for each actor in the graph that represents the
// edge below the actor showing its lifespan
// ┌──────────────┐
// │ actor │
// └──────┬───────┘
// │
// │ lifeline
// │
// │
func addLifelineEdges(g *d2graph.Graph, actors []*d2graph.Object, yStep float64) {
endY := g.Edges[len(g.Edges)-1].Route[0].Y + yStep
for _, actor := range actors {
actorBottom := actor.Center()
actorBottom.Y = actor.TopLeft.Y + actor.Height
actorLifelineEnd := actor.Center()
actorLifelineEnd.Y = endY
g.Edges = append(g.Edges, &d2graph.Edge{
Attributes: d2graph.Attributes{
Style: d2graph.Style{
StrokeDash: &d2graph.Scalar{Value: "10"},
Stroke: actor.Attributes.Style.Stroke,
StrokeWidth: actor.Attributes.Style.StrokeWidth,
},
},
Src: actor,
SrcArrow: false,
Dst: &d2graph.Object{
ID: actor.ID + fmt.Sprintf("-lifeline-end-%d", go2.StringToIntHash(actor.ID+"-lifeline-end")),
},
DstArrow: false,
Route: []*geo.Point{actorBottom, actorLifelineEnd},
})
}
}

View file

@ -0,0 +1,112 @@
package d2sequence
import (
"context"
"testing"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/log"
)
func TestLayout(t *testing.T) {
g := d2graph.NewGraph(nil)
g.Objects = []*d2graph.Object{
{
ID: "Alice",
Box: geo.NewBox(nil, 100, 100),
},
{
ID: "Bob",
Box: geo.NewBox(nil, 30, 30),
},
}
g.Edges = []*d2graph.Edge{
{
Src: g.Objects[0],
Dst: g.Objects[1],
},
{
Src: g.Objects[1],
Dst: g.Objects[0],
},
{
Src: g.Objects[0],
Dst: g.Objects[1],
},
{
Src: g.Objects[1],
Dst: g.Objects[0],
},
}
nEdges := len(g.Edges)
ctx := log.WithTB(context.Background(), t, nil)
Layout(ctx, g)
// asserts that actors were placed in the expected x order and at y=0
actors := []*d2graph.Object{
g.Objects[0],
g.Objects[1],
}
for i := 1; i < len(actors); i++ {
if actors[i].TopLeft.X < actors[i-1].TopLeft.X {
t.Fatalf("expected actor[%d].TopLeft.X > actor[%d].TopLeft.X", i, i-1)
}
actorBottom := actors[i].TopLeft.Y + actors[i].Height
prevActorBottom := actors[i-1].TopLeft.Y + actors[i-1].Height
if actorBottom != prevActorBottom {
t.Fatalf("expected actor[%d] and actor[%d] to be at the same bottom y", i, i-1)
}
}
nExpectedEdges := nEdges + len(actors)
if len(g.Edges) != nExpectedEdges {
t.Fatalf("expected %d edges, got %d", nExpectedEdges, len(g.Edges))
}
// assert that edges were placed in y order and have the endpoints at their actors
// uses `nEdges` because Layout creates some vertical edges to represent the actor lifeline
for i := 0; i < nEdges; i++ {
edge := g.Edges[i]
if len(edge.Route) != 2 {
t.Fatalf("expected edge[%d] to have only 2 points", i)
}
if edge.Route[0].Y != edge.Route[1].Y {
t.Fatalf("expected edge[%d] to be a horizontal line", i)
}
if edge.Route[0].X != edge.Src.Center().X {
t.Fatalf("expected edge[%d] source endpoint to be at the middle of the source actor", i)
}
if edge.Route[1].X != edge.Dst.Center().X {
t.Fatalf("expected edge[%d] target endpoint to be at the middle of the target actor", i)
}
if i > 0 {
prevEdge := g.Edges[i-1]
if edge.Route[0].Y < prevEdge.Route[0].Y {
t.Fatalf("expected edge[%d].TopLeft.Y > edge[%d].TopLeft.Y", i, i-1)
}
}
}
lastSequenceEdge := g.Edges[nEdges-1]
for i := nEdges; i < nExpectedEdges; i++ {
edge := g.Edges[i]
if len(edge.Route) != 2 {
t.Fatalf("expected edge[%d] to have only 2 points", i)
}
if edge.Route[0].X != edge.Route[1].X {
t.Fatalf("expected edge[%d] to be a vertical line", i)
}
if edge.Route[0].X != edge.Src.Center().X {
t.Fatalf("expected edge[%d] x to be at the actor center", i)
}
if edge.Route[0].Y != edge.Src.Height+edge.Src.TopLeft.Y {
t.Fatalf("expected edge[%d] to start at the bottom of the source actor", i)
}
if edge.Route[1].Y < lastSequenceEdge.Route[0].Y {
t.Fatalf("expected edge[%d] to end after the last sequence edge", i)
}
}
}

View file

@ -158,17 +158,17 @@ meow.(x -> y -> z)[3].shape: "all hail corn"
{
name: "errs",
text: `
--: meow]]]
meow][: ok
ok: "dmsadmakls" dsamkldkmsa
s.shape: orochimaru
x.shape: dasdasdas
--: meow]]] ` + `
meow][: ok ` + `
ok: "dmsadmakls" dsamkldkmsa ` + `
` + `
s.shape: orochimaru ` + `
x.shape: dasdasdas ` + `
wow:
:
: ` + `
` + `
[]
{}
@ -214,7 +214,7 @@ meow
{
name: "trailing_whitespace",
text: `
s.shape: orochimaru
s.shape: orochimaru ` + `
`,
},
{
@ -334,16 +334,16 @@ a: | hello |
name: "block_trailing_space",
text: `
x: |
meow
meow ` + `
|
""" hello
""" hello ` + `
"""
`,
},
{
name: "block_edge_case",
text: `
x: | meow
x: | meow ` + `
hello
yes
|

View file

@ -0,0 +1,83 @@
//go:build cgo
package d2latex
import (
_ "embed"
"fmt"
"math"
"regexp"
"strconv"
"oss.terrastruct.com/xdefer"
v8 "rogchap.com/v8go"
)
var pxPerEx = 8
//go:embed polyfills.js
var polyfillsJS string
//go:embed setup.js
var setupJS string
//go:embed mathjax.js
var mathjaxJS string
// Matches this
// <svg style="background: white;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="563" height="326" viewBox="-100 -100 563 326"><style type="text/css">
var svgRe = regexp.MustCompile(`<svg[^>]+width="([0-9\.]+)ex" height="([0-9\.]+)ex"[^>]+>`)
func Render(s string) (_ string, err error) {
defer xdefer.Errorf(&err, "latex failed to parse")
v8ctx := v8.NewContext()
if _, err := v8ctx.RunScript(polyfillsJS, "polyfills.js"); err != nil {
return "", err
}
if _, err := v8ctx.RunScript(mathjaxJS, "mathjax.js"); err != nil {
return "", err
}
if _, err := v8ctx.RunScript(setupJS, "setup.js"); err != nil {
return "", err
}
val, err := v8ctx.RunScript(fmt.Sprintf(`adaptor.innerHTML(html.convert(`+"`"+"%s`"+`, {
em: %d,
ex: %d,
}))`, s, pxPerEx*2, pxPerEx), "value.js")
if err != nil {
return "", err
}
return val.String(), nil
}
func Measure(s string) (width, height int, err error) {
defer xdefer.Errorf(&err, "latex failed to parse")
svg, err := Render(s)
if err != nil {
return 0, 0, err
}
dims := svgRe.FindAllStringSubmatch(svg, -1)
if len(dims) != 1 || len(dims[0]) != 3 {
return 0, 0, fmt.Errorf("svg parsing failed for latex: %v", svg)
}
wEx := dims[0][1]
hEx := dims[0][2]
wf, err := strconv.ParseFloat(wEx, 64)
if err != nil {
return 0, 0, fmt.Errorf("svg parsing failed for latex: %v", svg)
}
hf, err := strconv.ParseFloat(hEx, 64)
if err != nil {
return 0, 0, fmt.Errorf("svg parsing failed for latex: %v", svg)
}
return int(math.Ceil(wf * float64(pxPerEx))), int(math.Ceil(hf * float64(pxPerEx))), nil
}

View file

@ -0,0 +1,13 @@
//go:build !cgo
package d2latex
import "errors"
func Render(s string) (string, error) {
return "", errors.New("not found in build")
}
func Measure(s string) (width, height int, _ error) {
return 0, 0, errors.New("not found in build")
}

View file

@ -0,0 +1,33 @@
package d2latex
import (
"encoding/xml"
"testing"
)
func TestRender(t *testing.T) {
txts := []string{
`a + b = c`,
`\\frac{1}{2}`,
`a + b
= c
`,
}
for _, txt := range txts {
svg, err := Render(txt)
if err != nil {
t.Fatal(err)
}
var xmlParsed interface{}
if err := xml.Unmarshal([]byte(svg), &xmlParsed); err != nil {
t.Fatalf("invalid SVG: %v", err)
}
}
}
func TestRenderError(t *testing.T) {
_, err := Render(`\frac{1}{2}`)
if err == nil {
t.Fatal("expected to error on invalid latex syntax")
}
}

36
d2renderers/d2latex/mathjax.js vendored Normal file

File diff suppressed because one or more lines are too long

68
d2renderers/d2latex/polyfills.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,6 @@
const adaptor = MathJax._.adaptors.liteAdaptor.liteAdaptor();
MathJax._.handlers.html_ts.RegisterHTMLHandler(adaptor)
const html = MathJax._.mathjax.mathjax.document('', {
InputJax: new MathJax._.input.tex_ts.TeX({ packages: ['base', 'mathtools', 'amscd', 'braket', 'cancel', 'cases', 'color', 'gensymb', 'mhchem', 'physics'] }),
OutputJax: new MathJax._.output.svg_ts.SVG(),
});

View file

@ -20,6 +20,7 @@ import (
"github.com/alecthomas/chroma/styles"
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex"
"oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/color"
@ -701,17 +702,27 @@ func drawShape(writer io.Writer, targetShape d2target.Shape) error {
}
fmt.Fprintf(writer, "</g></g>")
case d2target.ShapeText:
render, err := textmeasure.RenderMarkdown(targetShape.Label)
if err != nil {
return err
if targetShape.Language == "latex" {
render, err := d2latex.Render(targetShape.Label)
if err != nil {
return err
}
fmt.Fprintf(writer, `<g transform="translate(%f %f)" style="opacity:%f">`, box.TopLeft.X, box.TopLeft.Y, targetShape.Opacity)
fmt.Fprintf(writer, render)
fmt.Fprintf(writer, "</g>")
} else {
render, err := textmeasure.RenderMarkdown(targetShape.Label)
if err != nil {
return err
}
fmt.Fprintf(writer, `<g><foreignObject requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" x="%f" y="%f" width="%d" height="%d">`,
box.TopLeft.X, box.TopLeft.Y, targetShape.Width, targetShape.Height,
)
// we need the self closing form in this svg/xhtml context
render = strings.ReplaceAll(render, "<hr>", "<hr />")
fmt.Fprintf(writer, `<div xmlns="http://www.w3.org/1999/xhtml" class="md">%v</div>`, render)
fmt.Fprint(writer, `</foreignObject></g>`)
}
fmt.Fprintf(writer, `<g><foreignObject requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" x="%f" y="%f" width="%d" height="%d">`,
box.TopLeft.X, box.TopLeft.Y, targetShape.Width, targetShape.Height,
)
// we need the self closing form in this svg/xhtml context
render = strings.ReplaceAll(render, "<hr>", "<hr />")
fmt.Fprintf(writer, `<div xmlns="http://www.w3.org/1999/xhtml" class="md">%v</div>`, render)
fmt.Fprint(writer, `</foreignObject></g>`)
default:
fontColor := "black"
if targetShape.Color != "" {

View file

@ -255,28 +255,29 @@ func NewPoint(x, y int) Point {
}
const (
ShapeRectangle = "rectangle"
ShapeSquare = "square"
ShapePage = "page"
ShapeParallelogram = "parallelogram"
ShapeDocument = "document"
ShapeCylinder = "cylinder"
ShapeQueue = "queue"
ShapePackage = "package"
ShapeStep = "step"
ShapeCallout = "callout"
ShapeStoredData = "stored_data"
ShapePerson = "person"
ShapeDiamond = "diamond"
ShapeOval = "oval"
ShapeCircle = "circle"
ShapeHexagon = "hexagon"
ShapeCloud = "cloud"
ShapeText = "text"
ShapeCode = "code"
ShapeClass = "class"
ShapeSQLTable = "sql_table"
ShapeImage = "image"
ShapeRectangle = "rectangle"
ShapeSquare = "square"
ShapePage = "page"
ShapeParallelogram = "parallelogram"
ShapeDocument = "document"
ShapeCylinder = "cylinder"
ShapeQueue = "queue"
ShapePackage = "package"
ShapeStep = "step"
ShapeCallout = "callout"
ShapeStoredData = "stored_data"
ShapePerson = "person"
ShapeDiamond = "diamond"
ShapeOval = "oval"
ShapeCircle = "circle"
ShapeHexagon = "hexagon"
ShapeCloud = "cloud"
ShapeText = "text"
ShapeCode = "code"
ShapeClass = "class"
ShapeSQLTable = "sql_table"
ShapeImage = "image"
ShapeSequenceDiagram = "sequence_diagram"
)
var Shapes = []string{
@ -302,6 +303,7 @@ var Shapes = []string{
ShapeClass,
ShapeSQLTable,
ShapeImage,
ShapeSequenceDiagram,
}
func IsShape(s string) bool {
@ -345,29 +347,30 @@ func (text MText) GetColor(theme *d2themes.Theme, isItalic bool) string {
}
var DSL_SHAPE_TO_SHAPE_TYPE = map[string]string{
"": shape.SQUARE_TYPE,
ShapeRectangle: shape.SQUARE_TYPE,
ShapeSquare: shape.REAL_SQUARE_TYPE,
ShapePage: shape.PAGE_TYPE,
ShapeParallelogram: shape.PARALLELOGRAM_TYPE,
ShapeDocument: shape.DOCUMENT_TYPE,
ShapeCylinder: shape.CYLINDER_TYPE,
ShapeQueue: shape.QUEUE_TYPE,
ShapePackage: shape.PACKAGE_TYPE,
ShapeStep: shape.STEP_TYPE,
ShapeCallout: shape.CALLOUT_TYPE,
ShapeStoredData: shape.STORED_DATA_TYPE,
ShapePerson: shape.PERSON_TYPE,
ShapeDiamond: shape.DIAMOND_TYPE,
ShapeOval: shape.OVAL_TYPE,
ShapeCircle: shape.CIRCLE_TYPE,
ShapeHexagon: shape.HEXAGON_TYPE,
ShapeCloud: shape.CLOUD_TYPE,
ShapeText: shape.TEXT_TYPE,
ShapeCode: shape.CODE_TYPE,
ShapeClass: shape.CLASS_TYPE,
ShapeSQLTable: shape.TABLE_TYPE,
ShapeImage: shape.IMAGE_TYPE,
"": shape.SQUARE_TYPE,
ShapeRectangle: shape.SQUARE_TYPE,
ShapeSquare: shape.REAL_SQUARE_TYPE,
ShapePage: shape.PAGE_TYPE,
ShapeParallelogram: shape.PARALLELOGRAM_TYPE,
ShapeDocument: shape.DOCUMENT_TYPE,
ShapeCylinder: shape.CYLINDER_TYPE,
ShapeQueue: shape.QUEUE_TYPE,
ShapePackage: shape.PACKAGE_TYPE,
ShapeStep: shape.STEP_TYPE,
ShapeCallout: shape.CALLOUT_TYPE,
ShapeStoredData: shape.STORED_DATA_TYPE,
ShapePerson: shape.PERSON_TYPE,
ShapeDiamond: shape.DIAMOND_TYPE,
ShapeOval: shape.OVAL_TYPE,
ShapeCircle: shape.CIRCLE_TYPE,
ShapeHexagon: shape.HEXAGON_TYPE,
ShapeCloud: shape.CLOUD_TYPE,
ShapeText: shape.TEXT_TYPE,
ShapeCode: shape.CODE_TYPE,
ShapeClass: shape.CLASS_TYPE,
ShapeSQLTable: shape.TABLE_TYPE,
ShapeImage: shape.IMAGE_TYPE,
ShapeSequenceDiagram: shape.SQUARE_TYPE,
}
var SHAPE_TYPE_TO_DSL_SHAPE map[string]string

View file

@ -30,6 +30,13 @@ The simplified D2 flow at a package level looks like:
## Logistics
- **Important**: Contributions to D2 require a CLA. We will never relicense D2, but
Hashicorp has a good explanation of why a CLA has benefits for all parties (contributor,
community, us): [https://www.hashicorp.com/cla](https://www.hashicorp.com/cla). Please
email cla@terrastruct.com with your name and Github username stating that you agree to
[Terrastruct's
CLA](https://terrastruct-site-assets.s3.us-west-1.amazonaws.com/documents/terrastruct_cla.pdf).
You only have to do this the first time you contribute.
- D2 uses Issues as TODOs. No auto-closing on staleness.
- Branch off `master`.
- Prefix pull request titles with a short descriptor of the domain, e.g. `d2renderer: Add

View file

@ -932,7 +932,7 @@ x -> y: {
script: `
markdown: {
md: |md
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
Lorem ipsum dolor sit amet, consectetur adipiscing elit, ` + `
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
}
@ -961,6 +961,31 @@ beta: {
alpha -> beta: gamma {
style.font-color: green
}
`,
},
{
name: "latex",
script: `a: |latex
\\Huge{\\frac{\\alpha g^2}{\\omega^5} e^{[ -0.74\\bigl\\{\\frac{\\omega U_\\omega 19.5}{g}\\bigr\\}^{\\!-4}\\,]}}
|
b: |latex
e = mc^2
|
z: |latex
gibberish\\; math:\\sum_{i=0}^\\infty i^2
|
z -> a
z -> b
a -> c
b -> c
sugar -> c
c: mixed together
c -> solution: we get
`,
},
}

514
e2etests/testdata/stable/latex/dagre/board.exp.json generated vendored Normal file
View file

@ -0,0 +1,514 @@
{
"name": "",
"shapes": [
{
"id": "a",
"type": "text",
"pos": {
"x": 0,
"y": 164
},
"width": 382,
"height": 101,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "\\\\Huge{\\\\frac{\\\\alpha g^2}{\\\\omega^5} e^{[ -0.74\\\\bigl\\\\{\\\\frac{\\\\omega U_\\\\omega 19.5}{g}\\\\bigr\\\\}^{\\\\!-4}\\\\,]}}",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 382,
"labelHeight": 101
},
{
"id": "b",
"type": "text",
"pos": {
"x": 442,
"y": 205
},
"width": 65,
"height": 18,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "e = mc^2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 65,
"labelHeight": 18
},
{
"id": "z",
"type": "text",
"pos": {
"x": 243,
"y": 0
},
"width": 179,
"height": 51,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "gibberish\\\\; math:\\\\sum_{i=0}^\\\\infty i^2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 179,
"labelHeight": 51
},
{
"id": "c",
"type": "",
"pos": {
"x": 368,
"y": 377
},
"width": 214,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "mixed together",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 114,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
},
{
"id": "sugar",
"type": "",
"pos": {
"x": 567,
"y": 151
},
"width": 146,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "sugar",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 46,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
},
{
"id": "solution",
"type": "",
"pos": {
"x": 393,
"y": 603
},
"width": 164,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "solution",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 64,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
}
],
"connections": [
{
"id": "(z -> a)[0]",
"src": "z",
"srcArrow": "none",
"srcLabel": "",
"dst": "a",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 284.87417218543044,
"y": 51
},
{
"x": 209.7748344370861,
"y": 91
},
{
"x": 191,
"y": 113.5
},
{
"x": 191,
"y": 163.5
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(z -> b)[0]",
"src": "z",
"srcArrow": "none",
"srcLabel": "",
"dst": "b",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 380.62582781456956,
"y": 51
},
{
"x": 455.7251655629139,
"y": 91
},
{
"x": 474.5,
"y": 121.8
},
{
"x": 474.5,
"y": 205
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(a -> c)[0]",
"src": "a",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 191,
"y": 265.5
},
{
"x": 191,
"y": 314.7
},
{
"x": 226.3,
"y": 341.0701940035273
},
{
"x": 367.5,
"y": 397.35097001763666
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(b -> c)[0]",
"src": "b",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 474.5,
"y": 223
},
{
"x": 474.5,
"y": 306.2
},
{
"x": 474.5,
"y": 337
},
{
"x": 474.5,
"y": 377
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(sugar -> c)[0]",
"src": "sugar",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 640,
"y": 277
},
{
"x": 640,
"y": 317
},
{
"x": 625.4,
"y": 337
},
{
"x": 567,
"y": 377
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(c -> solution)[0]",
"src": "c",
"srcArrow": "none",
"srcLabel": "",
"dst": "solution",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "we get",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 44,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 474.5,
"y": 503
},
{
"x": 474.5,
"y": 543
},
{
"x": 474.5,
"y": 563
},
{
"x": 474.5,
"y": 603
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 506 KiB

484
e2etests/testdata/stable/latex/elk/board.exp.json generated vendored Normal file
View file

@ -0,0 +1,484 @@
{
"name": "",
"shapes": [
{
"id": "a",
"type": "text",
"pos": {
"x": 291,
"y": 196
},
"width": 382,
"height": 101,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "\\\\Huge{\\\\frac{\\\\alpha g^2}{\\\\omega^5} e^{[ -0.74\\\\bigl\\\\{\\\\frac{\\\\omega U_\\\\omega 19.5}{g}\\\\bigr\\\\}^{\\\\!-4}\\\\,]}}",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 382,
"labelHeight": 101
},
{
"id": "b",
"type": "text",
"pos": {
"x": 450,
"y": 158
},
"width": 65,
"height": 18,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "e = mc^2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 65,
"labelHeight": 18
},
{
"id": "z",
"type": "text",
"pos": {
"x": 12,
"y": 150
},
"width": 179,
"height": 51,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "gibberish\\\\; math:\\\\sum_{i=0}^\\\\infty i^2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 179,
"labelHeight": 51
},
{
"id": "c",
"type": "",
"pos": {
"x": 773,
"y": 104
},
"width": 214,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "mixed together",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 114,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
},
{
"id": "sugar",
"type": "",
"pos": {
"x": 527,
"y": 12
},
"width": 146,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "sugar",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 46,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
},
{
"id": "solution",
"type": "",
"pos": {
"x": 1231,
"y": 104
},
"width": 164,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "solution",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 64,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
}
],
"connections": [
{
"id": "(z -> a)[0]",
"src": "z",
"srcArrow": "none",
"srcLabel": "",
"dst": "a",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 191,
"y": 184
},
{
"x": 241,
"y": 184
},
{
"x": 241,
"y": 246.5
},
{
"x": 291,
"y": 246.5
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(z -> b)[0]",
"src": "z",
"srcArrow": "none",
"srcLabel": "",
"dst": "b",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 191,
"y": 167
},
{
"x": 449.5,
"y": 167
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(a -> c)[0]",
"src": "a",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 673,
"y": 246.5
},
{
"x": 723,
"y": 246.5
},
{
"x": 723,
"y": 198.5
},
{
"x": 773,
"y": 198.5
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(b -> c)[0]",
"src": "b",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 514.5,
"y": 167
},
{
"x": 773,
"y": 167
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(sugar -> c)[0]",
"src": "sugar",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 673,
"y": 75
},
{
"x": 723,
"y": 75
},
{
"x": 723,
"y": 135.5
},
{
"x": 773,
"y": 135.5
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(c -> solution)[0]",
"src": "c",
"srcArrow": "none",
"srcLabel": "",
"dst": "solution",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "we get",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 44,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 987,
"y": 167
},
{
"x": 1231,
"y": 167
}
],
"animated": false,
"tooltip": "",
"icon": null
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 506 KiB

View file

@ -68,7 +68,7 @@ should_color() {
_COLOR=1
return 0
else
printf '$COLOR must be 0, 1, false or true but got %s' "$COLOR" >&2
printf '$COLOR must be 0, 1, false or true but got %s\n' "$COLOR" >&2
fi
fi
@ -225,8 +225,9 @@ header() {
}
bigheader() {
set -- "$(echo "$*" | sed "s/^/ * /")"
logp "/**
* $1
$*
**/"
}
@ -478,7 +479,7 @@ help() {
fi
cat <<EOF
usage: $arg0 [--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix /usr/local]
usage: $arg0 [-d|--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix /usr/local]
[--tala latest] [--force] [--uninstall]
install.sh automates the installation of D2 onto your system. It currently only supports
@ -490,7 +491,7 @@ If you pass --edge, it will clone the source, build a release and install from i
Flags:
--dry-run
-d, --dry-run
Pass to have install.sh show the install method and flags that will be used to install
without executing them. Very useful to understand what changes it will make to your system.
@ -565,7 +566,7 @@ main() {
help
return 0
;;
dry-run)
d|dry-run)
flag_noarg && shift "$FLAGSHIFT"
DRY_RUN=1
;;
@ -652,12 +653,16 @@ main() {
if [ -n "${UNINSTALL-}" ]; then
uninstall
if [ -n "${DRY_RUN-}" ]; then
log "Rerun without --dry-run to execute printed commands and perform uninstall."
FGCOLOR=3 bigheader "***********
Rerun without --dry-run to execute printed commands and perform install.
***********"
fi
else
install
if [ -n "${DRY_RUN-}" ]; then
log "Rerun without --dry-run to execute printed commands and perform install."
FGCOLOR=3 bigheader "***********
Rerun without --dry-run to execute printed commands and perform install.
***********"
fi
fi
}

133
testdata/d2compiler/TestCompile/basic_sequence.exp.json generated vendored Normal file
View file

@ -0,0 +1,133 @@
{
"graph": {
"ast": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,0:0:0-3:0:33",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,0:0:0-2:1:32",
"key": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,0:0:0-0:1:1",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,0:0:0-0:1:1",
"value": [
{
"string": "x",
"raw_string": "x"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,0:3:3-2:0:31",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,1:2:7-1:25:30",
"key": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,1:2:7-1:7:12",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,1:2:7-1:7:12",
"value": [
{
"string": "shape",
"raw_string": "shape"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,1:9:14-1:25:30",
"value": [
{
"string": "sequence_diagram",
"raw_string": "sequence_diagram"
}
]
}
}
}
}
]
}
}
}
}
]
},
"root": {
"id": "",
"id_val": "",
"label_dimensions": {
"width": 0,
"height": 0
},
"attributes": {
"label": {
"value": ""
},
"style": {},
"near_key": null,
"shape": {
"value": ""
}
}
},
"edges": null,
"objects": [
{
"id": "x",
"id_val": "x",
"label_dimensions": {
"width": 0,
"height": 0
},
"references": [
{
"key": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,0:0:0-0:1:1",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile/basic_sequence.d2,0:0:0-0:1:1",
"value": [
{
"string": "x",
"raw_string": "x"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": 0
}
],
"attributes": {
"label": {
"value": "x"
},
"style": {},
"near_key": null,
"shape": {
"value": "sequence_diagram"
}
}
}
]
},
"err": null
}

63
testdata/d2compiler/TestCompile/root_sequence.exp.json generated vendored Normal file
View file

@ -0,0 +1,63 @@
{
"graph": {
"ast": {
"range": "d2/testdata/d2compiler/TestCompile/root_sequence.d2,0:0:0-1:0:24",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile/root_sequence.d2,0:0:0-0:23:23",
"key": {
"range": "d2/testdata/d2compiler/TestCompile/root_sequence.d2,0:0:0-0:5:5",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile/root_sequence.d2,0:0:0-0:5:5",
"value": [
{
"string": "shape",
"raw_string": "shape"
}
]
}
}
]
},
"primary": {},
"value": {
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile/root_sequence.d2,0:7:7-0:23:23",
"value": [
{
"string": "sequence_diagram",
"raw_string": "sequence_diagram"
}
]
}
}
}
}
]
},
"root": {
"id": "",
"id_val": "",
"label_dimensions": {
"width": 0,
"height": 0
},
"attributes": {
"label": {
"value": ""
},
"style": {},
"near_key": null,
"shape": {
"value": "sequence_diagram"
}
}
},
"edges": null,
"objects": null
},
"err": null
}