@@ -21,14 +21,23 @@ type formatValueOptions struct {
21
21
// methods like error.Error or fmt.Stringer.String.
22
22
AvoidStringer bool
23
23
24
- // ShallowPointers controls whether to avoid descending into pointers .
24
+ // PrintShallowPointer controls whether to print the next pointer .
25
25
// Useful when printing map keys, where pointer comparison is performed
26
26
// on the pointer address rather than the pointed-at value.
27
- ShallowPointers bool
27
+ PrintShallowPointer bool
28
28
29
29
// PrintAddresses controls whether to print the address of all pointers,
30
30
// slice elements, and maps.
31
31
PrintAddresses bool
32
+
33
+ // VerbosityLevel controls the amount of output to produce.
34
+ // A higher value produces more output. A value of zero or lower produces
35
+ // no output (represented using an ellipsis).
36
+ // If LimitVerbosity is false, then the level is treated as infinite.
37
+ VerbosityLevel int
38
+
39
+ // LimitVerbosity specifies that formatting should respect VerbosityLevel.
40
+ LimitVerbosity bool
32
41
}
33
42
34
43
// FormatType prints the type as if it were wrapping s.
@@ -45,6 +54,9 @@ func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode {
45
54
default :
46
55
return s
47
56
}
57
+ if opts .DiffMode == diffIdentical {
58
+ return s // elide type for identical nodes
59
+ }
48
60
case elideType :
49
61
return s
50
62
}
@@ -86,11 +98,22 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
86
98
// Avoid calling Error or String methods on nil receivers since many
87
99
// implementations crash when doing so.
88
100
if (t .Kind () != reflect .Ptr && t .Kind () != reflect .Interface ) || ! v .IsNil () {
101
+ var prefix , strVal string
89
102
switch v := v .Interface ().(type ) {
90
103
case error :
91
- return textLine ( "e" + formatString ( v .Error ()) )
104
+ prefix , strVal = "e" , v .Error ()
92
105
case fmt.Stringer :
93
- return textLine ("s" + formatString (v .String ()))
106
+ prefix , strVal = "s" , v .String ()
107
+ }
108
+ if prefix != "" {
109
+ maxLen := len (strVal )
110
+ if opts .LimitVerbosity {
111
+ maxLen = (1 << opts .verbosity ()) << 5 // 32, 64, 128, 256, etc...
112
+ }
113
+ if len (strVal ) > maxLen + len (textEllipsis ) {
114
+ return textLine (prefix + formatString (strVal [:maxLen ]) + string (textEllipsis ))
115
+ }
116
+ return textLine (prefix + formatString (strVal ))
94
117
}
95
118
}
96
119
}
@@ -123,17 +146,33 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
123
146
case reflect .Complex64 , reflect .Complex128 :
124
147
return textLine (fmt .Sprint (v .Complex ()))
125
148
case reflect .String :
149
+ maxLen := v .Len ()
150
+ if opts .LimitVerbosity {
151
+ maxLen = (1 << opts .verbosity ()) << 5 // 32, 64, 128, 256, etc...
152
+ }
153
+ if v .Len () > maxLen + len (textEllipsis ) {
154
+ return textLine (formatString (v .String ()[:maxLen ]) + string (textEllipsis ))
155
+ }
126
156
return textLine (formatString (v .String ()))
127
157
case reflect .UnsafePointer , reflect .Chan , reflect .Func :
128
158
return textLine (formatPointer (v ))
129
159
case reflect .Struct :
130
160
var list textList
131
161
v := makeAddressable (v ) // needed for retrieveUnexportedField
162
+ maxLen := v .NumField ()
163
+ if opts .LimitVerbosity {
164
+ maxLen = ((1 << opts .verbosity ()) >> 1 ) << 2 // 0, 4, 8, 16, 32, etc...
165
+ opts .VerbosityLevel --
166
+ }
132
167
for i := 0 ; i < v .NumField (); i ++ {
133
168
vv := v .Field (i )
134
169
if value .IsZero (vv ) {
135
170
continue // Elide fields with zero values
136
171
}
172
+ if len (list ) == maxLen {
173
+ list .AppendEllipsis (diffStats {})
174
+ break
175
+ }
137
176
sf := t .Field (i )
138
177
if supportExporters && ! isExported (sf .Name ) {
139
178
vv = retrieveUnexportedField (v , sf , true )
@@ -147,12 +186,21 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
147
186
return textNil
148
187
}
149
188
if opts .PrintAddresses {
150
- ptr = formatPointer ( v )
189
+ ptr = fmt . Sprintf ( "⟪ptr:0x%x, len:%d, cap:%d⟫" , pointerValue ( v ), v . Len (), v . Cap () )
151
190
}
152
191
fallthrough
153
192
case reflect .Array :
193
+ maxLen := v .Len ()
194
+ if opts .LimitVerbosity {
195
+ maxLen = ((1 << opts .verbosity ()) >> 1 ) << 2 // 0, 4, 8, 16, 32, etc...
196
+ opts .VerbosityLevel --
197
+ }
154
198
var list textList
155
199
for i := 0 ; i < v .Len (); i ++ {
200
+ if len (list ) == maxLen {
201
+ list .AppendEllipsis (diffStats {})
202
+ break
203
+ }
156
204
vi := v .Index (i )
157
205
if vi .CanAddr () { // Check for cyclic elements
158
206
p := vi .Addr ()
@@ -177,8 +225,17 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
177
225
return textLine (formatPointer (v ))
178
226
}
179
227
228
+ maxLen := v .Len ()
229
+ if opts .LimitVerbosity {
230
+ maxLen = ((1 << opts .verbosity ()) >> 1 ) << 2 // 0, 4, 8, 16, 32, etc...
231
+ opts .VerbosityLevel --
232
+ }
180
233
var list textList
181
234
for _ , k := range value .SortKeys (v .MapKeys ()) {
235
+ if len (list ) == maxLen {
236
+ list .AppendEllipsis (diffStats {})
237
+ break
238
+ }
182
239
sk := formatMapKey (k )
183
240
sv := opts .WithTypeMode (elideType ).FormatValue (v .MapIndex (k ), false , m )
184
241
list = append (list , textRecord {Key : sk , Value : sv })
@@ -191,11 +248,12 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
191
248
if v .IsNil () {
192
249
return textNil
193
250
}
194
- if m .Visit (v ) || opts . ShallowPointers {
251
+ if m .Visit (v ) {
195
252
return textLine (formatPointer (v ))
196
253
}
197
- if opts .PrintAddresses {
254
+ if opts .PrintAddresses || opts . PrintShallowPointer {
198
255
ptr = formatPointer (v )
256
+ opts .PrintShallowPointer = false
199
257
}
200
258
skipType = true // Let the underlying value print the type instead
201
259
return textWrap {"&" + ptr , opts .FormatValue (v .Elem (), false , m ), "" }
@@ -217,7 +275,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, withinSlice bool, m visit
217
275
func formatMapKey (v reflect.Value ) string {
218
276
var opts formatOptions
219
277
opts .TypeMode = elideType
220
- opts .ShallowPointers = true
278
+ opts .PrintShallowPointer = true
221
279
s := opts .FormatValue (v , false , visitedPointers {}).String ()
222
280
return strings .TrimSpace (s )
223
281
}
@@ -268,11 +326,14 @@ func formatHex(u uint64) string {
268
326
269
327
// formatPointer prints the address of the pointer.
270
328
func formatPointer (v reflect.Value ) string {
329
+ return fmt .Sprintf ("⟪0x%x⟫" , pointerValue (v ))
330
+ }
331
+ func pointerValue (v reflect.Value ) uintptr {
271
332
p := v .Pointer ()
272
333
if flags .Deterministic {
273
334
p = 0xdeadf00f // Only used for stable testing purposes
274
335
}
275
- return fmt . Sprintf ( "⟪0x%x⟫" , p )
336
+ return p
276
337
}
277
338
278
339
type visitedPointers map [value.Pointer ]struct {}
0 commit comments