From 7547d51727540a8055226c2a65838367c4aa689d Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 9 Jun 2023 16:44:10 -0700 Subject: [PATCH 01/11] configure timeout with D2_TIMEOUT --- d2cli/main.go | 13 +++++++++++-- d2plugin/exec.go | 12 ++++++++++-- e2etests/report/main.go | 14 ++++++++++++-- lib/env/env.go | 11 +++++++++++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/d2cli/main.go b/d2cli/main.go index 74a24c17e..0bfa4edf4 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -31,6 +31,7 @@ import ( "oss.terrastruct.com/d2/d2themes" "oss.terrastruct.com/d2/d2themes/d2themescatalog" "oss.terrastruct.com/d2/lib/background" + "oss.terrastruct.com/d2/lib/env" "oss.terrastruct.com/d2/lib/imgbundler" ctxlog "oss.terrastruct.com/d2/lib/log" "oss.terrastruct.com/d2/lib/pdf" @@ -298,8 +299,16 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { return w.run() } - ctx, cancel := context.WithTimeout(ctx, time.Minute*2) - defer cancel() + timeout := time.Minute * 2 + if seconds, has := env.Timeout(); has { + timeout = time.Duration(seconds) * time.Second + } + + if timeout > 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + } _, written, err := compile(ctx, ms, plugin, renderOpts, fontFamily, *animateIntervalFlag, inputPath, outputPath, *bundleFlag, *forceAppendixFlag, pw.Page) if err != nil { diff --git a/d2plugin/exec.go b/d2plugin/exec.go index 4a5836f35..b357267af 100644 --- a/d2plugin/exec.go +++ b/d2plugin/exec.go @@ -15,6 +15,7 @@ import ( "oss.terrastruct.com/util-go/xmain" "oss.terrastruct.com/d2/d2graph" + "oss.terrastruct.com/d2/lib/env" ) // execPlugin uses the binary at pathname with the plugin protocol to implement @@ -147,8 +148,15 @@ func (p *execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) { } func (p *execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { - ctx, cancel := context.WithTimeout(ctx, time.Minute) - defer cancel() + timeout := time.Minute + if seconds, has := env.Timeout(); has { + timeout = time.Duration(seconds) * time.Second + } + if timeout > 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + } graphBytes, err := d2graph.SerializeGraph(g) if err != nil { diff --git a/e2etests/report/main.go b/e2etests/report/main.go index a2ef1896a..905aa0800 100644 --- a/e2etests/report/main.go +++ b/e2etests/report/main.go @@ -15,6 +15,7 @@ import ( "text/template" "time" + "oss.terrastruct.com/d2/lib/env" "oss.terrastruct.com/d2/lib/log" ) @@ -69,8 +70,17 @@ func main() { if !*skipTests { ctx := log.Stderr(context.Background()) - ctx, cancel := context.WithTimeout(ctx, 2*time.Minute) - defer cancel() + + timeout := 2 * time.Minute + if seconds, has := env.Timeout(); has { + timeout = time.Duration(seconds) * time.Second + } + if timeout > 0 { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + } + // don't want to pass empty args to CommandContext args := []string{"test", testDir, testMatchString} if cpuProfileStr != "" { diff --git a/lib/env/env.go b/lib/env/env.go index 07ba22eef..c37eea66d 100644 --- a/lib/env/env.go +++ b/lib/env/env.go @@ -2,6 +2,7 @@ package env import ( "os" + "strconv" ) func Test() bool { @@ -25,3 +26,13 @@ func DevOnly() bool { func SkipGraphDiffTests() bool { return os.Getenv("SKIP_GRAPH_DIFF_TESTS") != "" } + +func Timeout() (int, bool) { + if s := os.Getenv("D2_TIMEOUT"); s != "" { + i, err := strconv.ParseInt(s, 10, 64) + if err == nil { + return int(i), true + } + } + return -1, false +} From 31cbd3eb8735e141422300cc49138a7164e60f32 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 9 Jun 2023 17:36:45 -0700 Subject: [PATCH 02/11] changelog --- ci/release/changelogs/next.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 466f60bdc..196c9227e 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -1,5 +1,7 @@ #### Features ๐Ÿš€ +- Configure timeout value with D2_TIMEOUT env var. [#1392](https://github.com/terrastruct/d2/pull/1392) + #### Improvements ๐Ÿงน - Display version on CLI help invocation [#1400](https://github.com/terrastruct/d2/pull/1400) From a5b87038bd3c6030c5b2779a21176d914a0c1694 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Mon, 12 Jun 2023 11:47:59 -0700 Subject: [PATCH 03/11] option to configure timeout with flag --- d2cli/main.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/d2cli/main.go b/d2cli/main.go index 0bfa4edf4..2a7425141 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -31,7 +31,6 @@ import ( "oss.terrastruct.com/d2/d2themes" "oss.terrastruct.com/d2/d2themes/d2themescatalog" "oss.terrastruct.com/d2/lib/background" - "oss.terrastruct.com/d2/lib/env" "oss.terrastruct.com/d2/lib/imgbundler" ctxlog "oss.terrastruct.com/d2/lib/log" "oss.terrastruct.com/d2/lib/pdf" @@ -89,6 +88,11 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { if err != nil { return err } + timeoutFlag, err := ms.Opts.Int64("D2_TIMEOUT", "timeout", "", 0, "the number of seconds before d2 will timeout. default is 60s.") + if err != nil { + return err + } + versionFlag, err := ms.Opts.Bool("", "version", "v", false, "get the version") if err != nil { return err @@ -161,6 +165,9 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { if *browserFlag != "" { ms.Env.Setenv("BROWSER", *browserFlag) } + if timeoutFlag != nil { + os.Setenv("D2_TIMEOUT", fmt.Sprintf("%d", *timeoutFlag)) + } var inputPath string var outputPath string @@ -300,10 +307,9 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { } timeout := time.Minute * 2 - if seconds, has := env.Timeout(); has { - timeout = time.Duration(seconds) * time.Second + if timeoutFlag != nil { + timeout = time.Duration(*timeoutFlag) * time.Second } - if timeout > 0 { var cancel func() ctx, cancel = context.WithTimeout(ctx, timeout) From 19cc5ee0e22d9f0b8c7079e09fdc01d0026476d2 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Tue, 13 Jun 2023 17:34:47 -0700 Subject: [PATCH 04/11] util func for overriding default timeout according to env var --- d2cli/main.go | 12 +++--------- d2plugin/exec.go | 13 +++---------- e2etests/report/main.go | 12 ++---------- lib/log/log.go | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/d2cli/main.go b/d2cli/main.go index 2a7425141..9ae124b20 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -32,6 +32,7 @@ import ( "oss.terrastruct.com/d2/d2themes/d2themescatalog" "oss.terrastruct.com/d2/lib/background" "oss.terrastruct.com/d2/lib/imgbundler" + "oss.terrastruct.com/d2/lib/log" ctxlog "oss.terrastruct.com/d2/lib/log" "oss.terrastruct.com/d2/lib/pdf" "oss.terrastruct.com/d2/lib/png" @@ -306,15 +307,8 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { return w.run() } - timeout := time.Minute * 2 - if timeoutFlag != nil { - timeout = time.Duration(*timeoutFlag) * time.Second - } - if timeout > 0 { - var cancel func() - ctx, cancel = context.WithTimeout(ctx, timeout) - defer cancel() - } + ctx, cancel := log.WithTimeout(ctx, time.Minute*2) + defer cancel() _, written, err := compile(ctx, ms, plugin, renderOpts, fontFamily, *animateIntervalFlag, inputPath, outputPath, *bundleFlag, *forceAppendixFlag, pw.Page) if err != nil { diff --git a/d2plugin/exec.go b/d2plugin/exec.go index b357267af..bc9835846 100644 --- a/d2plugin/exec.go +++ b/d2plugin/exec.go @@ -15,7 +15,7 @@ import ( "oss.terrastruct.com/util-go/xmain" "oss.terrastruct.com/d2/d2graph" - "oss.terrastruct.com/d2/lib/env" + "oss.terrastruct.com/d2/lib/log" ) // execPlugin uses the binary at pathname with the plugin protocol to implement @@ -148,15 +148,8 @@ func (p *execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) { } func (p *execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { - timeout := time.Minute - if seconds, has := env.Timeout(); has { - timeout = time.Duration(seconds) * time.Second - } - if timeout > 0 { - var cancel func() - ctx, cancel = context.WithTimeout(ctx, timeout) - defer cancel() - } + ctx, cancel := log.WithTimeout(ctx, time.Minute) + defer cancel() graphBytes, err := d2graph.SerializeGraph(g) if err != nil { diff --git a/e2etests/report/main.go b/e2etests/report/main.go index 905aa0800..e50fca540 100644 --- a/e2etests/report/main.go +++ b/e2etests/report/main.go @@ -15,7 +15,6 @@ import ( "text/template" "time" - "oss.terrastruct.com/d2/lib/env" "oss.terrastruct.com/d2/lib/log" ) @@ -71,15 +70,8 @@ func main() { if !*skipTests { ctx := log.Stderr(context.Background()) - timeout := 2 * time.Minute - if seconds, has := env.Timeout(); has { - timeout = time.Duration(seconds) * time.Second - } - if timeout > 0 { - var cancel func() - ctx, cancel = context.WithTimeout(ctx, timeout) - defer cancel() - } + ctx, cancel := log.WithTimeout(ctx, 2*time.Minute) + defer cancel() // don't want to pass empty args to CommandContext args := []string{"test", testDir, testMatchString} diff --git a/lib/log/log.go b/lib/log/log.go index d70edf0b8..681da7231 100644 --- a/lib/log/log.go +++ b/lib/log/log.go @@ -8,6 +8,7 @@ import ( "os" "runtime/debug" "testing" + "time" "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" @@ -112,3 +113,16 @@ func Stderr(ctx context.Context) context.Context { return With(ctx, l) } + +// WithTimeout returns context.WithTimeout(ctx, timeout) but timeout is overridden with D2_TIMEOUT if set +func WithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { + t := timeout + if seconds, has := env.Timeout(); has { + t = time.Duration(seconds) * time.Second + } + if t <= 0 { + return ctx, func() {} + } + + return context.WithTimeout(ctx, t) +} From fcd4fefb642720e7b3bae16c9243080453c4e386 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 14 Jun 2023 11:47:43 -0700 Subject: [PATCH 05/11] move to timelib.WithTimeout --- d2cli/main.go | 4 ++-- d2plugin/exec.go | 4 ++-- e2etests/report/main.go | 3 ++- lib/log/log.go | 14 -------------- lib/time/time.go | 26 ++++++++++++++++++++++++++ 5 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 lib/time/time.go diff --git a/d2cli/main.go b/d2cli/main.go index 9ae124b20..81c0de2da 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -32,12 +32,12 @@ import ( "oss.terrastruct.com/d2/d2themes/d2themescatalog" "oss.terrastruct.com/d2/lib/background" "oss.terrastruct.com/d2/lib/imgbundler" - "oss.terrastruct.com/d2/lib/log" ctxlog "oss.terrastruct.com/d2/lib/log" "oss.terrastruct.com/d2/lib/pdf" "oss.terrastruct.com/d2/lib/png" "oss.terrastruct.com/d2/lib/pptx" "oss.terrastruct.com/d2/lib/textmeasure" + timelib "oss.terrastruct.com/d2/lib/time" "oss.terrastruct.com/d2/lib/version" "oss.terrastruct.com/d2/lib/xgif" @@ -307,7 +307,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { return w.run() } - ctx, cancel := log.WithTimeout(ctx, time.Minute*2) + ctx, cancel := timelib.WithTimeout(ctx, time.Minute*2) defer cancel() _, written, err := compile(ctx, ms, plugin, renderOpts, fontFamily, *animateIntervalFlag, inputPath, outputPath, *bundleFlag, *forceAppendixFlag, pw.Page) diff --git a/d2plugin/exec.go b/d2plugin/exec.go index bc9835846..9e5b4821f 100644 --- a/d2plugin/exec.go +++ b/d2plugin/exec.go @@ -15,7 +15,7 @@ import ( "oss.terrastruct.com/util-go/xmain" "oss.terrastruct.com/d2/d2graph" - "oss.terrastruct.com/d2/lib/log" + timelib "oss.terrastruct.com/d2/lib/time" ) // execPlugin uses the binary at pathname with the plugin protocol to implement @@ -148,7 +148,7 @@ func (p *execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) { } func (p *execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { - ctx, cancel := log.WithTimeout(ctx, time.Minute) + ctx, cancel := timelib.WithTimeout(ctx, time.Minute) defer cancel() graphBytes, err := d2graph.SerializeGraph(g) diff --git a/e2etests/report/main.go b/e2etests/report/main.go index e50fca540..481cc9fd1 100644 --- a/e2etests/report/main.go +++ b/e2etests/report/main.go @@ -16,6 +16,7 @@ import ( "time" "oss.terrastruct.com/d2/lib/log" + timelib "oss.terrastruct.com/d2/lib/time" ) //go:embed template.html @@ -70,7 +71,7 @@ func main() { if !*skipTests { ctx := log.Stderr(context.Background()) - ctx, cancel := log.WithTimeout(ctx, 2*time.Minute) + ctx, cancel := timelib.WithTimeout(ctx, 2*time.Minute) defer cancel() // don't want to pass empty args to CommandContext diff --git a/lib/log/log.go b/lib/log/log.go index 681da7231..d70edf0b8 100644 --- a/lib/log/log.go +++ b/lib/log/log.go @@ -8,7 +8,6 @@ import ( "os" "runtime/debug" "testing" - "time" "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" @@ -113,16 +112,3 @@ func Stderr(ctx context.Context) context.Context { return With(ctx, l) } - -// WithTimeout returns context.WithTimeout(ctx, timeout) but timeout is overridden with D2_TIMEOUT if set -func WithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { - t := timeout - if seconds, has := env.Timeout(); has { - t = time.Duration(seconds) * time.Second - } - if t <= 0 { - return ctx, func() {} - } - - return context.WithTimeout(ctx, t) -} diff --git a/lib/time/time.go b/lib/time/time.go new file mode 100644 index 000000000..a9a1f692b --- /dev/null +++ b/lib/time/time.go @@ -0,0 +1,26 @@ +package time + +import ( + "context" + "time" + + "oss.terrastruct.com/d2/lib/env" +) + +func HumanDate(t time.Time) string { + local := t.Local() + return local.Format(time.RFC822) +} + +// WithTimeout returns context.WithTimeout(ctx, timeout) but timeout is overridden with D2_TIMEOUT if set +func WithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { + t := timeout + if seconds, has := env.Timeout(); has { + t = time.Duration(seconds) * time.Second + } + if t <= 0 { + return ctx, func() {} + } + + return context.WithTimeout(ctx, t) +} From d9cf6871aad97514e93e7c09b42c8125d618b6dc Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 14 Jun 2023 11:52:30 -0700 Subject: [PATCH 06/11] fix default value in flag description --- d2cli/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2cli/main.go b/d2cli/main.go index 81c0de2da..b50a02bb0 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -89,7 +89,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { if err != nil { return err } - timeoutFlag, err := ms.Opts.Int64("D2_TIMEOUT", "timeout", "", 0, "the number of seconds before d2 will timeout. default is 60s.") + timeoutFlag, err := ms.Opts.Int64("D2_TIMEOUT", "timeout", "", 0, "the number of seconds before d2 will timeout. (default=120)") if err != nil { return err } From ce1cb0859b206ffd07697719cb65969bd993e6ca Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 14 Jun 2023 13:19:47 -0700 Subject: [PATCH 07/11] have Layout timeout match d2 --- d2plugin/exec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2plugin/exec.go b/d2plugin/exec.go index 9e5b4821f..4631ef036 100644 --- a/d2plugin/exec.go +++ b/d2plugin/exec.go @@ -148,7 +148,7 @@ func (p *execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) { } func (p *execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { - ctx, cancel := timelib.WithTimeout(ctx, time.Minute) + ctx, cancel := timelib.WithTimeout(ctx, time.Minute*2) defer cancel() graphBytes, err := d2graph.SerializeGraph(g) From e8254997c7f787795d85f04f28216e6fd79fcccd Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 14 Jun 2023 16:08:57 -0700 Subject: [PATCH 08/11] default 120 --- d2cli/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2cli/main.go b/d2cli/main.go index b50a02bb0..4ce34764c 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -89,7 +89,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { if err != nil { return err } - timeoutFlag, err := ms.Opts.Int64("D2_TIMEOUT", "timeout", "", 0, "the number of seconds before d2 will timeout. (default=120)") + timeoutFlag, err := ms.Opts.Int64("D2_TIMEOUT", "timeout", "", 120, "the number of seconds before d2 will timeout") if err != nil { return err } From 78d44347ff9d53a44947dc3c6dbde126cc3654a5 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 14 Jun 2023 16:11:54 -0700 Subject: [PATCH 09/11] man --- ci/release/template/man/d2.1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index ff8263105..7eb318f62 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -112,6 +112,9 @@ Print debug logs. .It Fl -img-cache Ar true In watch mode, images used in icons are cached for subsequent compilations. This should be disabled if images might change .Ns . +.It Fl -timeout Ar 120 +The number of seconds before d2 will timeout. +.Ns . .It Fl h , -help Print usage information and exit. .It Fl v , -version From 4a751b1bd5b2be530d5c96fa89df8f7e0f08b5ca Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 14 Jun 2023 16:30:29 -0700 Subject: [PATCH 10/11] copy --- ci/release/template/man/d2.1 | 2 +- d2cli/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index 7eb318f62..c71b96bec 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -113,7 +113,7 @@ Print debug logs. In watch mode, images used in icons are cached for subsequent compilations. This should be disabled if images might change .Ns . .It Fl -timeout Ar 120 -The number of seconds before d2 will timeout. +The maximum number of seconds that D2 runs for before timing out and exiting. When rendering a large diagram, it is recommended to increase this value. .Ns . .It Fl h , -help Print usage information and exit. diff --git a/d2cli/main.go b/d2cli/main.go index 4ce34764c..5fcf94c5d 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -89,7 +89,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { if err != nil { return err } - timeoutFlag, err := ms.Opts.Int64("D2_TIMEOUT", "timeout", "", 120, "the number of seconds before d2 will timeout") + timeoutFlag, err := ms.Opts.Int64("D2_TIMEOUT", "timeout", "", 120, "the maximum number of seconds that D2 runs for before timing out and exiting. When rendering a large diagram, it is recommended to increase this value") if err != nil { return err } From 45ce78a5a47cd9885fb2bde9fecb5a74637ff3f1 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Wed, 14 Jun 2023 16:50:00 -0700 Subject: [PATCH 11/11] trailing '.' in man page --- ci/release/template/man/d2.1 | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index c71b96bec..81e1557cb 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -103,33 +103,41 @@ Set the diagram layout engine to the passed string. For a list of available opti .Ar layout .Ns . .It Fl b , -bundle Ar true -Bundle all assets and layers into the output svg. +Bundle all assets and layers into the output svg +.Ns . .It Fl -force-appendix Ar false An appendix for tooltips and links is added to PNG exports since they are not interactive. Setting this to true adds an appendix to SVG exports as well .Ns . .It Fl d , -debug -Print debug logs. +Print debug logs +.Ns . .It Fl -img-cache Ar true In watch mode, images used in icons are cached for subsequent compilations. This should be disabled if images might change .Ns . .It Fl -timeout Ar 120 -The maximum number of seconds that D2 runs for before timing out and exiting. When rendering a large diagram, it is recommended to increase this value. +The maximum number of seconds that D2 runs for before timing out and exiting. When rendering a large diagram, it is recommended to increase this value .Ns . .It Fl h , -help -Print usage information and exit. +Print usage information and exit +.Ns . .It Fl v , -version -Print version information and exit. +Print version information and exit +.Ns . .El .Sh SUBCOMMANDS .Bl -tag -width Fl .It Ar layout -Lists available layout engine options with short help. +Lists available layout engine options with short help +.Ns . .It Ar layout Op Ar name -Display long help for a particular layout engine, including its configuration options. +Display long help for a particular layout engine, including its configuration options +.Ns . .It Ar themes -Lists available themes. +Lists available themes +.Ns . .It Ar fmt Ar file.d2 ... -Format all passed files. +Format all passed files +.Ns . .El .Sh SEE ALSO .Xr d2plugin-tala 1