diff --git a/spew/common.go b/spew/common.go index 1be8ce9..78ff8e8 100644 --- a/spew/common.go +++ b/spew/common.go @@ -62,6 +62,11 @@ var ( closeMapBytes = []byte("]") lenEqualsBytes = []byte("len=") capEqualsBytes = []byte("cap=") + highlight1StartBytes = []byte("\x1b[32m") + highlight2StartBytes = []byte("\x1b[33m") + highlight3StartBytes = []byte("\x1b[34m") + highlight4StartBytes = []byte("\x1b[36m") + highlightEndBytes = []byte("\x1b[0m") ) // hexDigits is used to map a decimal value to a hex digit. diff --git a/spew/config.go b/spew/config.go index 2e3d22f..6c87d5f 100644 --- a/spew/config.go +++ b/spew/config.go @@ -98,6 +98,10 @@ type ConfigState struct { // be spewed to strings and sorted by those strings. This is only // considered if SortKeys is true. SpewKeys bool + + // HighlightValues adds colour/color to scalar values in output. + // The colours are suitable for ANSI displays. + HighlightValues bool } // Config is the active configuration of the top-level functions. diff --git a/spew/doc.go b/spew/doc.go index aacaac6..cf23f90 100644 --- a/spew/doc.go +++ b/spew/doc.go @@ -118,6 +118,10 @@ The following configuration options are available: spewed to strings and sorted by those strings. This is only considered if SortKeys is true. + * HighlightValues + When true, values in dumps are highlighted using colours/colors + suitable for ANSI-compatible displays. + Dump Usage Simply call spew.Dump with a list of variables you want to dump: diff --git a/spew/dump.go b/spew/dump.go index f78d89f..f4f3544 100644 --- a/spew/dump.go +++ b/spew/dump.go @@ -309,6 +309,26 @@ func (d *dumpState) dump(v reflect.Value) { } } + highlightIsOn := false + if d.cs.HighlightValues { + switch kind { + case reflect.String: + highlightIsOn = true + d.w.Write(highlight1StartBytes) + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, + reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + highlightIsOn = true + d.w.Write(highlight2StartBytes) + case reflect.Bool: + highlightIsOn = true + d.w.Write(highlight3StartBytes) + case reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: + highlightIsOn = true + d.w.Write(highlight4StartBytes) + } + } + switch kind { case reflect.Invalid: // Do nothing. We should never get here since invalid has already @@ -446,6 +466,10 @@ func (d *dumpState) dump(v reflect.Value) { fmt.Fprintf(d.w, "%v", v.String()) } } + + if highlightIsOn { + d.w.Write(highlightEndBytes) + } } // fdump is a helper function to consolidate the logic from the various public diff --git a/spew/dump_test.go b/spew/dump_test.go index 4a31a2e..18b917b 100644 --- a/spew/dump_test.go +++ b/spew/dump_test.go @@ -64,6 +64,7 @@ package spew_test import ( "bytes" "fmt" + "regexp" "testing" "unsafe" @@ -1040,3 +1041,58 @@ func TestDumpSortedKeys(t *testing.T) { } } + +func TestDumpHighlightValues(t *testing.T) { + cfg := spew.ConfigState{SortKeys: true, HighlightValues: true} + col := map[string]string{ + "reset": "\x1b[0m", + "str": "\x1b[32m", + "num": "\x1b[33m", + "bool": "\x1b[34m", + "other": "\x1b[36m", + } + s := cfg.Sdump(map[int]string{1: "1", 3: "3", 2: "2"}) + expected := "(map[int]string) (len=3) {\n" + + "(int) " + col["num"] + "1" + col["reset"] + ": (string) (len=1) " + col["str"] + "\"1\"" + col["reset"] + ",\n" + + "(int) " + col["num"] + "2" + col["reset"] + ": (string) (len=1) " + col["str"] + "\"2\"" + col["reset"] + ",\n" + + "(int) " + col["num"] + "3" + col["reset"] + ": (string) (len=1) " + col["str"] + "\"3\"" + col["reset"] + "\n" + + "}\n" + if s != expected { + t.Errorf("Highlighted string mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[stringer]int{"1": 1, "3": 3, "2": 2}) + expected = "(map[spew_test.stringer]int) (len=3) {\n" + + "(spew_test.stringer) (len=1) stringer 1: (int) " + col["num"] + "1" + col["reset"] + ",\n" + + "(spew_test.stringer) (len=1) stringer 2: (int) " + col["num"] + "2" + col["reset"] + ",\n" + + "(spew_test.stringer) (len=1) stringer 3: (int) " + col["num"] + "3" + col["reset"] + "\n" + + "}\n" + if s != expected { + t.Errorf("Highlighted ints mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[string]bool{"custom1": true, "custom2": false}) + expected = "(map[string]bool) (len=2) {\n" + + "(string) (len=7) " + col["str"] + `"custom1"` + col["reset"] + ": (bool) " + col["bool"] + "true" + col["reset"] + ",\n" + + "(string) (len=7) " + col["str"] + `"custom2"` + col["reset"] + ": (bool) " + col["bool"] + "false" + col["reset"] + "\n" + + "}\n" + if s != expected { + t.Errorf("Highlighted keys mismatch:\n %v %v", s, expected) + } + + s = cfg.Sdump(map[string]chan int{"chanInt1": make(chan int), "chanInt2": make(chan int)}) + dummyPtr := "0x123456789a" + expected = "(map[string]chan int) (len=2) {\n" + + "(string) (len=8) " + col["str"] + `"chanInt1"` + col["reset"] + ": (chan int) " + col["other"] + dummyPtr + col["reset"] + ",\n" + + "(string) (len=8) " + col["str"] + `"chanInt2"` + col["reset"] + ": (chan int) " + col["other"] + dummyPtr + col["reset"] + "\n" + + "}\n" + + // replace all pointers with dummyPtr (they will be prefixed by 'm' - from col[], so cannot \b at start) to match expected + re := regexp.MustCompile(`0x[0-9a-f]{10}\b`) + s = re.ReplaceAllString(s, dummyPtr) + + if s != expected { + t.Errorf("Highlighted other mismatch:\n %v %v", s, expected) + } + +}