diff --git a/.gitignore b/.gitignore index 67d0886..7af7b35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .make-log .changed-files .make-log.txt *.got.json *.got.svg e2e_report.html +bin diff --git a/autofmt.go b/autofmt.go new file mode 100644 index 0000000..749c079 --- /dev/null +++ b/autofmt.go @@ -0,0 +1,36 @@ +package main + +import ( + "bytes" + "context" + + "oss.terrastruct.com/xdefer" + + "oss.terrastruct.com/d2/d2format" + "oss.terrastruct.com/d2/d2parser" + "oss.terrastruct.com/d2/lib/xmain" +) + +func autofmt(ctx context.Context, ms *xmain.State) (err error) { + defer xdefer.Errorf(&err, "autofmt failed") + + ms.Opts = xmain.NewOpts(ms.Env, ms.Log, ms.Opts.Flags.Args()[1:]) + if len(ms.Opts.Args) == 0 { + return xmain.UsageErrorf("fmt must be passed the file to be formatted") + } else if len(ms.Opts.Args) > 1 { + return xmain.UsageErrorf("fmt only accepts one argument for the file to be formatted") + } + + inputPath := ms.Opts.Args[0] + input, err := ms.ReadPath(inputPath) + if err != nil { + return err + } + + m, err := d2parser.Parse(inputPath, bytes.NewReader(input), nil) + if err != nil { + return err + } + + return ms.WritePath(inputPath, []byte(d2format.Format(m))) +} diff --git a/ci/dev.sh b/ci/dev.sh new file mode 100755 index 0000000..cbc71bf --- /dev/null +++ b/ci/dev.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -eu +cd -- "$(dirname "$0")/.." +. ./ci/sub/lib.sh + +sh_c go build --tags=dev -o=bin/d2 . +sh_c ./bin/d2 "$@" diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 25d9930..5fdc99d 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -1,10 +1,12 @@ #### Features 🚀 +- autoformat is supported on the CLI with the `fmt` subcommand. + [#292](https://github.com/terrastruct/d2/pull/292) - Latex is now supported. See [docs](https://d2lang.com/tour/text) for more. [#229](https://github.com/terrastruct/d2/pull/229) - `direction` keyword is now supported to specify `up`, `down`, `right`, `left` layouts. See [docs](https://d2lang.com/tour/layouts) for more. [#251](https://github.com/terrastruct/d2/pull/251) - 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) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index d46ca20..8347bd8 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -7,16 +7,18 @@ .Sh SYNOPSIS .Nm d2 .Op Fl -watch Ar false .Op Fl -theme Em 0 .Ar file.d2 .Op Ar file.svg | file.png .Nm d2 .Ar layout Op Ar name +.Nm d2 +.Ar fmt Ar file.d2 .Sh DESCRIPTION .Nm compiles and renders .Ar file.d2 to .Ar file.svg | .Ar file.png @@ -70,13 +72,17 @@ Print usage information and exit. Print version information and exit. .El .Sh SUBCOMMANDS .Bl -tag -width Fl .It Ar layout Lists available layout engine options with short help. .It Ar layout Op Ar name Display long help for a particular layout engine. +.It Ar fmt Ar file.d2 +Autoformat +.Ar file.d2 +.Ns . .El .Sh SEE ALSO .Xr d2plugin-tala 1 .Sh AUTHORS Terrastruct Inc. diff --git a/help.go b/help.go index 6c1cec8..aff1a72 100644 --- a/help.go +++ b/help.go @@ -10,34 +10,37 @@ import ( "strings" "oss.terrastruct.com/d2/d2plugin" "oss.terrastruct.com/d2/lib/xmain" ) func help(ms *xmain.State) { fmt.Fprintf(ms.Stdout, `Usage: - %s [--watch=false] [--theme=0] file.d2 [file.svg | file.png] + %[1]s [--watch=false] [--theme=0] file.d2 [file.svg | file.png] + %[1]s layout [name] + %[1]s fmt file.d2 %[1]s compiles and renders file.d2 to file.svg | file.png It defaults to file.svg if an output path is not provided. Use - to have d2 read from stdin or write to stdout. See man d2 for more detailed docs. Flags: %s Subcommands: %[1]s layout - Lists available layout engine options with short help - %[1]s layout [layout name] - Display long help for a particular layout engine + %[1]s layout [name] - Display long help for a particular layout engine + %[1]s fmt file.d2 - Autoformat file.d2 See more docs and the source code at https://oss.terrastruct.com/d2 -`, ms.Name, ms.Opts.Defaults()) +`, filepath.Base(ms.Name), ms.Opts.Defaults()) } func layoutHelp(ctx context.Context, ms *xmain.State) error { if len(ms.Opts.Flags.Args()) == 1 { return shortLayoutHelp(ctx, ms) } else if len(ms.Opts.Flags.Args()) == 2 { return longLayoutHelp(ctx, ms) } else { diff --git a/lib/xmain/xmain.go b/lib/xmain/xmain.go index 80de101..b0a1aeb 100644 --- a/lib/xmain/xmain.go +++ b/lib/xmain/xmain.go @@ -61,20 +61,20 @@ func Main(run RunFunc) { } else if errors.As(err, &uerr) { msg = err.Error() usage = true } else { msg = err.Error() } if msg != "" { + ms.Log.Error.Print(msg) if usage { - msg = fmt.Sprintf("%s\n%s", msg, "Run with --help to see usage.") + ms.Log.Error.Print("Run with --help to see usage.") } - ms.Log.Error.Print(msg) } os.Exit(code) } } type State struct { Name string diff --git a/main.go b/main.go index 7586d08..bc183e4 100644 --- a/main.go +++ b/main.go @@ -60,28 +60,36 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return err } err = ms.Opts.Flags.Parse(ms.Opts.Args) if !errors.Is(err, pflag.ErrHelp) && err != nil { return xmain.UsageErrorf("failed to parse flags: %v", err) } + if errors.Is(err, pflag.ErrHelp) { + help(ms) + return nil + } + if len(ms.Opts.Flags.Args()) > 0 { switch ms.Opts.Flags.Arg(0) { case "layout": return layoutHelp(ctx, ms) + case "fmt": + return autofmt(ctx, ms) + case "version": + if len(ms.Opts.Flags.Args()) > 1 { + return xmain.UsageErrorf("version subcommand accepts no arguments") + } + fmt.Println(version.Version) + return nil } } - if errors.Is(err, pflag.ErrHelp) { - help(ms) - return nil - } - if *debugFlag { ms.Env.Setenv("DEBUG", "1") } var inputPath string var outputPath string if len(ms.Opts.Flags.Args()) == 0 { @@ -91,20 +99,16 @@ func run(ctx context.Context, ms *xmain.State) (err error) { } help(ms) return nil } else if len(ms.Opts.Flags.Args()) >= 3 { return xmain.UsageErrorf("too many arguments passed") } if len(ms.Opts.Flags.Args()) >= 1 { - if ms.Opts.Flags.Arg(0) == "version" { - fmt.Println(version.Version) - return nil - } inputPath = ms.Opts.Flags.Arg(0) } if len(ms.Opts.Flags.Args()) >= 2 { outputPath = ms.Opts.Flags.Arg(1) } else { if inputPath == "-" { outputPath = "-" } else {