Merge pull request #563 from alixander/layout-configs
Pass layout configs
This commit is contained in:
commit
8708bceef0
21 changed files with 398 additions and 90 deletions
|
|
@ -82,7 +82,7 @@ Print version information and exit.
|
||||||
.It Ar layout
|
.It Ar layout
|
||||||
Lists available layout engine options with short help.
|
Lists available layout engine options with short help.
|
||||||
.It Ar layout Op Ar name
|
.It Ar layout Op Ar name
|
||||||
Display long help for a particular layout engine.
|
Display long help for a particular layout engine, including its configuration options.
|
||||||
.It Ar fmt Ar file.d2
|
.It Ar fmt Ar file.d2
|
||||||
Format
|
Format
|
||||||
.Ar file.d2
|
.Ar file.d2
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,5 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
xmain.Main(d2plugin.Serve(d2plugin.DagrePlugin))
|
xmain.Main(d2plugin.Serve(&d2plugin.DagrePlugin))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ func test(t *testing.T, textPath, text string) {
|
||||||
err = g.SetDimensions(nil, ruler, nil)
|
err = g.SetDimensions(nil, ruler, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
err = d2dagrelayout.Layout(ctx, g)
|
err = d2dagrelayout.DefaultLayout(ctx, g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,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.Layout)
|
err = d2sequence.Layout(ctx, g, d2dagrelayout.DefaultLayout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,16 @@ var setupJS string
|
||||||
//go:embed dagre.js
|
//go:embed dagre.js
|
||||||
var dagreJS string
|
var dagreJS string
|
||||||
|
|
||||||
|
type ConfigurableOpts struct {
|
||||||
|
NodeSep int `json:"nodesep"`
|
||||||
|
EdgeSep int `json:"edgesep"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultOpts = ConfigurableOpts{
|
||||||
|
NodeSep: 60,
|
||||||
|
EdgeSep: 40,
|
||||||
|
}
|
||||||
|
|
||||||
type DagreNode struct {
|
type DagreNode struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
X float64 `json:"x"`
|
X float64 `json:"x"`
|
||||||
|
|
@ -42,16 +52,23 @@ type DagreEdge struct {
|
||||||
Points []*geo.Point `json:"points"`
|
Points []*geo.Point `json:"points"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type dagreGraphAttrs struct {
|
type dagreOpts struct {
|
||||||
// for a top to bottom graph: ranksep is y spacing, nodesep is x spacing, edgesep is x spacing
|
// for a top to bottom graph: ranksep is y spacing, nodesep is x spacing, edgesep is x spacing
|
||||||
ranksep int
|
ranksep int
|
||||||
edgesep int
|
|
||||||
nodesep int
|
|
||||||
// graph direction: tb (top to bottom)| bt | lr | rl
|
// graph direction: tb (top to bottom)| bt | lr | rl
|
||||||
rankdir string
|
rankdir string
|
||||||
|
|
||||||
|
ConfigurableOpts
|
||||||
}
|
}
|
||||||
|
|
||||||
func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
func DefaultLayout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||||
|
return Layout(ctx, g, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &DefaultOpts
|
||||||
|
}
|
||||||
defer xdefer.Errorf(&err, "failed to dagre layout")
|
defer xdefer.Errorf(&err, "failed to dagre layout")
|
||||||
|
|
||||||
debugJS := false
|
debugJS := false
|
||||||
|
|
@ -63,9 +80,11 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rootAttrs := dagreGraphAttrs{
|
rootAttrs := dagreOpts{
|
||||||
edgesep: 40,
|
ConfigurableOpts: ConfigurableOpts{
|
||||||
nodesep: 60,
|
EdgeSep: opts.EdgeSep,
|
||||||
|
NodeSep: opts.NodeSep,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
isHorizontal := false
|
isHorizontal := false
|
||||||
switch g.Root.Attributes.Direction.Value {
|
switch g.Root.Attributes.Direction.Value {
|
||||||
|
|
@ -266,7 +285,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setGraphAttrs(attrs dagreGraphAttrs) string {
|
func setGraphAttrs(attrs dagreOpts) string {
|
||||||
return fmt.Sprintf(`g.setGraph({
|
return fmt.Sprintf(`g.setGraph({
|
||||||
ranksep: %d,
|
ranksep: %d,
|
||||||
edgesep: %d,
|
edgesep: %d,
|
||||||
|
|
@ -275,8 +294,8 @@ func setGraphAttrs(attrs dagreGraphAttrs) string {
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
attrs.ranksep,
|
attrs.ranksep,
|
||||||
attrs.edgesep,
|
attrs.ConfigurableOpts.EdgeSep,
|
||||||
attrs.nodesep,
|
attrs.ConfigurableOpts.NodeSep,
|
||||||
attrs.rankdir,
|
attrs.rankdir,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,23 +31,23 @@ var elkJS string
|
||||||
var setupJS string
|
var setupJS string
|
||||||
|
|
||||||
type ELKNode struct {
|
type ELKNode struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
X float64 `json:"x"`
|
X float64 `json:"x"`
|
||||||
Y float64 `json:"y"`
|
Y float64 `json:"y"`
|
||||||
Width float64 `json:"width"`
|
Width float64 `json:"width"`
|
||||||
Height float64 `json:"height"`
|
Height float64 `json:"height"`
|
||||||
Children []*ELKNode `json:"children,omitempty"`
|
Children []*ELKNode `json:"children,omitempty"`
|
||||||
Labels []*ELKLabel `json:"labels,omitempty"`
|
Labels []*ELKLabel `json:"labels,omitempty"`
|
||||||
LayoutOptions *ELKLayoutOptions `json:"layoutOptions,omitempty"`
|
LayoutOptions *elkOpts `json:"layoutOptions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ELKLabel struct {
|
type ELKLabel struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
X float64 `json:"x"`
|
X float64 `json:"x"`
|
||||||
Y float64 `json:"y"`
|
Y float64 `json:"y"`
|
||||||
Width float64 `json:"width"`
|
Width float64 `json:"width"`
|
||||||
Height float64 `json:"height"`
|
Height float64 `json:"height"`
|
||||||
LayoutOptions *ELKLayoutOptions `json:"layoutOptions,omitempty"`
|
LayoutOptions *elkOpts `json:"layoutOptions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ELKPoint struct {
|
type ELKPoint struct {
|
||||||
|
|
@ -71,26 +71,46 @@ type ELKEdge struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ELKGraph struct {
|
type ELKGraph struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
LayoutOptions *ELKLayoutOptions `json:"layoutOptions"`
|
LayoutOptions *elkOpts `json:"layoutOptions"`
|
||||||
Children []*ELKNode `json:"children,omitempty"`
|
Children []*ELKNode `json:"children,omitempty"`
|
||||||
Edges []*ELKEdge `json:"edges,omitempty"`
|
Edges []*ELKEdge `json:"edges,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ELKLayoutOptions struct {
|
type ConfigurableOpts struct {
|
||||||
Algorithm string `json:"elk.algorithm,omitempty"`
|
Algorithm string `json:"elk.algorithm,omitempty"`
|
||||||
HierarchyHandling string `json:"elk.hierarchyHandling,omitempty"`
|
NodeSpacing int `json:"spacing.nodeNodeBetweenLayers,omitempty"`
|
||||||
NodeSpacing float64 `json:"spacing.nodeNodeBetweenLayers,omitempty"`
|
Padding string `json:"elk.padding,omitempty"`
|
||||||
Padding string `json:"elk.padding,omitempty"`
|
EdgeNodeSpacing int `json:"spacing.edgeNodeBetweenLayers,omitempty"`
|
||||||
EdgeNodeSpacing float64 `json:"spacing.edgeNodeBetweenLayers,omitempty"`
|
SelfLoopSpacing int `json:"elk.spacing.nodeSelfLoop"`
|
||||||
Direction string `json:"elk.direction"`
|
|
||||||
SelfLoopSpacing float64 `json:"elk.spacing.nodeSelfLoop"`
|
|
||||||
InlineEdgeLabels bool `json:"elk.edgeLabels.inline,omitempty"`
|
|
||||||
ConsiderModelOrder string `json:"elk.layered.considerModelOrder.strategy,omitempty"`
|
|
||||||
ForceNodeModelOrder bool `json:"elk.layered.crossingMinimization.forceNodeModelOrder,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
var DefaultOpts = ConfigurableOpts{
|
||||||
|
Algorithm: "layered",
|
||||||
|
NodeSpacing: 100.0,
|
||||||
|
Padding: "[top=75,left=75,bottom=75,right=75]",
|
||||||
|
EdgeNodeSpacing: 50.0,
|
||||||
|
SelfLoopSpacing: 50.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
type elkOpts struct {
|
||||||
|
Direction string `json:"elk.direction"`
|
||||||
|
HierarchyHandling string `json:"elk.hierarchyHandling,omitempty"`
|
||||||
|
InlineEdgeLabels bool `json:"elk.edgeLabels.inline,omitempty"`
|
||||||
|
ForceNodeModelOrder bool `json:"elk.layered.crossingMinimization.forceNodeModelOrder,omitempty"`
|
||||||
|
ConsiderModelOrder string `json:"elk.layered.considerModelOrder.strategy,omitempty"`
|
||||||
|
|
||||||
|
ConfigurableOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultLayout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||||
|
return Layout(ctx, g, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &DefaultOpts
|
||||||
|
}
|
||||||
defer xdefer.Errorf(&err, "failed to ELK layout")
|
defer xdefer.Errorf(&err, "failed to ELK layout")
|
||||||
|
|
||||||
vm := goja.New()
|
vm := goja.New()
|
||||||
|
|
@ -109,13 +129,15 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||||
|
|
||||||
elkGraph := &ELKGraph{
|
elkGraph := &ELKGraph{
|
||||||
ID: "root",
|
ID: "root",
|
||||||
LayoutOptions: &ELKLayoutOptions{
|
LayoutOptions: &elkOpts{
|
||||||
Algorithm: "layered",
|
|
||||||
HierarchyHandling: "INCLUDE_CHILDREN",
|
HierarchyHandling: "INCLUDE_CHILDREN",
|
||||||
NodeSpacing: 100.0,
|
|
||||||
EdgeNodeSpacing: 50.0,
|
|
||||||
SelfLoopSpacing: 50.0,
|
|
||||||
ConsiderModelOrder: "NODES_AND_EDGES",
|
ConsiderModelOrder: "NODES_AND_EDGES",
|
||||||
|
ConfigurableOpts: ConfigurableOpts{
|
||||||
|
Algorithm: opts.Algorithm,
|
||||||
|
NodeSpacing: opts.NodeSpacing,
|
||||||
|
EdgeNodeSpacing: opts.EdgeNodeSpacing,
|
||||||
|
SelfLoopSpacing: opts.SelfLoopSpacing,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
switch g.Root.Attributes.Direction.Value {
|
switch g.Root.Attributes.Direction.Value {
|
||||||
|
|
@ -160,9 +182,11 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(obj.ChildrenArray) > 0 {
|
if len(obj.ChildrenArray) > 0 {
|
||||||
n.LayoutOptions = &ELKLayoutOptions{
|
n.LayoutOptions = &elkOpts{
|
||||||
Padding: "[top=75,left=75,bottom=75,right=75]",
|
|
||||||
ForceNodeModelOrder: true,
|
ForceNodeModelOrder: true,
|
||||||
|
ConfigurableOpts: ConfigurableOpts{
|
||||||
|
Padding: opts.Padding,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,7 +217,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
|
||||||
Text: edge.Attributes.Label.Value,
|
Text: edge.Attributes.Label.Value,
|
||||||
Width: float64(edge.LabelDimensions.Width),
|
Width: float64(edge.LabelDimensions.Width),
|
||||||
Height: float64(edge.LabelDimensions.Height),
|
Height: float64(edge.LabelDimensions.Height),
|
||||||
LayoutOptions: &ELKLayoutOptions{
|
LayoutOptions: &elkOpts{
|
||||||
InlineEdgeLabels: true,
|
InlineEdgeLabels: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
package d2lib
|
|
||||||
|
|
||||||
import "oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
dagreLayout = d2dagrelayout.Layout
|
|
||||||
}
|
|
||||||
11
d2lib/d2.go
11
d2lib/d2.go
|
|
@ -9,6 +9,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/d2graph"
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
"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"
|
||||||
|
|
@ -74,12 +75,12 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target
|
||||||
func getLayout(opts *CompileOptions) (func(context.Context, *d2graph.Graph) error, error) {
|
func getLayout(opts *CompileOptions) (func(context.Context, *d2graph.Graph) error, error) {
|
||||||
if opts.Layout != nil {
|
if opts.Layout != nil {
|
||||||
return opts.Layout, nil
|
return opts.Layout, nil
|
||||||
} else if os.Getenv("D2_LAYOUT") == "dagre" && dagreLayout != nil {
|
} else if os.Getenv("D2_LAYOUT") == "dagre" {
|
||||||
return dagreLayout, nil
|
defaultLayout := func(ctx context.Context, g *d2graph.Graph) error {
|
||||||
|
return d2dagrelayout.Layout(ctx, g, nil)
|
||||||
|
}
|
||||||
|
return defaultLayout, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("no available layout")
|
return nil, errors.New("no available layout")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// See c.go
|
|
||||||
var dagreLayout func(context.Context, *d2graph.Graph) error
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"oss.terrastruct.com/util-go/xdefer"
|
"oss.terrastruct.com/util-go/xdefer"
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2graph"
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
)
|
)
|
||||||
|
|
@ -37,6 +38,45 @@ import (
|
||||||
// the error to stderr.
|
// the error to stderr.
|
||||||
type execPlugin struct {
|
type execPlugin struct {
|
||||||
path string
|
path string
|
||||||
|
opts map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p execPlugin) Flags(ctx context.Context) (_ []PluginSpecificFlag, err error) {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||||
|
defer cancel()
|
||||||
|
cmd := exec.CommandContext(ctx, p.path, "flags")
|
||||||
|
defer xdefer.Errorf(&err, "failed to run %v", cmd.Args)
|
||||||
|
|
||||||
|
stdout, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
ee := &exec.ExitError{}
|
||||||
|
if errors.As(err, &ee) && len(ee.Stderr) > 0 {
|
||||||
|
return nil, fmt.Errorf("%v\nstderr:\n%s", ee, ee.Stderr)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags []PluginSpecificFlag
|
||||||
|
|
||||||
|
err = json.Unmarshal(stdout, &flags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *execPlugin) HydrateOpts(opts []byte) error {
|
||||||
|
if opts != nil {
|
||||||
|
var execOpts map[string]string
|
||||||
|
err := json.Unmarshal(opts, &execOpts)
|
||||||
|
if err != nil {
|
||||||
|
return xmain.UsageErrorf("non-exec layout options given for exec")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.opts = execOpts
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) {
|
func (p execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) {
|
||||||
|
|
@ -73,7 +113,11 @@ func (p execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, p.path, "layout")
|
args := []string{"layout"}
|
||||||
|
for k, v := range p.opts {
|
||||||
|
args = append(args, k, v)
|
||||||
|
}
|
||||||
|
cmd := exec.CommandContext(ctx, p.path, args...)
|
||||||
|
|
||||||
buffer := bytes.Buffer{}
|
buffer := bytes.Buffer{}
|
||||||
buffer.Write(graphBytes)
|
buffer.Write(graphBytes)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"oss.terrastruct.com/util-go/xexec"
|
"oss.terrastruct.com/util-go/xexec"
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2graph"
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
)
|
)
|
||||||
|
|
@ -19,10 +20,32 @@ import (
|
||||||
// See plugin_* files for the plugins available for bundling.
|
// See plugin_* files for the plugins available for bundling.
|
||||||
var plugins []Plugin
|
var plugins []Plugin
|
||||||
|
|
||||||
|
type PluginSpecificFlag struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
Default interface{}
|
||||||
|
Usage string
|
||||||
|
// Must match the tag in the opt
|
||||||
|
Tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *PluginSpecificFlag) AddToOpts(opts *xmain.Opts) {
|
||||||
|
switch f.Type {
|
||||||
|
case "string":
|
||||||
|
opts.String("", f.Name, "", f.Default.(string), f.Usage)
|
||||||
|
case "int64":
|
||||||
|
opts.Int64("", f.Name, "", f.Default.(int64), f.Usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Plugin interface {
|
type Plugin interface {
|
||||||
// Info returns the current info information of the plugin.
|
// Info returns the current info information of the plugin.
|
||||||
Info(context.Context) (*PluginInfo, error)
|
Info(context.Context) (*PluginInfo, error)
|
||||||
|
|
||||||
|
Flags(context.Context) ([]PluginSpecificFlag, error)
|
||||||
|
|
||||||
|
HydrateOpts([]byte) error
|
||||||
|
|
||||||
// Layout runs the plugin's autolayout algorithm on the input graph
|
// Layout runs the plugin's autolayout algorithm on the input graph
|
||||||
// and returns a new graph with the computed placements.
|
// and returns a new graph with the computed placements.
|
||||||
Layout(context.Context, *d2graph.Graph) error
|
Layout(context.Context, *d2graph.Graph) error
|
||||||
|
|
@ -109,3 +132,15 @@ func FindPlugin(ctx context.Context, name string) (Plugin, string, error) {
|
||||||
|
|
||||||
return &execPlugin{path: path}, path, nil
|
return &execPlugin{path: path}, path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ListPluginFlags(ctx context.Context) ([]PluginSpecificFlag, error) {
|
||||||
|
var out []PluginSpecificFlag
|
||||||
|
for _, p := range plugins {
|
||||||
|
flags, err := p.Flags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out = append(out, flags...)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,36 +4,82 @@ package d2plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"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/util-go/xmain"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DagrePlugin = dagrePlugin{}
|
var DagrePlugin = dagrePlugin{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
plugins = append(plugins, DagrePlugin)
|
plugins = append(plugins, &DagrePlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type dagrePlugin struct{}
|
type dagrePlugin struct {
|
||||||
|
opts *d2dagrelayout.ConfigurableOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p dagrePlugin) Flags(context.Context) ([]PluginSpecificFlag, error) {
|
||||||
|
return []PluginSpecificFlag{
|
||||||
|
{
|
||||||
|
Name: "dagre-nodesep",
|
||||||
|
Type: "int64",
|
||||||
|
Default: int64(d2dagrelayout.DefaultOpts.NodeSep),
|
||||||
|
Usage: "number of pixels that separate nodes horizontally.",
|
||||||
|
Tag: "nodesep",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "dagre-edgesep",
|
||||||
|
Type: "int64",
|
||||||
|
Default: int64(d2dagrelayout.DefaultOpts.EdgeSep),
|
||||||
|
Usage: "number of pixels that separate edges horizontally.",
|
||||||
|
Tag: "edgesep",
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *dagrePlugin) HydrateOpts(opts []byte) error {
|
||||||
|
if opts != nil {
|
||||||
|
var dagreOpts d2dagrelayout.ConfigurableOpts
|
||||||
|
err := json.Unmarshal(opts, &dagreOpts)
|
||||||
|
if err != nil {
|
||||||
|
return xmain.UsageErrorf("non-dagre layout options given for dagre")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.opts = &dagreOpts
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p dagrePlugin) Info(ctx context.Context) (*PluginInfo, error) {
|
||||||
|
opts := xmain.NewOpts(nil, nil, nil)
|
||||||
|
flags, err := p.Flags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, f := range flags {
|
||||||
|
f.AddToOpts(opts)
|
||||||
|
}
|
||||||
|
|
||||||
func (p dagrePlugin) Info(context.Context) (*PluginInfo, error) {
|
|
||||||
return &PluginInfo{
|
return &PluginInfo{
|
||||||
Name: "dagre",
|
Name: "dagre",
|
||||||
ShortHelp: "The directed graph layout library Dagre",
|
ShortHelp: "The directed graph layout library Dagre",
|
||||||
LongHelp: `dagre is a directed graph layout library for JavaScript.
|
LongHelp: fmt.Sprintf(`dagre is a directed graph layout library for JavaScript.
|
||||||
See https://github.com/dagrejs/dagre
|
See https://github.com/dagrejs/dagre
|
||||||
The implementation of this plugin is at: https://github.com/terrastruct/d2/tree/master/d2plugin/d2dagrelayout
|
|
||||||
|
|
||||||
note: dagre is the primary layout algorithm for text to diagram generator Mermaid.js.
|
Flags correspond to ones found at https://github.com/dagrejs/dagre/wiki. See dagre's reference for more on each.
|
||||||
See https://github.com/mermaid-js/mermaid
|
|
||||||
We have a useful comparison at https://text-to-diagram.com/?example=basic&a=d2&b=mermaid
|
Flags:
|
||||||
`,
|
%s
|
||||||
|
`, opts.Defaults()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p dagrePlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
func (p dagrePlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
||||||
return d2dagrelayout.Layout(ctx, g)
|
return d2dagrelayout.Layout(ctx, g, p.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p dagrePlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) {
|
func (p dagrePlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) {
|
||||||
|
|
|
||||||
|
|
@ -4,31 +4,103 @@ package d2plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2graph"
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
|
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
|
||||||
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ELKPlugin = elkPlugin{}
|
var ELKPlugin = elkPlugin{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
plugins = append(plugins, ELKPlugin)
|
plugins = append(plugins, &ELKPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type elkPlugin struct{}
|
type elkPlugin struct {
|
||||||
|
opts *d2elklayout.ConfigurableOpts
|
||||||
|
}
|
||||||
|
|
||||||
func (p elkPlugin) Info(context.Context) (*PluginInfo, error) {
|
func (p elkPlugin) Flags(context.Context) ([]PluginSpecificFlag, error) {
|
||||||
|
return []PluginSpecificFlag{
|
||||||
|
{
|
||||||
|
Name: "elk-algorithm",
|
||||||
|
Type: "string",
|
||||||
|
Default: d2elklayout.DefaultOpts.Algorithm,
|
||||||
|
Usage: "layout algorithm",
|
||||||
|
Tag: "elk.algorithm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "elk-nodeNodeBetweenLayers",
|
||||||
|
Type: "int64",
|
||||||
|
Default: int64(d2elklayout.DefaultOpts.NodeSpacing),
|
||||||
|
Usage: "the spacing to be preserved between any pair of nodes of two adjacent layers",
|
||||||
|
Tag: "spacing.nodeNodeBetweenLayers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "elk-padding",
|
||||||
|
Type: "string",
|
||||||
|
Default: d2elklayout.DefaultOpts.Padding,
|
||||||
|
Usage: "the padding to be left to a parent element’s border when placing child elements",
|
||||||
|
Tag: "elk.padding",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "elk-edgeNodeBetweenLayers",
|
||||||
|
Type: "int64",
|
||||||
|
Default: int64(d2elklayout.DefaultOpts.EdgeNodeSpacing),
|
||||||
|
Usage: "the spacing to be preserved between nodes and edges that are routed next to the node’s layer",
|
||||||
|
Tag: "spacing.edgeNodeBetweenLayers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "elk-nodeSelfLoop",
|
||||||
|
Type: "int64",
|
||||||
|
Default: int64(d2elklayout.DefaultOpts.SelfLoopSpacing),
|
||||||
|
Usage: "spacing to be preserved between a node and its self loops",
|
||||||
|
Tag: "elk.spacing.nodeSelfLoop",
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *elkPlugin) HydrateOpts(opts []byte) error {
|
||||||
|
if opts != nil {
|
||||||
|
var elkOpts d2elklayout.ConfigurableOpts
|
||||||
|
err := json.Unmarshal(opts, &elkOpts)
|
||||||
|
if err != nil {
|
||||||
|
return xmain.UsageErrorf("non-ELK layout options given for ELK")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.opts = &elkOpts
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p elkPlugin) Info(ctx context.Context) (*PluginInfo, error) {
|
||||||
|
opts := xmain.NewOpts(nil, nil, nil)
|
||||||
|
flags, err := p.Flags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, f := range flags {
|
||||||
|
f.AddToOpts(opts)
|
||||||
|
}
|
||||||
return &PluginInfo{
|
return &PluginInfo{
|
||||||
Name: "elk",
|
Name: "elk",
|
||||||
ShortHelp: "Eclipse Layout Kernel (ELK) with the Layered algorithm.",
|
ShortHelp: "Eclipse Layout Kernel (ELK) with the Layered algorithm.",
|
||||||
LongHelp: `ELK is a layout engine offered by Eclipse.
|
LongHelp: fmt.Sprintf(`ELK is a layout engine offered by Eclipse.
|
||||||
Originally written in Java, it has been ported to Javascript and cross-compiled into D2.
|
Originally written in Java, it has been ported to Javascript and cross-compiled into D2.
|
||||||
See https://github.com/kieler/elkjs for more.`,
|
See https://github.com/kieler/elkjs for more.
|
||||||
|
|
||||||
|
Flags correspond to ones found at https://www.eclipse.org/elk/reference.html. See ELK's reference for more on each.
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
%s
|
||||||
|
`, opts.Defaults()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p elkPlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
func (p elkPlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
||||||
return d2elklayout.Layout(ctx, g)
|
return d2elklayout.Layout(ctx, g, p.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p elkPlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) {
|
func (p elkPlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) {
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ func Serve(p Plugin) xmain.RunFunc {
|
||||||
switch subcmd {
|
switch subcmd {
|
||||||
case "info":
|
case "info":
|
||||||
return info(ctx, p, ms)
|
return info(ctx, p, ms)
|
||||||
|
case "flags":
|
||||||
|
return flags(ctx, p, ms)
|
||||||
case "layout":
|
case "layout":
|
||||||
return layout(ctx, p, ms)
|
return layout(ctx, p, ms)
|
||||||
case "postprocess":
|
case "postprocess":
|
||||||
|
|
@ -64,6 +66,22 @@ func info(ctx context.Context, p Plugin, ms *xmain.State) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func flags(ctx context.Context, p Plugin, ms *xmain.State) error {
|
||||||
|
flags, err := p.Flags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = ms.Stdout.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func layout(ctx context.Context, p Plugin, ms *xmain.State) error {
|
func layout(ctx context.Context, p Plugin, ms *xmain.State) error {
|
||||||
in, err := io.ReadAll(ms.Stdin)
|
in, err := io.ReadAll(ms.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -314,7 +314,7 @@ func run(t *testing.T, tc testCase) {
|
||||||
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
ThemeID: 0,
|
ThemeID: 0,
|
||||||
Layout: d2dagrelayout.Layout,
|
Layout: d2dagrelayout.DefaultLayout,
|
||||||
FontFamily: go2.Pointer(d2fonts.HandDrawn),
|
FontFamily: go2.Pointer(d2fonts.HandDrawn),
|
||||||
})
|
})
|
||||||
if !tassert.Nil(t, err) {
|
if !tassert.Nil(t, err) {
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ func run(t *testing.T, tc testCase) {
|
||||||
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
ThemeID: 0,
|
ThemeID: 0,
|
||||||
Layout: d2dagrelayout.Layout,
|
Layout: d2dagrelayout.DefaultLayout,
|
||||||
})
|
})
|
||||||
if !tassert.Nil(t, err) {
|
if !tassert.Nil(t, err) {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
"oss.terrastruct.com/d2/d2lib"
|
"oss.terrastruct.com/d2/d2lib"
|
||||||
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
||||||
|
|
@ -15,8 +16,11 @@ import (
|
||||||
// Remember to add if err != nil checks in production.
|
// Remember to add if err != nil checks in production.
|
||||||
func main() {
|
func main() {
|
||||||
ruler, _ := textmeasure.NewRuler()
|
ruler, _ := textmeasure.NewRuler()
|
||||||
|
defaultLayout := func(ctx context.Context, g *d2graph.Graph) error {
|
||||||
|
return d2dagrelayout.Layout(ctx, g, nil)
|
||||||
|
}
|
||||||
diagram, _, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
|
diagram, _, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
|
||||||
Layout: d2dagrelayout.Layout,
|
Layout: defaultLayout,
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
ThemeID: d2themescatalog.GrapeSoda.ID,
|
ThemeID: d2themescatalog.GrapeSoda.ID,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ func main() {
|
||||||
// From one.go
|
// From one.go
|
||||||
ruler, _ := textmeasure.NewRuler()
|
ruler, _ := textmeasure.NewRuler()
|
||||||
_, graph, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
|
_, graph, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
|
||||||
Layout: d2dagrelayout.Layout,
|
Layout: d2dagrelayout.DefaultLayout,
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
ThemeID: d2themescatalog.GrapeSoda.ID,
|
ThemeID: d2themescatalog.GrapeSoda.ID,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ func main() {
|
||||||
graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil)
|
graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil)
|
||||||
ruler, _ := textmeasure.NewRuler()
|
ruler, _ := textmeasure.NewRuler()
|
||||||
_ = graph.SetDimensions(nil, ruler, nil)
|
_ = graph.SetDimensions(nil, ruler, nil)
|
||||||
_ = d2dagrelayout.Layout(context.Background(), graph)
|
_ = d2dagrelayout.Layout(context.Background(), graph, nil)
|
||||||
diagram, _ := d2exporter.Export(context.Background(), graph, d2themescatalog.NeutralDefault.ID, nil)
|
diagram, _ := d2exporter.Export(context.Background(), graph, d2themescatalog.NeutralDefault.ID, nil)
|
||||||
out, _ := d2svg.Render(diagram, &d2svg.RenderOpts{
|
out, _ := d2svg.Render(diagram, &d2svg.RenderOpts{
|
||||||
Pad: d2svg.DEFAULT_PADDING,
|
Pad: d2svg.DEFAULT_PADDING,
|
||||||
|
|
|
||||||
|
|
@ -130,9 +130,9 @@ func run(t *testing.T, tc testCase) {
|
||||||
for _, layoutName := range layoutsTested {
|
for _, layoutName := range layoutsTested {
|
||||||
var layout func(context.Context, *d2graph.Graph) error
|
var layout func(context.Context, *d2graph.Graph) error
|
||||||
if layoutName == "dagre" {
|
if layoutName == "dagre" {
|
||||||
layout = d2dagrelayout.Layout
|
layout = d2dagrelayout.DefaultLayout
|
||||||
} else if layoutName == "elk" {
|
} else if layoutName == "elk" {
|
||||||
layout = d2elklayout.Layout
|
layout = d2elklayout.DefaultLayout
|
||||||
}
|
}
|
||||||
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
|
|
|
||||||
4
help.go
4
help.go
|
|
@ -32,7 +32,7 @@ Flags:
|
||||||
|
|
||||||
Subcommands:
|
Subcommands:
|
||||||
%[1]s layout - Lists available layout engine options with short help
|
%[1]s layout - Lists available layout engine options with short help
|
||||||
%[1]s layout [name] - Display long help for a particular layout engine
|
%[1]s layout [name] - Display long help for a particular layout engine, including its configuration options
|
||||||
%[1]s fmt file.d2 - Format file.d2
|
%[1]s fmt file.d2 - Format file.d2
|
||||||
|
|
||||||
See more docs and the source code at https://oss.terrastruct.com/d2
|
See more docs and the source code at https://oss.terrastruct.com/d2
|
||||||
|
|
@ -75,7 +75,7 @@ Example:
|
||||||
D2_LAYOUT=dagre d2 in.d2 out.svg
|
D2_LAYOUT=dagre d2 in.d2 out.svg
|
||||||
|
|
||||||
Subcommands:
|
Subcommands:
|
||||||
%s layout [layout name] - Display long help for a particular layout engine
|
%s layout [layout name] - Display long help for a particular layout engine, including its configuration options
|
||||||
|
|
||||||
See more docs at https://oss.terrastruct.com/d2
|
See more docs at https://oss.terrastruct.com/d2
|
||||||
`, strings.Join(pluginLines, "\n"), ms.Name)
|
`, strings.Join(pluginLines, "\n"), ms.Name)
|
||||||
|
|
|
||||||
52
main.go
52
main.go
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -75,6 +76,11 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = populateLayoutOpts(ctx, ms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
err = ms.Opts.Flags.Parse(ms.Opts.Args)
|
err = ms.Opts.Flags.Parse(ms.Opts.Args)
|
||||||
if !errors.Is(err, pflag.ErrHelp) && err != nil {
|
if !errors.Is(err, pflag.ErrHelp) && err != nil {
|
||||||
return xmain.UsageErrorf("failed to parse flags: %v", err)
|
return xmain.UsageErrorf("failed to parse flags: %v", err)
|
||||||
|
|
@ -144,6 +150,11 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = parseLayoutOpts(ctx, ms, plugin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
pluginLocation := "bundled"
|
pluginLocation := "bundled"
|
||||||
if path != "" {
|
if path != "" {
|
||||||
pluginLocation = fmt.Sprintf("executable plugin at %s", humanPath(path))
|
pluginLocation = fmt.Sprintf("executable plugin at %s", humanPath(path))
|
||||||
|
|
@ -288,3 +299,44 @@ func renameExt(fp string, newExt string) string {
|
||||||
func DiscardSlog(ctx context.Context) context.Context {
|
func DiscardSlog(ctx context.Context) context.Context {
|
||||||
return ctxlog.With(ctx, slog.Make(sloghuman.Sink(io.Discard)))
|
return ctxlog.With(ctx, slog.Make(sloghuman.Sink(io.Discard)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func populateLayoutOpts(ctx context.Context, ms *xmain.State) error {
|
||||||
|
pluginFlags, err := d2plugin.ListPluginFlags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range pluginFlags {
|
||||||
|
f.AddToOpts(ms.Opts)
|
||||||
|
// Don't pollute the main d2 flagset with these. It'll be a lot
|
||||||
|
ms.Opts.Flags.MarkHidden(f.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLayoutOpts(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin) error {
|
||||||
|
opts := make(map[string]interface{})
|
||||||
|
flags, err := plugin.Flags(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, f := range flags {
|
||||||
|
switch f.Type {
|
||||||
|
case "string":
|
||||||
|
val, _ := ms.Opts.Flags.GetString(f.Name)
|
||||||
|
opts[f.Tag] = val
|
||||||
|
case "int64":
|
||||||
|
val, _ := ms.Opts.Flags.GetInt64(f.Name)
|
||||||
|
opts[f.Tag] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = plugin.HydrateOpts(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue