Skip to content

Commit 5dd4072

Browse files
authored
Merge pull request #282 from qualidafial/fprint-bytes-written
fix: include escape codes in byte counts from `Fprint`, `Fprintf`
2 parents 26fd578 + cbda2c3 commit 5dd4072

File tree

2 files changed

+98
-11
lines changed

2 files changed

+98
-11
lines changed

color.go

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,22 +220,30 @@ func (c *Color) unset() {
220220
// a low-level function, and users should use the higher-level functions, such
221221
// as color.Fprint, color.Print, etc.
222222
func (c *Color) SetWriter(w io.Writer) *Color {
223+
_, _ = c.setWriter(w)
224+
return c
225+
}
226+
227+
func (c *Color) setWriter(w io.Writer) (int, error) {
223228
if c.isNoColorSet() {
224-
return c
229+
return 0, nil
225230
}
226231

227-
fmt.Fprint(w, c.format())
228-
return c
232+
return fmt.Fprint(w, c.format())
229233
}
230234

231235
// UnsetWriter resets all escape attributes and clears the output with the give
232236
// io.Writer. Usually should be called after SetWriter().
233237
func (c *Color) UnsetWriter(w io.Writer) {
238+
_, _ = c.unsetWriter(w)
239+
}
240+
241+
func (c *Color) unsetWriter(w io.Writer) (int, error) {
234242
if c.isNoColorSet() {
235-
return
243+
return 0, nil
236244
}
237245

238-
fmt.Fprintf(w, "%s[%dm", escape, Reset)
246+
return fmt.Fprintf(w, "%s[%dm", escape, Reset)
239247
}
240248

241249
// Add is used to chain SGR parameters. Use as many as parameters to combine
@@ -251,10 +259,20 @@ func (c *Color) Add(value ...Attribute) *Color {
251259
// On Windows, users should wrap w with colorable.NewColorable() if w is of
252260
// type *os.File.
253261
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
254-
c.SetWriter(w)
255-
defer c.UnsetWriter(w)
262+
n, err = c.setWriter(w)
263+
if err != nil {
264+
return n, err
265+
}
266+
267+
nn, err := fmt.Fprint(w, a...)
268+
n += nn
269+
if err != nil {
270+
return
271+
}
256272

257-
return fmt.Fprint(w, a...)
273+
nn, err = c.unsetWriter(w)
274+
n += nn
275+
return n, err
258276
}
259277

260278
// Print formats using the default formats for its operands and writes to
@@ -274,10 +292,20 @@ func (c *Color) Print(a ...interface{}) (n int, err error) {
274292
// On Windows, users should wrap w with colorable.NewColorable() if w is of
275293
// type *os.File.
276294
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
277-
c.SetWriter(w)
278-
defer c.UnsetWriter(w)
295+
n, err = c.setWriter(w)
296+
if err != nil {
297+
return n, err
298+
}
299+
300+
nn, err := fmt.Fprintf(w, format, a...)
301+
n += nn
302+
if err != nil {
303+
return
304+
}
279305

280-
return fmt.Fprintf(w, format, a...)
306+
nn, err = c.unsetWriter(w)
307+
n += nn
308+
return n, err
281309
}
282310

283311
// Printf formats according to a format specifier and writes to standard output.

color_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77
"os"
8+
"strings"
89
"testing"
910

1011
"github.com/mattn/go-colorable"
@@ -446,6 +447,64 @@ func TestColor_Sprintln_Newline(t *testing.T) {
446447
}
447448
}
448449

450+
func TestColor_Fprint(t *testing.T) {
451+
rb := new(strings.Builder)
452+
c := New(FgRed)
453+
454+
n, err := c.Fprint(rb, "foo", "bar")
455+
if err != nil {
456+
t.Errorf("Fprint error: %v", err)
457+
}
458+
got := rb.String()
459+
want := "\x1b[31mfoobar\x1b[0m"
460+
461+
if want != got {
462+
t.Errorf("Fprint error\n\nwant: %q\n got: %q", want, got)
463+
}
464+
if n != len(got) {
465+
t.Errorf("Fprint byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
466+
}
467+
}
468+
469+
func TestColor_Fprintln(t *testing.T) {
470+
rb := new(strings.Builder)
471+
c := New(FgRed)
472+
473+
n, err := c.Fprintln(rb, "foo", "bar")
474+
if err != nil {
475+
t.Errorf("Fprint error: %v", err)
476+
}
477+
got := rb.String()
478+
want := "\x1b[31mfoo bar\x1b[0m\n"
479+
480+
if want != got {
481+
t.Errorf("Fprintln error\n\nwant: %q\n got: %q", want, got)
482+
}
483+
if n != len(got) {
484+
t.Errorf("Fprintln byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
485+
}
486+
}
487+
488+
func TestColor_Fprintf(t *testing.T) {
489+
rb := new(strings.Builder)
490+
c := New(FgRed)
491+
492+
n, err := c.Fprintf(rb, "%-7s %-7s %5d\n", "hello", "world", 123)
493+
if err != nil {
494+
t.Errorf("Fprint error: %v", err)
495+
}
496+
497+
want := "\x1b[31mhello world 123\n\x1b[0m"
498+
499+
got := rb.String()
500+
if want != got {
501+
t.Errorf("Fprintf error\n\nwant: %q\n got: %q", want, got)
502+
}
503+
if n != len(got) {
504+
t.Errorf("Fprintf byte count does not match actual bytes written\n\nwant: %d\n got: %d", len(got), n)
505+
}
506+
}
507+
449508
func TestColor_Fprintln_Newline(t *testing.T) {
450509
rb := new(bytes.Buffer)
451510
c := New(FgRed)

0 commit comments

Comments
 (0)