Implement support for %#v and %#+v in Formatter.
This commit implements feature request #3. In particular, it allows the formatter to respond to %#v and %#+v. The # flag (%#v) adds type information to the output and the combination of the # and + flags (%#+v) adds both type information and pointer information. This allows the consumer a choice between displaying types, pointer information, or both.
This commit is contained in:
parent
c5fba05307
commit
1f81f22357
|
@ -70,7 +70,7 @@ var (
|
||||||
iBytes = []byte("i")
|
iBytes = []byte("i")
|
||||||
trueBytes = []byte("true")
|
trueBytes = []byte("true")
|
||||||
falseBytes = []byte("false")
|
falseBytes = []byte("false")
|
||||||
interfaceBytes = []byte("(interface {}) ")
|
interfaceBytes = []byte("(interface {})")
|
||||||
commaNewlineBytes = []byte(",\n")
|
commaNewlineBytes = []byte(",\n")
|
||||||
newlineBytes = []byte("\n")
|
newlineBytes = []byte("\n")
|
||||||
openBraceBytes = []byte("{")
|
openBraceBytes = []byte("{")
|
||||||
|
@ -102,16 +102,6 @@ var (
|
||||||
// hexDigits is used to map a decimal value to a hex digit.
|
// hexDigits is used to map a decimal value to a hex digit.
|
||||||
var hexDigits = "0123456789abcdef"
|
var hexDigits = "0123456789abcdef"
|
||||||
|
|
||||||
// unpackValue returns values inside of non-nil interfaces when possible.
|
|
||||||
// This is useful for data types like structs, arrays, slices, and maps which
|
|
||||||
// can contain varying types packed inside an interface.
|
|
||||||
func unpackValue(v reflect.Value) reflect.Value {
|
|
||||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// catchPanic handles any panics that might occur during the handleMethods
|
// catchPanic handles any panics that might occur during the handleMethods
|
||||||
// calls.
|
// calls.
|
||||||
func catchPanic(w io.Writer, v reflect.Value) {
|
func catchPanic(w io.Writer, v reflect.Value) {
|
||||||
|
|
36
spew/doc.go
36
spew/doc.go
|
@ -51,10 +51,13 @@ information use Dump or Fdump:
|
||||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||||
|
|
||||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||||
printing style, use the convenience wrappers Printf, Fprintf, etc with either
|
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
||||||
%v (most compact) or %+v (adds pointer addresses):
|
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
||||||
|
%#+v (adds types and pointer addresses):
|
||||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
|
||||||
Configuration Options
|
Configuration Options
|
||||||
|
|
||||||
|
@ -118,31 +121,40 @@ so that it integrates cleanly with standard fmt package printing functions. The
|
||||||
formatter is useful for inline printing of smaller data types similar to the
|
formatter is useful for inline printing of smaller data types similar to the
|
||||||
standard %v format specifier.
|
standard %v format specifier.
|
||||||
|
|
||||||
The spew formatter only responds to the %v and %+v verb combinations. Any other
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
variations such as %x, %q, and %#v will be sent to the the standard fmt package
|
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||||
for formatting. In addition, the spew formatter ignores the width and precision
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
arguments (however they will still work on the format specifiers spew does not
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
handle).
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
Custom Formatter Usage
|
Custom Formatter Usage
|
||||||
|
|
||||||
The simplest way to make use of the spew custom formatter is to call one of the
|
The simplest way to make use of the spew custom formatter is to call one of the
|
||||||
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
||||||
functions have the exact same syntax you are most likely already familiar with:
|
functions have syntax you are most likely already familiar with:
|
||||||
|
|
||||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
spew.Println(myVar, myVar2)
|
spew.Println(myVar, myVar2)
|
||||||
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||||
|
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||||
|
|
||||||
See the Index for the full list convenience functions.
|
See the Index for the full list convenience functions.
|
||||||
|
|
||||||
Sample Formatter Output
|
Sample Formatter Output
|
||||||
|
|
||||||
Double pointer to a uint8 via %v:
|
Double pointer to a uint8:
|
||||||
<**>5
|
%v: <**>5
|
||||||
|
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||||
|
%#v: (**uint8)5
|
||||||
|
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||||
|
|
||||||
Circular struct with a uint8 field and a pointer to itself via %+v:
|
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||||
{ui8:1 c:<*>(0xf84002d200){ui8:1 c:<*>(0xf84002d200)<shown>}}
|
%v: <*>{1 <*><shown>}
|
||||||
|
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||||
|
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||||
|
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||||
|
|
||||||
See the Printf example for details on the setup of variables being shown
|
See the Printf example for details on the setup of variables being shown
|
||||||
here.
|
here.
|
||||||
|
|
19
spew/dump.go
19
spew/dump.go
|
@ -45,6 +45,16 @@ func (d *dumpState) pad() {
|
||||||
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unpackValue returns values inside of non-nil interfaces when possible.
|
||||||
|
// This is useful for data types like structs, arrays, slices, and maps which
|
||||||
|
// can contain varying types packed inside an interface.
|
||||||
|
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
||||||
|
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
||||||
func (d *dumpState) dumpPtr(v reflect.Value) {
|
func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||||
// Remove pointers at or below the current depth from map used to detect
|
// Remove pointers at or below the current depth from map used to detect
|
||||||
|
@ -191,7 +201,7 @@ func (d *dumpState) dump(v reflect.Value) {
|
||||||
} else {
|
} else {
|
||||||
numEntries := v.Len()
|
numEntries := v.Len()
|
||||||
for i := 0; i < numEntries; i++ {
|
for i := 0; i < numEntries; i++ {
|
||||||
d.dump(unpackValue(v.Index(i)))
|
d.dump(d.unpackValue(v.Index(i)))
|
||||||
if i < (numEntries - 1) {
|
if i < (numEntries - 1) {
|
||||||
d.w.Write(commaNewlineBytes)
|
d.w.Write(commaNewlineBytes)
|
||||||
} else {
|
} else {
|
||||||
|
@ -223,10 +233,10 @@ func (d *dumpState) dump(v reflect.Value) {
|
||||||
numEntries := v.Len()
|
numEntries := v.Len()
|
||||||
keys := v.MapKeys()
|
keys := v.MapKeys()
|
||||||
for i, key := range keys {
|
for i, key := range keys {
|
||||||
d.dump(unpackValue(key))
|
d.dump(d.unpackValue(key))
|
||||||
d.w.Write(colonSpaceBytes)
|
d.w.Write(colonSpaceBytes)
|
||||||
d.ignoreNextPad = true
|
d.ignoreNextPad = true
|
||||||
d.dump(unpackValue(v.MapIndex(key)))
|
d.dump(d.unpackValue(v.MapIndex(key)))
|
||||||
if i < (numEntries - 1) {
|
if i < (numEntries - 1) {
|
||||||
d.w.Write(commaNewlineBytes)
|
d.w.Write(commaNewlineBytes)
|
||||||
} else {
|
} else {
|
||||||
|
@ -253,7 +263,7 @@ func (d *dumpState) dump(v reflect.Value) {
|
||||||
d.w.Write([]byte(vtf.Name))
|
d.w.Write([]byte(vtf.Name))
|
||||||
d.w.Write(colonSpaceBytes)
|
d.w.Write(colonSpaceBytes)
|
||||||
d.ignoreNextPad = true
|
d.ignoreNextPad = true
|
||||||
d.dump(unpackValue(v.Field(i)))
|
d.dump(d.unpackValue(v.Field(i)))
|
||||||
if i < (numFields - 1) {
|
if i < (numFields - 1) {
|
||||||
d.w.Write(commaNewlineBytes)
|
d.w.Write(commaNewlineBytes)
|
||||||
} else {
|
} else {
|
||||||
|
@ -289,6 +299,7 @@ func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||||
for _, arg := range a {
|
for _, arg := range a {
|
||||||
if arg == nil {
|
if arg == nil {
|
||||||
w.Write(interfaceBytes)
|
w.Write(interfaceBytes)
|
||||||
|
w.Write(spaceBytes)
|
||||||
w.Write(nilAngleBytes)
|
w.Write(nilAngleBytes)
|
||||||
w.Write(newlineBytes)
|
w.Write(newlineBytes)
|
||||||
continue
|
continue
|
||||||
|
|
108
spew/format.go
108
spew/format.go
|
@ -32,11 +32,12 @@ const supportedFlags = "0-+# "
|
||||||
// be used to get a new Formatter which can be used directly as arguments
|
// be used to get a new Formatter which can be used directly as arguments
|
||||||
// in standard fmt package printing calls.
|
// in standard fmt package printing calls.
|
||||||
type formatState struct {
|
type formatState struct {
|
||||||
value interface{}
|
value interface{}
|
||||||
depth int
|
fs fmt.State
|
||||||
pointers map[uintptr]int // Holds map of points and depth they were seen at
|
depth int
|
||||||
fs fmt.State
|
pointers map[uintptr]int
|
||||||
cs *ConfigState
|
ignoreNextType bool
|
||||||
|
cs *ConfigState
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildDefaultFormat recreates the original format string without precision
|
// buildDefaultFormat recreates the original format string without precision
|
||||||
|
@ -85,10 +86,24 @@ func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
||||||
return format
|
return format
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unpackValue returns values inside of non-nil interfaces when possible and
|
||||||
|
// ensures that types for values which have been unpacked from an interface
|
||||||
|
// are displayed when the show types flag is also set.
|
||||||
|
// This is useful for data types like structs, arrays, slices, and maps which
|
||||||
|
// can contain varying types packed inside an interface.
|
||||||
|
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
||||||
|
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||||
|
f.ignoreNextType = false
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
||||||
func (f *formatState) formatPtr(v reflect.Value) {
|
func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
// Display nil if top level pointer is nil.
|
// Display nil if top level pointer is nil.
|
||||||
if v.IsNil() {
|
showTypes := f.fs.Flag('#')
|
||||||
|
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
||||||
f.fs.Write(nilAngleBytes)
|
f.fs.Write(nilAngleBytes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -101,8 +116,6 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plusSyntax := f.fs.Flag('+')
|
|
||||||
|
|
||||||
// Keep list of all dereferenced pointers to possibly show later.
|
// Keep list of all dereferenced pointers to possibly show later.
|
||||||
pointerChain := make([]uintptr, 0)
|
pointerChain := make([]uintptr, 0)
|
||||||
|
|
||||||
|
@ -123,6 +136,7 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
pointerChain = append(pointerChain, addr)
|
pointerChain = append(pointerChain, addr)
|
||||||
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
||||||
cycleFound = true
|
cycleFound = true
|
||||||
|
indirects--
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
f.pointers[addr] = f.depth
|
f.pointers[addr] = f.depth
|
||||||
|
@ -137,13 +151,23 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display indirection level.
|
// Display type or indirection level depending on flags.
|
||||||
f.fs.Write(openAngleBytes)
|
if showTypes && !f.ignoreNextType {
|
||||||
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
f.fs.Write(openParenBytes)
|
||||||
f.fs.Write(closeAngleBytes)
|
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||||
|
f.fs.Write([]byte(ve.Type().String()))
|
||||||
|
f.fs.Write(closeParenBytes)
|
||||||
|
} else {
|
||||||
|
if nilFound || cycleFound {
|
||||||
|
indirects += strings.Count(ve.Type().String(), "*")
|
||||||
|
}
|
||||||
|
f.fs.Write(openAngleBytes)
|
||||||
|
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
||||||
|
f.fs.Write(closeAngleBytes)
|
||||||
|
}
|
||||||
|
|
||||||
// Display pointer information depending on flags.
|
// Display pointer information depending on flags.
|
||||||
if plusSyntax && (len(pointerChain) > 0) {
|
if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
||||||
f.fs.Write(openParenBytes)
|
f.fs.Write(openParenBytes)
|
||||||
for i, addr := range pointerChain {
|
for i, addr := range pointerChain {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
|
@ -163,6 +187,7 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
f.fs.Write(circularShortBytes)
|
f.fs.Write(circularShortBytes)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
f.ignoreNextType = true
|
||||||
f.format(ve)
|
f.format(ve)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,9 +197,23 @@ func (f *formatState) formatPtr(v reflect.Value) {
|
||||||
// dealing with and formats it appropriately. It is a recursive function,
|
// dealing with and formats it appropriately. It is a recursive function,
|
||||||
// however circular data structures are detected and handled properly.
|
// however circular data structures are detected and handled properly.
|
||||||
func (f *formatState) format(v reflect.Value) {
|
func (f *formatState) format(v reflect.Value) {
|
||||||
|
// Handle pointers specially.
|
||||||
|
kind := v.Kind()
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
f.formatPtr(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print type information unless already handled elsewhere.
|
||||||
|
if !f.ignoreNextType && f.fs.Flag('#') {
|
||||||
|
f.fs.Write(openParenBytes)
|
||||||
|
f.fs.Write([]byte(v.Type().String()))
|
||||||
|
f.fs.Write(closeParenBytes)
|
||||||
|
}
|
||||||
|
f.ignoreNextType = false
|
||||||
|
|
||||||
// Call Stringer/error interfaces if they exist and the handle methods
|
// Call Stringer/error interfaces if they exist and the handle methods
|
||||||
// flag is enabled.
|
// flag is enabled.
|
||||||
kind := v.Kind()
|
|
||||||
if !f.cs.DisableMethods {
|
if !f.cs.DisableMethods {
|
||||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||||
if handled := handleMethods(f.cs, f.fs, v); handled {
|
if handled := handleMethods(f.cs, f.fs, v); handled {
|
||||||
|
@ -219,7 +258,8 @@ func (f *formatState) format(v reflect.Value) {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
f.fs.Write(spaceBytes)
|
f.fs.Write(spaceBytes)
|
||||||
}
|
}
|
||||||
f.format(unpackValue(v.Index(i)))
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(v.Index(i)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.depth--
|
f.depth--
|
||||||
|
@ -231,6 +271,10 @@ func (f *formatState) format(v reflect.Value) {
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
// Do nothing. We should never get here due to unpackValue calls
|
// Do nothing. We should never get here due to unpackValue calls
|
||||||
|
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Do nothing. We should never get here since pointers have already
|
||||||
|
// been handled above.
|
||||||
|
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
f.fs.Write(openMapBytes)
|
f.fs.Write(openMapBytes)
|
||||||
f.depth++
|
f.depth++
|
||||||
|
@ -242,17 +286,16 @@ func (f *formatState) format(v reflect.Value) {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
f.fs.Write(spaceBytes)
|
f.fs.Write(spaceBytes)
|
||||||
}
|
}
|
||||||
f.format(unpackValue(key))
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(key))
|
||||||
f.fs.Write(colonBytes)
|
f.fs.Write(colonBytes)
|
||||||
f.format(unpackValue(v.MapIndex(key)))
|
f.ignoreNextType = true
|
||||||
|
f.format(f.unpackValue(v.MapIndex(key)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.depth--
|
f.depth--
|
||||||
f.fs.Write(closeMapBytes)
|
f.fs.Write(closeMapBytes)
|
||||||
|
|
||||||
case reflect.Ptr:
|
|
||||||
f.formatPtr(v)
|
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
numFields := v.NumField()
|
numFields := v.NumField()
|
||||||
f.fs.Write(openBraceBytes)
|
f.fs.Write(openBraceBytes)
|
||||||
|
@ -266,11 +309,11 @@ func (f *formatState) format(v reflect.Value) {
|
||||||
f.fs.Write(spaceBytes)
|
f.fs.Write(spaceBytes)
|
||||||
}
|
}
|
||||||
vtf := vt.Field(i)
|
vtf := vt.Field(i)
|
||||||
if f.fs.Flag('+') {
|
if f.fs.Flag('+') || f.fs.Flag('#') {
|
||||||
f.fs.Write([]byte(vtf.Name))
|
f.fs.Write([]byte(vtf.Name))
|
||||||
f.fs.Write(colonBytes)
|
f.fs.Write(colonBytes)
|
||||||
}
|
}
|
||||||
f.format(unpackValue(v.Field(i)))
|
f.format(f.unpackValue(v.Field(i)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.depth--
|
f.depth--
|
||||||
|
@ -299,17 +342,21 @@ func (f *formatState) format(v reflect.Value) {
|
||||||
func (f *formatState) Format(fs fmt.State, verb rune) {
|
func (f *formatState) Format(fs fmt.State, verb rune) {
|
||||||
f.fs = fs
|
f.fs = fs
|
||||||
|
|
||||||
// Use standard formatting for verbs that are not v or #v.
|
// Use standard formatting for verbs that are not v.
|
||||||
if (verb != 'v') || (verb == 'v' && fs.Flag('#')) {
|
if verb != 'v' {
|
||||||
format := f.constructOrigFormat(verb)
|
format := f.constructOrigFormat(verb)
|
||||||
fmt.Fprintf(fs, format, f.value)
|
fmt.Fprintf(fs, format, f.value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.value == nil {
|
if f.value == nil {
|
||||||
fmt.Fprint(fs, string(nilAngleBytes))
|
if fs.Flag('#') {
|
||||||
|
fs.Write(interfaceBytes)
|
||||||
|
}
|
||||||
|
fs.Write(nilAngleBytes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f.format(reflect.ValueOf(f.value))
|
f.format(reflect.ValueOf(f.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,11 +374,12 @@ interface. As a result, it integrates cleanly with standard fmt package
|
||||||
printing functions. The formatter is useful for inline printing of smaller data
|
printing functions. The formatter is useful for inline printing of smaller data
|
||||||
types similar to the standard %v format specifier.
|
types similar to the standard %v format specifier.
|
||||||
|
|
||||||
The custom formatter only responds to the %v and %+v verb combinations. Any
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
other variations such as %x, %q, and %#v will be sent to the the standard fmt
|
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||||
package for formatting. In addition, the custom formatter ignores the width and
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
precision arguments (however they will still work on the format specifiers not
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
handled by the custom formatter).
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
Typically this function shouldn't be called directly. It is much easier to make
|
Typically this function shouldn't be called directly. It is much easier to make
|
||||||
use of the custom formatter by calling one of the convenience functions such as
|
use of the custom formatter by calling one of the convenience functions such as
|
||||||
|
|
11
spew/spew.go
11
spew/spew.go
|
@ -226,11 +226,12 @@ interface. As a result, it integrates cleanly with standard fmt package
|
||||||
printing functions. The formatter is useful for inline printing of smaller data
|
printing functions. The formatter is useful for inline printing of smaller data
|
||||||
types similar to the standard %v format specifier.
|
types similar to the standard %v format specifier.
|
||||||
|
|
||||||
The custom formatter only responds to the %v and %+v verb combinations. Any
|
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||||
other variations such as %x, %q, and %#v will be sent to the the standard fmt
|
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||||
package for formatting. In addition, the custom formatter ignores the width and
|
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||||
precision arguments (however they will still work on the format specifiers not
|
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||||
handled by the custom formatter).
|
the width and precision arguments (however they will still work on the format
|
||||||
|
specifiers not handled by the custom formatter).
|
||||||
|
|
||||||
Typically this function shouldn't be called directly. It is much easier to make
|
Typically this function shouldn't be called directly. It is much easier to make
|
||||||
use of the custom formatter by calling one of the convenience functions such as
|
use of the custom formatter by calling one of the convenience functions such as
|
||||||
|
|
Loading…
Reference in New Issue