xerrors: gobble colon and newline in edge cases We gobble newlines at the start of a detailed section, at end before a new message, but not at the end of a message. This CL add that. Also, we should not print the colon after the last non-detailed message if the detailed message consists solely of newlines that are gobbled. Change-Id: I9888253e600fa1d7a05dd61f2cc49cda4275ceb1 Reviewed-on: https://go-review.googlesource.com/c/160717 Reviewed-by: Damien Neil <dneil@google.com> Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/adaptor.go b/adaptor.go index 83a7945..4317f24 100644 --- a/adaptor.go +++ b/adaptor.go
@@ -70,8 +70,6 @@ loop: for { - p.inDetail = false - switch v := err.(type) { case Formatter: err = v.FormatError((*printer)(p)) @@ -85,15 +83,13 @@ if err == nil { break } - if !p.inDetail || !p.printDetail { + if p.needColon || !p.printDetail { p.buf.WriteByte(':') - } - // Strip last newline of detail. - if bytes.HasSuffix(p.buf.Bytes(), detailSep) { - p.buf.Truncate(p.buf.Len() - len(detailSep)) + p.needColon = false } p.buf.WriteString(sep) p.inDetail = false + p.needNewline = false } exit: @@ -135,6 +131,7 @@ printDetail bool inDetail bool + needColon bool needNewline bool } @@ -143,24 +140,32 @@ if len(b) == 0 { return 0, nil } - if s.inDetail && s.needNewline { - s.needNewline = false - s.buf.WriteByte(':') - s.buf.Write(detailSep) + if s.inDetail && s.needColon { + s.needNewline = true if b[0] == '\n' { b = b[1:] } } k := 0 for i, c := range b { + if s.needNewline { + if s.inDetail && s.needColon { + s.buf.WriteByte(':') + s.needColon = false + } + s.buf.Write(detailSep) + s.needNewline = false + } if c == '\n' { s.buf.Write(b[k:i]) - s.buf.Write(detailSep) k = i + 1 + s.needNewline = true } } s.buf.Write(b[k:]) - s.needNewline = !s.inDetail + if !s.inDetail { + s.needColon = true + } } else if !s.inDetail { s.buf.Write(b) }
diff --git a/fmt_test.go b/fmt_test.go index 8534c29..6744b8a 100644 --- a/fmt_test.go +++ b/fmt_test.go
@@ -132,6 +132,26 @@ "\n and the 12 monkeys" + "\n are laughing", }, { + err: &oneNewline{nil}, + fmt: "%+v", + want: "123", + }, { + err: &oneNewline{&oneNewline{nil}}, + fmt: "%+v", + want: "123:" + + "\n - 123", + }, { + err: &newlineAtEnd{nil}, + fmt: "%+v", + want: "newlineAtEnd:\n detail", + }, { + err: &newlineAtEnd{&newlineAtEnd{nil}}, + fmt: "%+v", + want: "newlineAtEnd:" + + "\n detail" + + "\n - newlineAtEnd:" + + "\n detail", + }, { err: framed, fmt: "%+v", want: "something:" + @@ -302,7 +322,7 @@ var ok bool if tc.regexp { var err error - ok, err = regexp.MatchString(tc.want, got) + ok, err = regexp.MatchString(tc.want+"$", got) if err != nil { t.Fatal(err) } @@ -420,6 +440,42 @@ return nil } +type oneNewline struct { + next error +} + +func (e *oneNewline) Error() string { return fmt.Sprint(e) } + +func (e *oneNewline) Format(s fmt.State, verb rune) { + xerrors.FormatError(e, s, verb) +} + +func (e *oneNewline) FormatError(p xerrors.Printer) (next error) { + p.Print("1") + p.Print("2") + p.Print("3") + p.Detail() + p.Print("\n") + return e.next +} + +type newlineAtEnd struct { + next error +} + +func (e *newlineAtEnd) Error() string { return fmt.Sprint(e) } + +func (e *newlineAtEnd) Format(s fmt.State, verb rune) { + xerrors.FormatError(e, s, verb) +} + +func (e *newlineAtEnd) FormatError(p xerrors.Printer) (next error) { + p.Print("newlineAtEnd") + p.Detail() + p.Print("detail\n") + return e.next +} + type adapted struct { msg string err error