diff --git a/.golangci.yml b/.golangci.yml index e500bc5..0e0401b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,3 +31,4 @@ issues: # Very commonly not checked. - 'Error return value of .(.*\.Help|.*\.MarkFlagRequired|(os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked' - 'exported method (.*\.MarshalJSON|.*\.UnmarshalJSON) should have comment or be unexported' + - 'composite literal uses unkeyed fields' diff --git a/build.go b/build.go index 85eb204..75ad002 100644 --- a/build.go +++ b/build.go @@ -49,7 +49,7 @@ func flattenedFields(v reflect.Value) (out []flattenedField) { if !fv.CanSet() { continue } - out = append(out, flattenedField{ft, fv}) + out = append(out, flattenedField{field: ft, value: fv}) } return } diff --git a/kong.go b/kong.go index 053d0a4..ec10dfe 100644 --- a/kong.go +++ b/kong.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "reflect" + "strings" ) // Error reported by Kong. @@ -14,7 +15,7 @@ type Error struct{ msg string } func (e Error) Error() string { return e.msg } func fail(format string, args ...interface{}) { - panic(Error{fmt.Sprintf(format, args...)}) + panic(Error{msg: fmt.Sprintf(format, args...)}) } // Must creates a new Parser or panics if there is an error. @@ -170,15 +171,23 @@ func (k *Kong) applyHooks(ctx *Context) error { return nil } +func formatMultilineMessage(w io.Writer, leader string, format string, args ...interface{}) { + lines := strings.Split(fmt.Sprintf(format, args...), "\n") + fmt.Fprintf(w, "%s%s\n", leader, lines[0]) + for _, line := range lines[1:] { + fmt.Fprintf(w, "%*s%s\n", len(leader), " ", line) + } +} + // Printf writes a message to Kong.Stdout with the application name prefixed. func (k *Kong) Printf(format string, args ...interface{}) *Kong { - fmt.Fprintf(k.Stdout, k.Model.Name+": "+format, args...) + formatMultilineMessage(k.Stdout, k.Model.Name+": ", format, args...) return k } // Errorf writes a message to Kong.Stderr with the application name prefixed. func (k *Kong) Errorf(format string, args ...interface{}) *Kong { - fmt.Fprintf(k.Stderr, k.Model.Name+": error: "+format, args...) + formatMultilineMessage(k.Stderr, k.Model.Name+": error: ", format, args...) return k } @@ -191,7 +200,7 @@ func (k *Kong) FatalIfErrorf(err error, args ...interface{}) { if len(args) > 0 { msg = fmt.Sprintf(args[0].(string), args[1:]...) + ": " + err.Error() } - k.Errorf("%s\n", msg) + k.Errorf("%s", msg) k.Exit(1) } diff --git a/kong_test.go b/kong_test.go index 8590603..1af7cbe 100644 --- a/kong_test.go +++ b/kong_test.go @@ -1,6 +1,7 @@ package kong import ( + "bytes" "strings" "testing" @@ -10,6 +11,7 @@ import ( func mustNew(t *testing.T, cli interface{}, options ...Option) *Kong { t.Helper() options = append([]Option{ + Name("test"), Exit(func(int) { t.Helper() t.Fatalf("unexpected exit()") @@ -444,3 +446,11 @@ func TestSliceWithDisabledSeparator(t *testing.T) { require.NoError(t, err) require.Equal(t, []string{"a,b", "b,c"}, cli.Flag) } + +func TestMultilineMessage(t *testing.T) { + w := &bytes.Buffer{} + var cli struct{} + p := mustNew(t, &cli, Writers(w, w)) + p.Printf("hello\nworld") + require.Equal(t, "test: hello\n world\n", w.String()) +}