refactor
This commit is contained in:
parent
1becff5c5d
commit
598c0f3ff1
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
Copyright 2020 Arpad Ryszka
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
||||
to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of
|
||||
the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
149
README.md
149
README.md
@ -1,7 +1,7 @@
|
||||
# Notation - Print Go objects
|
||||
# Notation - print Go objects
|
||||
|
||||
This package can be used to print (or sprint) Go objects with optional wrapping (indentation) and optional type
|
||||
information for debugging purposes.
|
||||
This package can be used to print (or sprint) Go objects for debugging purposes, with optional wrapping
|
||||
(indentation) and optional type information.
|
||||
|
||||
### Alternatives
|
||||
|
||||
@ -11,7 +11,7 @@ Notation is similar to the following great, more established and mature packages
|
||||
- [litter](https://github.com/sanity-io/litter)
|
||||
- [utter](https://github.com/kortschak/utter)
|
||||
|
||||
Notation differs from these primarily in the 'flavour' of printing and the package interface.
|
||||
Notation differs from these primarily in the 'flavor' of printing and the package interface.
|
||||
|
||||
### Installation
|
||||
|
||||
@ -47,7 +47,7 @@ For the available functions, see also the [godoc](https://godoc.org/github.com/a
|
||||
|
||||
Assuming to have the required types defined, if we do the following:
|
||||
|
||||
```go
|
||||
```
|
||||
b := bike{
|
||||
frame: frame{
|
||||
fork: fork{},
|
||||
@ -87,12 +87,17 @@ s := notation.Sprintw(b)
|
||||
|
||||
We get the following string:
|
||||
|
||||
```go
|
||||
```
|
||||
{
|
||||
frame: {
|
||||
fork: {
|
||||
wheel: {size: 70, cassette: nil},
|
||||
handlebar: {levers: []{{withShift: true}, {withShift: true}}},
|
||||
handlebar: {
|
||||
levers: []{
|
||||
{withShift: true},
|
||||
{withShift: true},
|
||||
},
|
||||
},
|
||||
frontBrake: {discSize: 160},
|
||||
},
|
||||
saddlePost: {saddle: {}},
|
||||
@ -111,7 +116,10 @@ We get the following string:
|
||||
chain: {},
|
||||
levers: []{{withShift: true}, {withShift: true}},
|
||||
},
|
||||
wheels: []{{size: 70, cassette: nil}, {size: 70, cassette: {wheels: 11, chain: {}}}},
|
||||
wheels: []{
|
||||
{size: 70, cassette: nil},
|
||||
{size: 70, cassette: {wheels: 11, chain: {}}},
|
||||
},
|
||||
handlebar: {levers: []{{withShift: true}, {withShift: true}}},
|
||||
saddle: {},
|
||||
}
|
||||
@ -119,11 +127,14 @@ We get the following string:
|
||||
|
||||
Using `notation.Sprintwv` instead of `notation.Sprintw`, we would get the following string:
|
||||
|
||||
```go
|
||||
```
|
||||
bike{
|
||||
frame: frame{
|
||||
fork: fork{
|
||||
wheel: *wheel{size: float64(70), cassette: (*cassette)(nil)},
|
||||
wheel: *wheel{
|
||||
size: float64(70),
|
||||
cassette: (*cassette)(nil),
|
||||
},
|
||||
handlebar: *handlebar{
|
||||
levers: []*lever{
|
||||
*lever{withShift: bool(true)},
|
||||
@ -133,32 +144,63 @@ bike{
|
||||
frontBrake: *brake{discSize: float64(160)},
|
||||
},
|
||||
saddlePost: saddlePost{saddle: *saddle{}},
|
||||
bottomBracket: *bracket{crank: *crank{wheels: int(2), chain: *chain{}}},
|
||||
bottomBracket: *bracket{
|
||||
crank: *crank{
|
||||
wheels: int(2),
|
||||
chain: *chain{},
|
||||
},
|
||||
},
|
||||
frontDerailleur: *derailleur{gears: int(2)},
|
||||
rearDerailleur: *derailleur{gears: int(11)},
|
||||
rearBrake: *brake{discSize: float64(140)},
|
||||
rearWheel: *wheel{
|
||||
size: float64(70),
|
||||
cassette: *cassette{wheels: int(11), chain: *chain{}},
|
||||
cassette: *cassette{
|
||||
wheels: int(11),
|
||||
chain: *chain{},
|
||||
},
|
||||
},
|
||||
},
|
||||
driveTrain: driveTrain{
|
||||
bottomBracket: bracket{crank: *crank{wheels: int(2), chain: *chain{}}},
|
||||
bottomBracket: bracket{
|
||||
crank: *crank{
|
||||
wheels: int(2),
|
||||
chain: *chain{},
|
||||
},
|
||||
},
|
||||
crank: crank{wheels: int(2), chain: *chain{}},
|
||||
brakes: []brake{brake{discSize: float64(160)}, brake{discSize: float64(140)}},
|
||||
brakes: []brake{
|
||||
brake{discSize: float64(160)},
|
||||
brake{discSize: float64(140)},
|
||||
},
|
||||
derailleurs: []derailleur{
|
||||
derailleur{gears: int(2)},
|
||||
derailleur{gears: int(11)},
|
||||
derailleur{
|
||||
gears: int(2),
|
||||
},
|
||||
derailleur{
|
||||
gears: int(11),
|
||||
},
|
||||
},
|
||||
cassette: cassette{wheels: int(11), chain: *chain{}},
|
||||
chain: chain{},
|
||||
levers: []lever{lever{withShift: bool(true)}, lever{withShift: bool(true)}},
|
||||
levers: []lever{
|
||||
lever{withShift: bool(true)},
|
||||
lever{withShift: bool(true)},
|
||||
},
|
||||
},
|
||||
wheels: []wheel{
|
||||
wheel{size: float64(70), cassette: (*cassette)(nil)},
|
||||
wheel{size: float64(70), cassette: *cassette{wheels: int(11), chain: *chain{}}},
|
||||
wheel{
|
||||
size: float64(70),
|
||||
cassette: *cassette{wheels: int(11), chain: *chain{}},
|
||||
},
|
||||
},
|
||||
handlebar: handlebar{
|
||||
levers: []*lever{
|
||||
*lever{withShift: bool(true)},
|
||||
*lever{withShift: bool(true)},
|
||||
},
|
||||
},
|
||||
handlebar: handlebar{levers: []*lever{*lever{withShift: bool(true)}, *lever{withShift: bool(true)}}},
|
||||
saddle: saddle{},
|
||||
}
|
||||
```
|
||||
@ -172,28 +214,28 @@ input objects.
|
||||
##### Numbers
|
||||
|
||||
Numbers are printed based on the `fmt` package's default formatting. When printing with moderate type
|
||||
information, the type for the `int`, default witdth signed integers, will be omitted.
|
||||
information, the type for the `int`, default width signed integers, will be omitted.
|
||||
|
||||
```go
|
||||
```
|
||||
i := 42
|
||||
notation.Printlnt(i)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
42
|
||||
```
|
||||
|
||||
##### Strings
|
||||
|
||||
When printing strings, by default they are escaped using the `strconv.Quote` function. However, when wrapping
|
||||
long strings, and the string contains a newline and doesn't contain a backquote '`', then the string is printed
|
||||
as a raw string literal, delimited by '`'.
|
||||
long strings, and the string contains a newline and doesn't contain a backquote, then the string is printed
|
||||
as a raw string literal, delimited by backquotes.
|
||||
|
||||
Short string:
|
||||
|
||||
```go
|
||||
```
|
||||
s := `foobar
|
||||
baz`
|
||||
notation.Printlnw(s)
|
||||
@ -201,13 +243,13 @@ notation.Printlnw(s)
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
"foobar\nbaz"
|
||||
```
|
||||
|
||||
Long string:
|
||||
|
||||
```go
|
||||
```
|
||||
s := `The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
|
||||
quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
|
||||
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps
|
||||
@ -220,7 +262,7 @@ notation.Println(s)
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
`The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
|
||||
quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
|
||||
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps
|
||||
@ -234,28 +276,28 @@ quick brown fox jumps over the lazy dog.`
|
||||
Slices are are printed by printing their elements between braces, prefixed either by '[]' or the type of the
|
||||
slice. Example:
|
||||
|
||||
```go
|
||||
```
|
||||
l := []int{1, 2, 3}
|
||||
notation.Println(l)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
[]{1, 2, 3}
|
||||
```
|
||||
|
||||
To differentiate arrays from slices, arrays are always prefixed with their type or square brackets containing
|
||||
the length of the array:
|
||||
|
||||
```go
|
||||
```
|
||||
a := [...]{1, 2, 3}
|
||||
notation.Println(a)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
[3]{1, 2, 3}
|
||||
```
|
||||
|
||||
@ -264,7 +306,7 @@ Output:
|
||||
When the type of a slice is `uint8`, or an alias of it, e.g. `byte`, then it is printed as []byte, with the hexa
|
||||
representation of its bytes:
|
||||
|
||||
```go
|
||||
```
|
||||
b := []byte(
|
||||
`The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
|
||||
quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
|
||||
@ -279,7 +321,7 @@ notation.Printlnwt(b)
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
[]byte{
|
||||
54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a
|
||||
75 6d 70 73 20 6f 76 65 72 20 74 68 65 20 6c 61 7a 79 20 64 6f
|
||||
@ -314,14 +356,14 @@ Output:
|
||||
|
||||
Maps are printed with their entries sorted by the string representation of their keys:
|
||||
|
||||
```go
|
||||
```
|
||||
m := map[string]int{"b": 1, "c": 2, "a": 3}
|
||||
notation.Println(m)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
map{"a": 3, "b": 1, "c": 2}
|
||||
```
|
||||
|
||||
@ -330,42 +372,42 @@ disabled via the `MAPSORT=0` environment variable.
|
||||
|
||||
##### Hidden values: channels, functions
|
||||
|
||||
Certain values, like channels and functions are printed without expanding their internals, e.g. channee state or
|
||||
Certain values, like channels and functions are printed without expanding their internals, e.g. channel state or
|
||||
function body. When printing with types, the signature of these objects is printed:
|
||||
|
||||
```go
|
||||
```
|
||||
f := func(int) int { return 42 }
|
||||
notation.Println(f)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
func()
|
||||
```
|
||||
|
||||
With types:
|
||||
|
||||
```go
|
||||
```
|
||||
f := func(int) int { return 42 }
|
||||
notation.Printlnt(f)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
func(int) int
|
||||
```
|
||||
|
||||
##### Wrapping
|
||||
|
||||
Whe 'w' variant of the printing functions wraps the output with Go style indentation where the lines would be
|
||||
The 'w' variant of the printing functions wraps the output with Go style indentation where the lines would be
|
||||
too long otherwise. The wrapping is not eager, it only aims for fitting the lines on 72 columns. To measure the
|
||||
indentation, it assumes 8 character width tabs. In certain cases, it tolerates longer lines up to 112 columns,
|
||||
when the output would probably more readable that way. Of course, readability is subjective.
|
||||
|
||||
As a hidden feature, when it's really necessary, it is possible to change the above control values via
|
||||
environmetn variables. TABWIDTH controls the measuring of the indentation. LINEWIDTH sets the aimed column width
|
||||
environment variables. TABWIDTH controls the measuring of the indentation. LINEWIDTH sets the aimed column width
|
||||
of the printed lines. LINEWIDTH1 sets the tolerated threshold for those lines that are allowed to exceed the
|
||||
default line width. E.g. if somebody uses two-character wide tabs in their console, they can use the package
|
||||
like this:
|
||||
@ -374,7 +416,7 @@ like this:
|
||||
TABWIDTH=2 go test -v -count 1
|
||||
```
|
||||
|
||||
As a consequence, it is also possible to wrap all lines:
|
||||
As a consequence, it is also possible to forcibly wrap all lines:
|
||||
|
||||
```
|
||||
TABWIDTH=0 LINEWIDTH=0 LINEWIDTH1=0 go test -v -count 1
|
||||
@ -388,7 +430,7 @@ type. The package path is not printed.
|
||||
|
||||
Named type:
|
||||
|
||||
```go
|
||||
```
|
||||
type t struct{foo int}
|
||||
v := t{42}
|
||||
notation.Printlnt(v)
|
||||
@ -396,43 +438,43 @@ notation.Printlnt(v)
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
t{foo: 42}
|
||||
```
|
||||
|
||||
Unnamed type:
|
||||
|
||||
```go
|
||||
```
|
||||
v := struct{foo int}{42}
|
||||
notation.Printlnt(v)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
struct{foo int}{foo: 42}
|
||||
```
|
||||
|
||||
Using the 't' suffixed variants of the printing functions, displaying only moderately verbose type information,
|
||||
the types of certain values is omitted, where it can be inferred from the context:
|
||||
|
||||
```go
|
||||
```
|
||||
v := []struct{foo int}{{42}, {84}}
|
||||
notation.Printlnt(os.Stdout, v)
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
[]struct{foo int}{{foo: 42}, {foo: 84}}
|
||||
```
|
||||
|
||||
##### Cyclic references
|
||||
|
||||
Cyclic references are detected based on an approach similar to the one in the stdlib's reflect.DeepEqual
|
||||
function. Such occurences are displayed in the output with references:
|
||||
function. Such occurrences are displayed in the output with references:
|
||||
|
||||
```go
|
||||
```
|
||||
l := []interface{}{"foo"}
|
||||
l[0] = l
|
||||
notation.Fprint(os.Stdout, l)
|
||||
@ -440,6 +482,7 @@ notation.Fprint(os.Stdout, l)
|
||||
|
||||
Output:
|
||||
|
||||
```go
|
||||
```
|
||||
r0=[]{r0}
|
||||
```
|
||||
|
||||
|
@ -100,7 +100,12 @@ func Example() {
|
||||
// frame: {
|
||||
// fork: {
|
||||
// wheel: {size: 70, cassette: nil},
|
||||
// handlebar: {levers: []{{withShift: true}, {withShift: true}}},
|
||||
// handlebar: {
|
||||
// levers: []{
|
||||
// {withShift: true},
|
||||
// {withShift: true},
|
||||
// },
|
||||
// },
|
||||
// frontBrake: {discSize: 160},
|
||||
// },
|
||||
// saddlePost: {saddle: {}},
|
||||
@ -119,7 +124,10 @@ func Example() {
|
||||
// chain: {},
|
||||
// levers: []{{withShift: true}, {withShift: true}},
|
||||
// },
|
||||
// wheels: []{{size: 70, cassette: nil}, {size: 70, cassette: {wheels: 11, chain: {}}}},
|
||||
// wheels: []{
|
||||
// {size: 70, cassette: nil},
|
||||
// {size: 70, cassette: {wheels: 11, chain: {}}},
|
||||
// },
|
||||
// handlebar: {levers: []{{withShift: true}, {withShift: true}}},
|
||||
// saddle: {},
|
||||
// }
|
||||
|
345
fprint.go
345
fprint.go
@ -1,183 +1,222 @@
|
||||
package notation
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func unwrappable(n node) bool {
|
||||
return n.len == n.wrapLen.max &&
|
||||
n.len == n.fullWrap.max
|
||||
func ifZero(a, b int) int {
|
||||
if a == 0 {
|
||||
return b
|
||||
}
|
||||
|
||||
func initialize(t *int, v int) {
|
||||
if *t > 0 {
|
||||
return
|
||||
return a
|
||||
}
|
||||
|
||||
*t = v
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
|
||||
func max(t *int, v int) {
|
||||
if *t >= v {
|
||||
return
|
||||
return b
|
||||
}
|
||||
|
||||
*t = v
|
||||
func strLen(s str) str {
|
||||
l := strings.Split(s.raw, "\n")
|
||||
for j, li := range l {
|
||||
if j == 0 {
|
||||
s.rawLen.first = len(li)
|
||||
}
|
||||
|
||||
if len(li) > s.rawLen.max {
|
||||
s.rawLen.max = len(li)
|
||||
}
|
||||
|
||||
if j == len(l)-1 {
|
||||
s.rawLen.last = len(li)
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func nodeLen(t int, n node) node {
|
||||
var w, f int
|
||||
for i, p := range n.parts {
|
||||
switch part := p.(type) {
|
||||
case string:
|
||||
n.len += len(part)
|
||||
w += len(part)
|
||||
f += len(part)
|
||||
case str:
|
||||
// We assume here that an str is always contained by a node that has only a
|
||||
// single str.
|
||||
// We assume here that an str is always contained
|
||||
// by a node that has only a single str.
|
||||
//
|
||||
// If this changes in the future, then we need to provide tests for the
|
||||
// additional cases. If this doesn't change anytime soon, then we can
|
||||
// refactor this part.
|
||||
//
|
||||
n.len = len(part.val)
|
||||
if part.raw == "" {
|
||||
w = len(part.val)
|
||||
f = len(part.val)
|
||||
} else {
|
||||
lines := strings.Split(part.raw, "\n")
|
||||
part.rawLen.first = len(lines[0])
|
||||
for _, line := range lines {
|
||||
if len(line) > part.rawLen.max {
|
||||
part.rawLen.max = len(line)
|
||||
}
|
||||
if s, ok := n.parts[0].(str); ok {
|
||||
s = strLen(s)
|
||||
n.parts[0] = s
|
||||
n.len = len(s.val)
|
||||
if s.raw == "" {
|
||||
wl := wrapLen{
|
||||
first: len(s.val),
|
||||
max: len(s.val),
|
||||
last: len(s.val),
|
||||
}
|
||||
|
||||
part.rawLen.last = len(lines[len(lines)-1])
|
||||
n.parts[i] = part
|
||||
n.wrapLen.first = part.rawLen.first
|
||||
n.fullWrap.first = part.rawLen.first
|
||||
n.wrapLen.max = part.rawLen.max
|
||||
n.fullWrap.max = part.rawLen.max
|
||||
w = part.rawLen.last
|
||||
f = part.rawLen.last
|
||||
n.wrapLen = wl
|
||||
n.fullWrap = wl
|
||||
return n
|
||||
}
|
||||
|
||||
n.wrapLen = s.rawLen
|
||||
n.fullWrap = s.rawLen
|
||||
return n
|
||||
}
|
||||
|
||||
for i := range n.parts {
|
||||
switch pt := n.parts[i].(type) {
|
||||
case node:
|
||||
part = nodeLen(t, part)
|
||||
n.parts[i] = part
|
||||
n.len += part.len
|
||||
if unwrappable(part) {
|
||||
w += part.len
|
||||
f += part.len
|
||||
continue
|
||||
}
|
||||
|
||||
if part.len == part.wrapLen.max {
|
||||
w += part.len
|
||||
} else {
|
||||
w += part.wrapLen.first
|
||||
initialize(&n.wrapLen.first, w)
|
||||
max(&n.wrapLen.max, w)
|
||||
w = part.wrapLen.last
|
||||
}
|
||||
|
||||
f += part.fullWrap.first
|
||||
initialize(&n.fullWrap.first, f)
|
||||
max(&n.fullWrap.max, f)
|
||||
f = part.fullWrap.last
|
||||
n.parts[i] = nodeLen(t, pt)
|
||||
case wrapper:
|
||||
if len(part.items) == 0 {
|
||||
for j := range pt.items {
|
||||
pt.items[j] = nodeLen(t, pt.items[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range n.parts {
|
||||
switch pt := p.(type) {
|
||||
case node:
|
||||
n.len += pt.len
|
||||
case wrapper:
|
||||
if len(pt.items) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
initialize(&n.wrapLen.first, w)
|
||||
max(&n.wrapLen.max, w)
|
||||
initialize(&n.fullWrap.first, f)
|
||||
max(&n.fullWrap.max, f)
|
||||
w, f = 0, 0
|
||||
n.len += (len(part.items) - 1) * len(part.sep)
|
||||
if part.mode == line {
|
||||
w += (len(part.items) - 1) * len(part.sep)
|
||||
n.len += (len(pt.items) - 1) * len(pt.sep)
|
||||
for _, pti := range pt.items {
|
||||
n.len += pti.len
|
||||
}
|
||||
|
||||
for j, item := range part.items {
|
||||
item = nodeLen(t, item)
|
||||
part.items[j] = item
|
||||
n.len += item.len
|
||||
switch part.mode {
|
||||
case line:
|
||||
w += item.len
|
||||
max(&f, item.len)
|
||||
default:
|
||||
wj := t + item.len + len(part.suffix)
|
||||
max(&w, wj)
|
||||
fj := t + item.fullWrap.max
|
||||
max(&f, fj)
|
||||
fj = t + item.fullWrap.last + len(part.suffix)
|
||||
max(&f, fj)
|
||||
n.len += len(fmt.Sprint(p))
|
||||
}
|
||||
}
|
||||
|
||||
max(&n.wrapLen.max, w)
|
||||
max(&n.fullWrap.max, f)
|
||||
w, f = 0, 0
|
||||
var w, f int
|
||||
for _, p := range n.parts {
|
||||
switch pt := p.(type) {
|
||||
case node:
|
||||
w += pt.wrapLen.first
|
||||
if pt.len != pt.wrapLen.first {
|
||||
n.wrapLen.first = ifZero(n.wrapLen.first, w)
|
||||
n.wrapLen.max = max(n.wrapLen.max, w)
|
||||
n.wrapLen.max = max(n.wrapLen.max, pt.wrapLen.max)
|
||||
w = pt.wrapLen.last
|
||||
}
|
||||
|
||||
f += pt.fullWrap.first
|
||||
if pt.len != pt.fullWrap.first {
|
||||
n.fullWrap.first = ifZero(n.fullWrap.first, f)
|
||||
n.fullWrap.max = max(n.fullWrap.max, f)
|
||||
n.fullWrap.max = max(n.fullWrap.max, pt.fullWrap.max)
|
||||
f = pt.fullWrap.last
|
||||
}
|
||||
case wrapper:
|
||||
if len(pt.items) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
n.wrapLen.first = ifZero(n.wrapLen.first, w)
|
||||
n.wrapLen.max = max(n.wrapLen.max, w)
|
||||
n.fullWrap.first = ifZero(n.fullWrap.first, f)
|
||||
n.fullWrap.max = max(n.fullWrap.max, f)
|
||||
w = 0
|
||||
f = 0
|
||||
switch pt.mode {
|
||||
case line:
|
||||
// line wrapping is flexible, here
|
||||
// we measure the longest case
|
||||
//
|
||||
w = (len(pt.items) - 1) * len(pt.sep)
|
||||
for _, pti := range pt.items {
|
||||
w += pti.len
|
||||
}
|
||||
|
||||
// here me measure the shortest
|
||||
// possible case
|
||||
//
|
||||
for _, pti := range pt.items {
|
||||
f = max(f, pti.fullWrap.max)
|
||||
}
|
||||
default:
|
||||
// for non-full wrap, we measure the full
|
||||
// length of the items
|
||||
//
|
||||
for _, pti := range pt.items {
|
||||
w = max(w, t+pti.len+len(pt.suffix))
|
||||
}
|
||||
|
||||
// for full wrap, we measure the fully
|
||||
// wrapped length of the items
|
||||
//
|
||||
for _, pti := range pt.items {
|
||||
f = max(f, t+pti.fullWrap.max)
|
||||
f = max(f, t+pti.fullWrap.last+len(pt.suffix))
|
||||
}
|
||||
}
|
||||
|
||||
initialize(&n.wrapLen.first, w)
|
||||
max(&n.wrapLen.max, w)
|
||||
n.wrapLen.max = max(n.wrapLen.max, w)
|
||||
n.fullWrap.max = max(n.fullWrap.max, f)
|
||||
w = 0
|
||||
f = 0
|
||||
default:
|
||||
w += len(fmt.Sprint(p))
|
||||
f += len(fmt.Sprint(p))
|
||||
}
|
||||
}
|
||||
|
||||
n.wrapLen.first = ifZero(n.wrapLen.first, w)
|
||||
n.wrapLen.max = max(n.wrapLen.max, w)
|
||||
n.wrapLen.last = w
|
||||
initialize(&n.fullWrap.first, f)
|
||||
max(&n.fullWrap.max, f)
|
||||
n.fullWrap.first = ifZero(n.fullWrap.first, f)
|
||||
n.fullWrap.max = max(n.fullWrap.max, f)
|
||||
n.fullWrap.last = f
|
||||
return n
|
||||
}
|
||||
|
||||
func wrapNode(t, cf0, c0, c1 int, n node) node {
|
||||
// fits:
|
||||
if n.len <= c0 {
|
||||
return n
|
||||
}
|
||||
|
||||
// we don't want to make it longer:
|
||||
if n.wrapLen.max >= n.len && n.fullWrap.max >= n.len {
|
||||
return n
|
||||
}
|
||||
|
||||
if n.len <= c1 && n.len-c0 <= n.wrapLen.max {
|
||||
// tolerate below c1 when it's not worth wrapping:
|
||||
if n.len <= c1 && n.len-c0 <= c0-n.wrapLen.max {
|
||||
return n
|
||||
}
|
||||
|
||||
n.wrap = true
|
||||
|
||||
// We assume here that an str is always contained
|
||||
// by a node that has only a single str.
|
||||
//
|
||||
if s, ok := n.parts[0].(str); ok {
|
||||
s.useRaw = s.raw != ""
|
||||
n.parts[0] = s
|
||||
return n
|
||||
}
|
||||
|
||||
cc0, cc1 := c0, c1
|
||||
lastWrapperIndex := -1
|
||||
var trackBack bool
|
||||
for i := 0; i < len(n.parts); i++ {
|
||||
p := n.parts[i]
|
||||
switch part := p.(type) {
|
||||
case string:
|
||||
cc0 -= len(part)
|
||||
cc1 -= len(part)
|
||||
if !trackBack && cc1 < 0 {
|
||||
cc0 = 0
|
||||
cc1 = 0
|
||||
i = lastWrapperIndex
|
||||
trackBack = true
|
||||
}
|
||||
case str:
|
||||
// We assume here that an str is always contained by a node that has only a
|
||||
// single str. Therefore we don't need to trackback to here, because the
|
||||
// decision on wrapping was already made for the node.
|
||||
//
|
||||
// If this changes in the future, then we need to provide tests for the
|
||||
// additional cases. If this doesn't change anytime soon, then we can
|
||||
// refactor this part.
|
||||
//
|
||||
part.useRaw = part.raw != ""
|
||||
n.parts[i] = part
|
||||
case node:
|
||||
part = wrapNode(t, cf0, cc0, cc1, part)
|
||||
n.parts[i] = part
|
||||
if part.wrap {
|
||||
// This is an approximation: sometimes part.fullWrap.first should be applied
|
||||
// This is an approximation: sometimes
|
||||
// part.fullWrap.last should be applied
|
||||
// here, but usually those are the same.
|
||||
//
|
||||
cc0 -= part.wrapLen.first
|
||||
cc1 -= part.wrapLen.first
|
||||
} else {
|
||||
@ -185,12 +224,26 @@ func wrapNode(t, cf0, c0, c1 int, n node) node {
|
||||
cc1 -= part.len
|
||||
}
|
||||
|
||||
if !trackBack && cc1 < 0 {
|
||||
cc0 = 0
|
||||
cc1 = 0
|
||||
if cc1 >= 0 {
|
||||
if part.wrap {
|
||||
cc0 = c0 - part.wrapLen.last
|
||||
cc1 = c1 - part.wrapLen.last
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if trackBack {
|
||||
continue
|
||||
}
|
||||
|
||||
// trackback from after the last wrapper:
|
||||
i = lastWrapperIndex
|
||||
trackBack = true
|
||||
}
|
||||
|
||||
// force wrapping during trackback:
|
||||
cc0 = 0
|
||||
cc1 = 0
|
||||
case wrapper:
|
||||
if len(part.items) == 0 {
|
||||
continue
|
||||
@ -201,37 +254,50 @@ func wrapNode(t, cf0, c0, c1 int, n node) node {
|
||||
lastWrapperIndex = i
|
||||
switch part.mode {
|
||||
case line:
|
||||
// we only set the line endings. We use
|
||||
// the full column width:
|
||||
//
|
||||
cl := cf0 - t
|
||||
var w int
|
||||
for j, ni := range part.items {
|
||||
if w > 0 && w+len(part.sep)+ni.len > cl {
|
||||
for j, nj := range part.items {
|
||||
if w > 0 && w+len(part.sep)+nj.len > cl {
|
||||
part.lineEnds = append(part.lineEnds, j)
|
||||
w = 0
|
||||
part.lineEnds = append(
|
||||
part.lineEnds,
|
||||
j,
|
||||
)
|
||||
}
|
||||
|
||||
if w > 0 {
|
||||
w += len(part.sep)
|
||||
}
|
||||
|
||||
w += ni.len
|
||||
w += nj.len
|
||||
}
|
||||
|
||||
part.lineEnds = append(part.lineEnds, len(part.items))
|
||||
n.parts[i] = part
|
||||
default:
|
||||
for j := range part.items {
|
||||
part.items[j] = wrapNode(
|
||||
t,
|
||||
cf0,
|
||||
c0-t,
|
||||
c1-t,
|
||||
part.items[j],
|
||||
)
|
||||
part.items[j] = wrapNode(t, cf0, c0-t, c1-t, part.items[j])
|
||||
}
|
||||
}
|
||||
default:
|
||||
s := fmt.Sprint(part)
|
||||
cc0 -= len(s)
|
||||
cc1 -= len(s)
|
||||
if cc1 >= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if trackBack {
|
||||
continue
|
||||
}
|
||||
|
||||
// trackback from after the last wrapper:
|
||||
i = lastWrapperIndex
|
||||
trackBack = true
|
||||
|
||||
// force wrapping during trackback:
|
||||
cc0 = 0
|
||||
cc1 = 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,8 +343,7 @@ func fprint(w *writer, t int, n node) {
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
w.blankLine()
|
||||
w.tabs(1)
|
||||
w.line(1)
|
||||
for i, ni := range line {
|
||||
if i > 0 {
|
||||
w.write(part.sep)
|
||||
|
47
notation.go
47
notation.go
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Package notation can be used to print (or sprint) Go objects with optional wrapping (pretty printing) and
|
||||
Package notation can be used to print (or sprint) Go objects with optional wrapping (and indentation) and
|
||||
optional type information.
|
||||
*/
|
||||
package notation
|
||||
@ -40,18 +40,18 @@ type pending struct {
|
||||
}
|
||||
|
||||
type node struct {
|
||||
parts []interface{}
|
||||
len int
|
||||
wrapLen wrapLen
|
||||
fullWrap wrapLen
|
||||
wrap bool
|
||||
parts []interface{}
|
||||
}
|
||||
|
||||
type str struct {
|
||||
val string
|
||||
raw string
|
||||
useRaw bool
|
||||
rawLen wrapLen
|
||||
useRaw bool
|
||||
}
|
||||
|
||||
type wrapMode int
|
||||
@ -76,6 +76,11 @@ type writer struct {
|
||||
|
||||
var stderr io.Writer = os.Stderr
|
||||
|
||||
func nodeOf(parts ...interface{}) node {
|
||||
return node{parts: parts}
|
||||
}
|
||||
|
||||
// used only for debugging
|
||||
func (n node) String() string {
|
||||
var b bytes.Buffer
|
||||
w := &writer{w: &b}
|
||||
@ -116,10 +121,6 @@ func (w *writer) line(t int) {
|
||||
w.tabs(t)
|
||||
}
|
||||
|
||||
func nodeOf(parts ...interface{}) node {
|
||||
return node{parts: parts}
|
||||
}
|
||||
|
||||
func config(name string, dflt int) int {
|
||||
s := os.Getenv(name)
|
||||
if s == "" {
|
||||
@ -166,12 +167,8 @@ func fprintValues(w io.Writer, o opts, v []interface{}) (int, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
n := reflectValue(
|
||||
o,
|
||||
&pending{values: make(map[uintptr]nodeRef)},
|
||||
reflect.ValueOf(vi),
|
||||
)
|
||||
|
||||
p := &pending{values: make(map[uintptr]nodeRef)}
|
||||
n := reflectValue(o, p, reflect.ValueOf(vi))
|
||||
if o&wrap != 0 {
|
||||
n = nodeLen(tab, n)
|
||||
n = wrapNode(tab, cols0, cols0, cols1, n)
|
||||
@ -209,7 +206,7 @@ func Fprint(w io.Writer, v ...interface{}) (int, error) {
|
||||
return fprintValues(w, none, v)
|
||||
}
|
||||
|
||||
// Fprintw prints the provided objects to the provided writer, with wrapping (pretty-printing) where necessary.
|
||||
// Fprintw prints the provided objects to the provided writer, with wrapping (and indentation) where necessary.
|
||||
// When multiple objects are printed, they'll be separated by a newline.
|
||||
func Fprintw(w io.Writer, v ...interface{}) (int, error) {
|
||||
return fprintValues(w, wrap, v)
|
||||
@ -221,7 +218,7 @@ func Fprintt(w io.Writer, v ...interface{}) (int, error) {
|
||||
return fprintValues(w, types, v)
|
||||
}
|
||||
|
||||
// Fprintwt prints the provided objects to the provided writer, with wrapping (pretty-printing) where necessary,
|
||||
// Fprintwt prints the provided objects to the provided writer, with wrapping (and indentation) where necessary,
|
||||
// and with moderate type information. When multiple objects are printed, they'll be separated by a newline.
|
||||
func Fprintwt(w io.Writer, v ...interface{}) (int, error) {
|
||||
return fprintValues(w, wrap|types, v)
|
||||
@ -233,7 +230,7 @@ func Fprintv(w io.Writer, v ...interface{}) (int, error) {
|
||||
return fprintValues(w, allTypes, v)
|
||||
}
|
||||
|
||||
// Fprintwv prints the provided objects to the provided writer, with wrapping (pretty-printing) where necessary,
|
||||
// Fprintwv prints the provided objects to the provided writer, with wrapping (and indentation) where necessary,
|
||||
// and with verbose type information. When multiple objects are printed, they'll be separated by a newline.
|
||||
func Fprintwv(w io.Writer, v ...interface{}) (int, error) {
|
||||
return fprintValues(w, wrap|allTypes, v)
|
||||
@ -245,7 +242,7 @@ func Print(v ...interface{}) (int, error) {
|
||||
return printValues(none, v)
|
||||
}
|
||||
|
||||
// Printw prints the provided objects to stderr, with wrapping (pretty-printing) where necessary. When multiple
|
||||
// Printw prints the provided objects to stderr, with wrapping (and indentation) where necessary. When multiple
|
||||
// objects are printed, they'll be separated by a newline.
|
||||
func Printw(v ...interface{}) (int, error) {
|
||||
return printValues(wrap, v)
|
||||
@ -257,7 +254,7 @@ func Printt(v ...interface{}) (int, error) {
|
||||
return printValues(types, v)
|
||||
}
|
||||
|
||||
// Printwt prints the provided objects to stderr, with wrapping (pretty-printing) where necessary, and with
|
||||
// Printwt prints the provided objects to stderr, with wrapping (and indentation) where necessary, and with
|
||||
// moderate type information. When multiple objects are printed, they'll be separated by a newline.
|
||||
func Printwt(v ...interface{}) (int, error) {
|
||||
return printValues(wrap|types, v)
|
||||
@ -269,7 +266,7 @@ func Printv(v ...interface{}) (int, error) {
|
||||
return printValues(allTypes, v)
|
||||
}
|
||||
|
||||
// Printwv prints the provided objects to stderr, with wrapping (pretty-printing) where necessary, and with
|
||||
// Printwv prints the provided objects to stderr, with wrapping (and indentation) where necessary, and with
|
||||
// verbose type information. When multiple objects are printed, they'll be separated by a newline.
|
||||
func Printwv(v ...interface{}) (int, error) {
|
||||
return printValues(wrap|allTypes, v)
|
||||
@ -281,7 +278,7 @@ func Println(v ...interface{}) (int, error) {
|
||||
return printlnValues(none, v)
|
||||
}
|
||||
|
||||
// Printlnw prints the provided objects to stderr with a closing newline, with wrapping (pretty-printing) where
|
||||
// Printlnw prints the provided objects to stderr with a closing newline, with wrapping (and indentation) where
|
||||
// necessary. When multiple objects are printed, they'll be separated by a newline.
|
||||
func Printlnw(v ...interface{}) (int, error) {
|
||||
return printlnValues(wrap, v)
|
||||
@ -293,7 +290,7 @@ func Printlnt(v ...interface{}) (int, error) {
|
||||
return printlnValues(types, v)
|
||||
}
|
||||
|
||||
// Printlnwt prints the provided objects to stderr with a closing newline, with wrapping (pretty-printing) where
|
||||
// Printlnwt prints the provided objects to stderr with a closing newline, with wrapping (and indentation) where
|
||||
// necessary, and with moderate type information. When multiple objects are printed, they'll be separated by a
|
||||
// newline.
|
||||
func Printlnwt(v ...interface{}) (int, error) {
|
||||
@ -306,7 +303,7 @@ func Printlnv(v ...interface{}) (int, error) {
|
||||
return printlnValues(allTypes, v)
|
||||
}
|
||||
|
||||
// Printlnwv prints the provided objects to stderr with a closing newline, with wrapping (pretty-printing) where
|
||||
// Printlnwv prints the provided objects to stderr with a closing newline, with wrapping (and indentation) where
|
||||
// necessary, and with verbose type information. When multiple objects are printed, they'll be separated by a
|
||||
// newline.
|
||||
func Printlnwv(v ...interface{}) (int, error) {
|
||||
@ -319,7 +316,7 @@ func Sprint(v ...interface{}) string {
|
||||
return sprintValues(none, v)
|
||||
}
|
||||
|
||||
// Sprint returns the string representation of the Go objects, with wrapping (pretty-printing) where necessary.
|
||||
// Sprint returns the string representation of the Go objects, with wrapping (and indentation) where necessary.
|
||||
// When multiple objects are provided, they'll be seprated by a newline.
|
||||
func Sprintw(v ...interface{}) string {
|
||||
return sprintValues(wrap, v)
|
||||
@ -331,7 +328,7 @@ func Sprintt(v ...interface{}) string {
|
||||
return sprintValues(types, v)
|
||||
}
|
||||
|
||||
// Sprint returns the string representation of the Go objects, with wrapping (pretty-printing) where necessary,
|
||||
// Sprint returns the string representation of the Go objects, with wrapping (and indentation) where necessary,
|
||||
// and with moderate type information. When multiple objects are provided, they'll be seprated by a newline.
|
||||
func Sprintwt(v ...interface{}) string {
|
||||
return sprintValues(wrap|types, v)
|
||||
@ -343,7 +340,7 @@ func Sprintv(v ...interface{}) string {
|
||||
return sprintValues(allTypes, v)
|
||||
}
|
||||
|
||||
// Sprint returns the string representation of the Go objects, with wrapping (pretty-printing) where necessary,
|
||||
// Sprint returns the string representation of the Go objects, with wrapping (and indentation) where necessary,
|
||||
// and with verbose type information. When multiple objects are provided, they'll be seprated by a newline.
|
||||
func Sprintwv(v ...interface{}) string {
|
||||
return sprintValues(wrap|allTypes, v)
|
||||
|
84
reflect.go
84
reflect.go
@ -38,11 +38,10 @@ func reflectPrimitive(o opts, r reflect.Value, v interface{}, suppressType ...st
|
||||
}
|
||||
|
||||
for _, suppress := range suppressType {
|
||||
if tn.parts[0] != suppress {
|
||||
continue
|
||||
if tn.parts[0] == suppress {
|
||||
return nodeOf(s)
|
||||
}
|
||||
|
||||
return nodeOf(s)
|
||||
}
|
||||
|
||||
return nodeOf(tn, "(", s, ")")
|
||||
@ -63,31 +62,33 @@ func reflectNil(o opts, groupUnnamedType bool, r reflect.Value) node {
|
||||
|
||||
func reflectItems(o opts, p *pending, prefix string, r reflect.Value) node {
|
||||
typ := r.Type()
|
||||
var items wrapper
|
||||
if typ.Elem().Name() == "uint8" {
|
||||
items = wrapper{sep: " ", mode: line}
|
||||
var w wrapper
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
w.sep = " "
|
||||
w.mode = line
|
||||
for i := 0; i < r.Len(); i++ {
|
||||
items.items = append(
|
||||
items.items,
|
||||
w.items = append(
|
||||
w.items,
|
||||
nodeOf(fmt.Sprintf("%02x", r.Index(i).Uint())),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items = wrapper{sep: ", ", suffix: ","}
|
||||
w.sep = ", "
|
||||
w.suffix = ","
|
||||
itemOpts := o | skipTypes
|
||||
for i := 0; i < r.Len(); i++ {
|
||||
items.items = append(
|
||||
items.items,
|
||||
w.items = append(
|
||||
w.items,
|
||||
reflectValue(itemOpts, p, r.Index(i)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if _, t, _ := withType(o); !t {
|
||||
return nodeOf(prefix, "{", items, "}")
|
||||
if _, t, _ := withType(o); t {
|
||||
return nodeOf(reflectType(typ), "{", w, "}")
|
||||
}
|
||||
|
||||
return nodeOf(reflectType(typ), "{", items, "}")
|
||||
return nodeOf(prefix, "{", w, "}")
|
||||
}
|
||||
|
||||
func reflectHidden(o opts, hidden string, r reflect.Value) node {
|
||||
@ -95,11 +96,11 @@ func reflectHidden(o opts, hidden string, r reflect.Value) node {
|
||||
return reflectNil(o, true, r)
|
||||
}
|
||||
|
||||
if _, t, _ := withType(o); !t {
|
||||
return nodeOf(hidden)
|
||||
if _, t, _ := withType(o); t {
|
||||
return reflectType(r.Type())
|
||||
}
|
||||
|
||||
return reflectType(r.Type())
|
||||
return nodeOf(hidden)
|
||||
}
|
||||
|
||||
func reflectArray(o opts, p *pending, r reflect.Value) node {
|
||||
@ -138,46 +139,38 @@ func reflectMap(o opts, p *pending, r reflect.Value) node {
|
||||
}
|
||||
|
||||
var skeys []string
|
||||
items := wrapper{sep: ", ", suffix: ","}
|
||||
itemOpts := o | skipTypes
|
||||
keys := r.MapKeys()
|
||||
sv := make(map[string]reflect.Value)
|
||||
sn := make(map[string]node)
|
||||
for _, key := range keys {
|
||||
for _, key := range r.MapKeys() {
|
||||
kn := reflectValue(itemOpts, p, key)
|
||||
var b bytes.Buffer
|
||||
nk := reflectValue(itemOpts, p, key)
|
||||
wr := writer{w: &b}
|
||||
fprint(&wr, 0, nk)
|
||||
fprint(&wr, 0, kn)
|
||||
skey := b.String()
|
||||
skeys = append(skeys, skey)
|
||||
sv[skey] = key
|
||||
sn[skey] = nk
|
||||
sn[skey] = kn
|
||||
}
|
||||
|
||||
if o&randomMaps == 0 {
|
||||
sort.Strings(skeys)
|
||||
}
|
||||
|
||||
w := wrapper{sep: ", ", suffix: ","}
|
||||
for _, skey := range skeys {
|
||||
items.items = append(
|
||||
items.items,
|
||||
nodeOf(
|
||||
sn[skey],
|
||||
": ",
|
||||
reflectValue(
|
||||
itemOpts,
|
||||
p,
|
||||
r.MapIndex(sv[skey]),
|
||||
),
|
||||
),
|
||||
vn := reflectValue(itemOpts, p, r.MapIndex(sv[skey]))
|
||||
w.items = append(
|
||||
w.items,
|
||||
nodeOf(sn[skey], ": ", vn),
|
||||
)
|
||||
}
|
||||
|
||||
if _, t, _ := withType(o); !t {
|
||||
return nodeOf("map{", items, "}")
|
||||
return nodeOf("map{", w, "}")
|
||||
}
|
||||
|
||||
return nodeOf(reflectType(r.Type()), "{", items, "}")
|
||||
return nodeOf(reflectType(r.Type()), "{", w, "}")
|
||||
}
|
||||
|
||||
func reflectPointer(o opts, p *pending, r reflect.Value) node {
|
||||
@ -229,17 +222,10 @@ func reflectStruct(o opts, p *pending, r reflect.Value) node {
|
||||
rt := r.Type()
|
||||
for i := 0; i < r.NumField(); i++ {
|
||||
name := rt.Field(i).Name
|
||||
fv := reflectValue(fieldOpts, p, r.FieldByName(name))
|
||||
wr.items = append(
|
||||
wr.items,
|
||||
nodeOf(
|
||||
name,
|
||||
": ",
|
||||
reflectValue(
|
||||
fieldOpts,
|
||||
p,
|
||||
r.FieldByName(name),
|
||||
),
|
||||
),
|
||||
nodeOf(name, ": ", fv),
|
||||
)
|
||||
}
|
||||
|
||||
@ -265,12 +251,12 @@ func reflectUnsafePointer(o opts, r reflect.Value) node {
|
||||
func checkPending(p *pending, r reflect.Value) (applyRef func(node) node, ref node, isPending bool) {
|
||||
applyRef = func(n node) node { return n }
|
||||
switch r.Kind() {
|
||||
case reflect.Slice, reflect.Map:
|
||||
case reflect.Ptr:
|
||||
if r.IsNil() {
|
||||
case reflect.Slice, reflect.Map, reflect.Ptr:
|
||||
default:
|
||||
return
|
||||
}
|
||||
default:
|
||||
|
||||
if r.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -64,13 +64,8 @@ func reflectInterfaceType(t reflect.Type) node {
|
||||
wr := wrapper{sep: "; "}
|
||||
for i := 0; i < t.NumMethod(); i++ {
|
||||
method := t.Method(i)
|
||||
wr.items = append(
|
||||
wr.items,
|
||||
nodeOf(
|
||||
method.Name,
|
||||
reflectFuncBaseType(method.Type),
|
||||
),
|
||||
)
|
||||
mn := nodeOf(method.Name, reflectFuncBaseType(method.Type))
|
||||
wr.items = append(wr.items, mn)
|
||||
}
|
||||
|
||||
return nodeOf("interface{", wr, "}")
|
||||
@ -92,14 +87,8 @@ func reflectStructType(t reflect.Type) node {
|
||||
wr := wrapper{sep: "; "}
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
fi := t.Field(i)
|
||||
wr.items = append(
|
||||
wr.items,
|
||||
nodeOf(
|
||||
fi.Name,
|
||||
" ",
|
||||
reflectType(fi.Type),
|
||||
),
|
||||
)
|
||||
fn := nodeOf(fi.Name, " ", reflectType(fi.Type))
|
||||
wr.items = append(wr.items, fn)
|
||||
}
|
||||
|
||||
return nodeOf("struct{", wr, "}")
|
||||
|
@ -973,7 +973,7 @@ func TestLongNonWrapperNodes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSingleLongString(t *testing.T) {
|
||||
func TestLongString(t *testing.T) {
|
||||
t.Run("no line breaks", func(t *testing.T) {
|
||||
const expect = `"foobarbazquxquuxquzquuz"`
|
||||
defer withEnv(t, "TABWIDTH=2", "LINEWIDTH=9", "LINEWIDTH1=12")()
|
||||
@ -1034,7 +1034,11 @@ func TestCyclicReferences(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("multiple refs", func(t *testing.T) {
|
||||
const expect = `{f0: r1={f0: r2={f0: nil, f1: r1, f2: r2}, f1: nil, f2: nil}, f1: nil, f2: nil}`
|
||||
const expect = `{
|
||||
f0: r1={f0: r2={f0: nil, f1: r1, f2: r2}, f1: nil, f2: nil},
|
||||
f1: nil,
|
||||
f2: nil,
|
||||
}`
|
||||
type typ struct{ f0, f1, f2 *typ }
|
||||
v0 := new(typ)
|
||||
v1 := new(typ)
|
||||
|
Loading…
Reference in New Issue
Block a user