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.
|
151
README.md
151
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
|
This package can be used to print (or sprint) Go objects for debugging purposes, with optional wrapping
|
||||||
information for debugging purposes.
|
(indentation) and optional type information.
|
||||||
|
|
||||||
### Alternatives
|
### Alternatives
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ Notation is similar to the following great, more established and mature packages
|
|||||||
- [litter](https://github.com/sanity-io/litter)
|
- [litter](https://github.com/sanity-io/litter)
|
||||||
- [utter](https://github.com/kortschak/utter)
|
- [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
|
### 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:
|
Assuming to have the required types defined, if we do the following:
|
||||||
|
|
||||||
```go
|
```
|
||||||
b := bike{
|
b := bike{
|
||||||
frame: frame{
|
frame: frame{
|
||||||
fork: fork{},
|
fork: fork{},
|
||||||
@ -87,12 +87,17 @@ s := notation.Sprintw(b)
|
|||||||
|
|
||||||
We get the following string:
|
We get the following string:
|
||||||
|
|
||||||
```go
|
```
|
||||||
{
|
{
|
||||||
frame: {
|
frame: {
|
||||||
fork: {
|
fork: {
|
||||||
wheel: {size: 70, cassette: nil},
|
wheel: {size: 70, cassette: nil},
|
||||||
handlebar: {levers: []{{withShift: true}, {withShift: true}}},
|
handlebar: {
|
||||||
|
levers: []{
|
||||||
|
{withShift: true},
|
||||||
|
{withShift: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
frontBrake: {discSize: 160},
|
frontBrake: {discSize: 160},
|
||||||
},
|
},
|
||||||
saddlePost: {saddle: {}},
|
saddlePost: {saddle: {}},
|
||||||
@ -111,7 +116,10 @@ We get the following string:
|
|||||||
chain: {},
|
chain: {},
|
||||||
levers: []{{withShift: true}, {withShift: true}},
|
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}}},
|
handlebar: {levers: []{{withShift: true}, {withShift: true}}},
|
||||||
saddle: {},
|
saddle: {},
|
||||||
}
|
}
|
||||||
@ -119,11 +127,14 @@ We get the following string:
|
|||||||
|
|
||||||
Using `notation.Sprintwv` instead of `notation.Sprintw`, we would get the following string:
|
Using `notation.Sprintwv` instead of `notation.Sprintw`, we would get the following string:
|
||||||
|
|
||||||
```go
|
```
|
||||||
bike{
|
bike{
|
||||||
frame: frame{
|
frame: frame{
|
||||||
fork: fork{
|
fork: fork{
|
||||||
wheel: *wheel{size: float64(70), cassette: (*cassette)(nil)},
|
wheel: *wheel{
|
||||||
|
size: float64(70),
|
||||||
|
cassette: (*cassette)(nil),
|
||||||
|
},
|
||||||
handlebar: *handlebar{
|
handlebar: *handlebar{
|
||||||
levers: []*lever{
|
levers: []*lever{
|
||||||
*lever{withShift: bool(true)},
|
*lever{withShift: bool(true)},
|
||||||
@ -133,32 +144,63 @@ bike{
|
|||||||
frontBrake: *brake{discSize: float64(160)},
|
frontBrake: *brake{discSize: float64(160)},
|
||||||
},
|
},
|
||||||
saddlePost: saddlePost{saddle: *saddle{}},
|
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)},
|
frontDerailleur: *derailleur{gears: int(2)},
|
||||||
rearDerailleur: *derailleur{gears: int(11)},
|
rearDerailleur: *derailleur{gears: int(11)},
|
||||||
rearBrake: *brake{discSize: float64(140)},
|
rearBrake: *brake{discSize: float64(140)},
|
||||||
rearWheel: *wheel{
|
rearWheel: *wheel{
|
||||||
size: float64(70),
|
size: float64(70),
|
||||||
cassette: *cassette{wheels: int(11), chain: *chain{}},
|
cassette: *cassette{
|
||||||
|
wheels: int(11),
|
||||||
|
chain: *chain{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
driveTrain: driveTrain{
|
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{}},
|
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{
|
derailleurs: []derailleur{
|
||||||
derailleur{gears: int(2)},
|
derailleur{
|
||||||
derailleur{gears: int(11)},
|
gears: int(2),
|
||||||
|
},
|
||||||
|
derailleur{
|
||||||
|
gears: int(11),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cassette: cassette{wheels: int(11), chain: *chain{}},
|
cassette: cassette{wheels: int(11), chain: *chain{}},
|
||||||
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{
|
wheels: []wheel{
|
||||||
wheel{size: float64(70), cassette: (*cassette)(nil)},
|
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{},
|
saddle: saddle{},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -172,28 +214,28 @@ input objects.
|
|||||||
##### Numbers
|
##### Numbers
|
||||||
|
|
||||||
Numbers are printed based on the `fmt` package's default formatting. When printing with moderate type
|
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
|
i := 42
|
||||||
notation.Printlnt(i)
|
notation.Printlnt(i)
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
42
|
42
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Strings
|
##### Strings
|
||||||
|
|
||||||
When printing strings, by default they are escaped using the `strconv.Quote` function. However, when wrapping
|
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
|
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 '`'.
|
as a raw string literal, delimited by backquotes.
|
||||||
|
|
||||||
Short string:
|
Short string:
|
||||||
|
|
||||||
```go
|
```
|
||||||
s := `foobar
|
s := `foobar
|
||||||
baz`
|
baz`
|
||||||
notation.Printlnw(s)
|
notation.Printlnw(s)
|
||||||
@ -201,13 +243,13 @@ notation.Printlnw(s)
|
|||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
"foobar\nbaz"
|
"foobar\nbaz"
|
||||||
```
|
```
|
||||||
|
|
||||||
Long string:
|
Long string:
|
||||||
|
|
||||||
```go
|
```
|
||||||
s := `The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
|
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
|
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
|
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:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
`The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
|
`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
|
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
|
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
|
Slices are are printed by printing their elements between braces, prefixed either by '[]' or the type of the
|
||||||
slice. Example:
|
slice. Example:
|
||||||
|
|
||||||
```go
|
```
|
||||||
l := []int{1, 2, 3}
|
l := []int{1, 2, 3}
|
||||||
notation.Println(l)
|
notation.Println(l)
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
[]{1, 2, 3}
|
[]{1, 2, 3}
|
||||||
```
|
```
|
||||||
|
|
||||||
To differentiate arrays from slices, arrays are always prefixed with their type or square brackets containing
|
To differentiate arrays from slices, arrays are always prefixed with their type or square brackets containing
|
||||||
the length of the array:
|
the length of the array:
|
||||||
|
|
||||||
```go
|
```
|
||||||
a := [...]{1, 2, 3}
|
a := [...]{1, 2, 3}
|
||||||
notation.Println(a)
|
notation.Println(a)
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
[3]{1, 2, 3}
|
[3]{1, 2, 3}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -264,9 +306,9 @@ 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
|
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:
|
representation of its bytes:
|
||||||
|
|
||||||
```go
|
```
|
||||||
b := []byte(
|
b := []byte(
|
||||||
`The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The
|
`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
|
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
|
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
|
over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
|
||||||
@ -279,7 +321,7 @@ notation.Printlnwt(b)
|
|||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
[]byte{
|
[]byte{
|
||||||
54 68 65 20 71 75 69 63 6b 20 62 72 6f 77 6e 20 66 6f 78 20 6a
|
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
|
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:
|
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}
|
m := map[string]int{"b": 1, "c": 2, "a": 3}
|
||||||
notation.Println(m)
|
notation.Println(m)
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
map{"a": 3, "b": 1, "c": 2}
|
map{"a": 3, "b": 1, "c": 2}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -330,42 +372,42 @@ disabled via the `MAPSORT=0` environment variable.
|
|||||||
|
|
||||||
##### Hidden values: channels, functions
|
##### 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:
|
function body. When printing with types, the signature of these objects is printed:
|
||||||
|
|
||||||
```go
|
```
|
||||||
f := func(int) int { return 42 }
|
f := func(int) int { return 42 }
|
||||||
notation.Println(f)
|
notation.Println(f)
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
func()
|
func()
|
||||||
```
|
```
|
||||||
|
|
||||||
With types:
|
With types:
|
||||||
|
|
||||||
```go
|
```
|
||||||
f := func(int) int { return 42 }
|
f := func(int) int { return 42 }
|
||||||
notation.Printlnt(f)
|
notation.Printlnt(f)
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
func(int) int
|
func(int) int
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Wrapping
|
##### 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
|
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,
|
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.
|
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
|
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
|
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
|
default line width. E.g. if somebody uses two-character wide tabs in their console, they can use the package
|
||||||
like this:
|
like this:
|
||||||
@ -374,7 +416,7 @@ like this:
|
|||||||
TABWIDTH=2 go test -v -count 1
|
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
|
TABWIDTH=0 LINEWIDTH=0 LINEWIDTH1=0 go test -v -count 1
|
||||||
@ -388,7 +430,7 @@ type. The package path is not printed.
|
|||||||
|
|
||||||
Named type:
|
Named type:
|
||||||
|
|
||||||
```go
|
```
|
||||||
type t struct{foo int}
|
type t struct{foo int}
|
||||||
v := t{42}
|
v := t{42}
|
||||||
notation.Printlnt(v)
|
notation.Printlnt(v)
|
||||||
@ -396,43 +438,43 @@ notation.Printlnt(v)
|
|||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
t{foo: 42}
|
t{foo: 42}
|
||||||
```
|
```
|
||||||
|
|
||||||
Unnamed type:
|
Unnamed type:
|
||||||
|
|
||||||
```go
|
```
|
||||||
v := struct{foo int}{42}
|
v := struct{foo int}{42}
|
||||||
notation.Printlnt(v)
|
notation.Printlnt(v)
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
struct{foo int}{foo: 42}
|
struct{foo int}{foo: 42}
|
||||||
```
|
```
|
||||||
|
|
||||||
Using the 't' suffixed variants of the printing functions, displaying only moderately verbose type information,
|
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:
|
the types of certain values is omitted, where it can be inferred from the context:
|
||||||
|
|
||||||
```go
|
```
|
||||||
v := []struct{foo int}{{42}, {84}}
|
v := []struct{foo int}{{42}, {84}}
|
||||||
notation.Printlnt(os.Stdout, v)
|
notation.Printlnt(os.Stdout, v)
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
[]struct{foo int}{{foo: 42}, {foo: 84}}
|
[]struct{foo int}{{foo: 42}, {foo: 84}}
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Cyclic references
|
##### Cyclic references
|
||||||
|
|
||||||
Cyclic references are detected based on an approach similar to the one in the stdlib's reflect.DeepEqual
|
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 := []interface{}{"foo"}
|
||||||
l[0] = l
|
l[0] = l
|
||||||
notation.Fprint(os.Stdout, l)
|
notation.Fprint(os.Stdout, l)
|
||||||
@ -440,6 +482,7 @@ notation.Fprint(os.Stdout, l)
|
|||||||
|
|
||||||
Output:
|
Output:
|
||||||
|
|
||||||
```go
|
```
|
||||||
r0=[]{r0}
|
r0=[]{r0}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -100,7 +100,12 @@ func Example() {
|
|||||||
// frame: {
|
// frame: {
|
||||||
// fork: {
|
// fork: {
|
||||||
// wheel: {size: 70, cassette: nil},
|
// wheel: {size: 70, cassette: nil},
|
||||||
// handlebar: {levers: []{{withShift: true}, {withShift: true}}},
|
// handlebar: {
|
||||||
|
// levers: []{
|
||||||
|
// {withShift: true},
|
||||||
|
// {withShift: true},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
// frontBrake: {discSize: 160},
|
// frontBrake: {discSize: 160},
|
||||||
// },
|
// },
|
||||||
// saddlePost: {saddle: {}},
|
// saddlePost: {saddle: {}},
|
||||||
@ -119,7 +124,10 @@ func Example() {
|
|||||||
// chain: {},
|
// chain: {},
|
||||||
// levers: []{{withShift: true}, {withShift: true}},
|
// 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}}},
|
// handlebar: {levers: []{{withShift: true}, {withShift: true}}},
|
||||||
// saddle: {},
|
// saddle: {},
|
||||||
// }
|
// }
|
||||||
|
349
fprint.go
349
fprint.go
@ -1,183 +1,222 @@
|
|||||||
package notation
|
package notation
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
func unwrappable(n node) bool {
|
func ifZero(a, b int) int {
|
||||||
return n.len == n.wrapLen.max &&
|
if a == 0 {
|
||||||
n.len == n.fullWrap.max
|
return b
|
||||||
}
|
|
||||||
|
|
||||||
func initialize(t *int, v int) {
|
|
||||||
if *t > 0 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*t = v
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func max(t *int, v int) {
|
func max(a, b int) int {
|
||||||
if *t >= v {
|
if a > b {
|
||||||
return
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
*t = v
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
func nodeLen(t int, n node) node {
|
||||||
var w, f int
|
// We assume here that an str is always contained
|
||||||
for i, p := range n.parts {
|
// by a node that has only a single str.
|
||||||
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.
|
|
||||||
//
|
//
|
||||||
// If this changes in the future, then we need to provide tests for the
|
if s, ok := n.parts[0].(str); ok {
|
||||||
// additional cases. If this doesn't change anytime soon, then we can
|
s = strLen(s)
|
||||||
// refactor this part.
|
n.parts[0] = s
|
||||||
//
|
n.len = len(s.val)
|
||||||
n.len = len(part.val)
|
if s.raw == "" {
|
||||||
if part.raw == "" {
|
wl := wrapLen{
|
||||||
w = len(part.val)
|
first: len(s.val),
|
||||||
f = len(part.val)
|
max: len(s.val),
|
||||||
} else {
|
last: len(s.val),
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
part.rawLen.last = len(lines[len(lines)-1])
|
n.wrapLen = wl
|
||||||
n.parts[i] = part
|
n.fullWrap = wl
|
||||||
n.wrapLen.first = part.rawLen.first
|
return n
|
||||||
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 = s.rawLen
|
||||||
|
n.fullWrap = s.rawLen
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range n.parts {
|
||||||
|
switch pt := n.parts[i].(type) {
|
||||||
case node:
|
case node:
|
||||||
part = nodeLen(t, part)
|
n.parts[i] = nodeLen(t, pt)
|
||||||
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
|
|
||||||
case wrapper:
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(&n.wrapLen.first, w)
|
n.len += (len(pt.items) - 1) * len(pt.sep)
|
||||||
max(&n.wrapLen.max, w)
|
for _, pti := range pt.items {
|
||||||
initialize(&n.fullWrap.first, f)
|
n.len += pti.len
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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:
|
default:
|
||||||
wj := t + item.len + len(part.suffix)
|
n.len += len(fmt.Sprint(p))
|
||||||
max(&w, wj)
|
|
||||||
fj := t + item.fullWrap.max
|
|
||||||
max(&f, fj)
|
|
||||||
fj = t + item.fullWrap.last + len(part.suffix)
|
|
||||||
max(&f, fj)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
max(&n.wrapLen.max, w)
|
var w, f int
|
||||||
max(&n.fullWrap.max, f)
|
for _, p := range n.parts {
|
||||||
w, f = 0, 0
|
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)
|
n.wrapLen.max = max(n.wrapLen.max, w)
|
||||||
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
|
n.wrapLen.last = w
|
||||||
initialize(&n.fullWrap.first, f)
|
n.fullWrap.first = ifZero(n.fullWrap.first, f)
|
||||||
max(&n.fullWrap.max, f)
|
n.fullWrap.max = max(n.fullWrap.max, f)
|
||||||
n.fullWrap.last = f
|
n.fullWrap.last = f
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapNode(t, cf0, c0, c1 int, n node) node {
|
func wrapNode(t, cf0, c0, c1 int, n node) node {
|
||||||
|
// fits:
|
||||||
if n.len <= c0 {
|
if n.len <= c0 {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we don't want to make it longer:
|
||||||
if n.wrapLen.max >= n.len && n.fullWrap.max >= n.len {
|
if n.wrapLen.max >= n.len && n.fullWrap.max >= n.len {
|
||||||
return n
|
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
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
n.wrap = true
|
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
|
cc0, cc1 := c0, c1
|
||||||
lastWrapperIndex := -1
|
lastWrapperIndex := -1
|
||||||
var trackBack bool
|
var trackBack bool
|
||||||
for i := 0; i < len(n.parts); i++ {
|
for i := 0; i < len(n.parts); i++ {
|
||||||
p := n.parts[i]
|
p := n.parts[i]
|
||||||
switch part := p.(type) {
|
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:
|
case node:
|
||||||
part = wrapNode(t, cf0, cc0, cc1, part)
|
part = wrapNode(t, cf0, cc0, cc1, part)
|
||||||
n.parts[i] = part
|
n.parts[i] = part
|
||||||
if part.wrap {
|
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.
|
// here, but usually those are the same.
|
||||||
|
//
|
||||||
cc0 -= part.wrapLen.first
|
cc0 -= part.wrapLen.first
|
||||||
cc1 -= part.wrapLen.first
|
cc1 -= part.wrapLen.first
|
||||||
} else {
|
} else {
|
||||||
@ -185,12 +224,26 @@ func wrapNode(t, cf0, c0, c1 int, n node) node {
|
|||||||
cc1 -= part.len
|
cc1 -= part.len
|
||||||
}
|
}
|
||||||
|
|
||||||
if !trackBack && cc1 < 0 {
|
if cc1 >= 0 {
|
||||||
cc0 = 0
|
if part.wrap {
|
||||||
cc1 = 0
|
cc0 = c0 - part.wrapLen.last
|
||||||
|
cc1 = c1 - part.wrapLen.last
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if trackBack {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// trackback from after the last wrapper:
|
||||||
i = lastWrapperIndex
|
i = lastWrapperIndex
|
||||||
trackBack = true
|
trackBack = true
|
||||||
}
|
|
||||||
|
// force wrapping during trackback:
|
||||||
|
cc0 = 0
|
||||||
|
cc1 = 0
|
||||||
case wrapper:
|
case wrapper:
|
||||||
if len(part.items) == 0 {
|
if len(part.items) == 0 {
|
||||||
continue
|
continue
|
||||||
@ -201,37 +254,50 @@ func wrapNode(t, cf0, c0, c1 int, n node) node {
|
|||||||
lastWrapperIndex = i
|
lastWrapperIndex = i
|
||||||
switch part.mode {
|
switch part.mode {
|
||||||
case line:
|
case line:
|
||||||
|
// we only set the line endings. We use
|
||||||
|
// the full column width:
|
||||||
|
//
|
||||||
cl := cf0 - t
|
cl := cf0 - t
|
||||||
var w int
|
var w int
|
||||||
for j, ni := range part.items {
|
for j, nj := range part.items {
|
||||||
if w > 0 && w+len(part.sep)+ni.len > cl {
|
if w > 0 && w+len(part.sep)+nj.len > cl {
|
||||||
|
part.lineEnds = append(part.lineEnds, j)
|
||||||
w = 0
|
w = 0
|
||||||
part.lineEnds = append(
|
|
||||||
part.lineEnds,
|
|
||||||
j,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if w > 0 {
|
if w > 0 {
|
||||||
w += len(part.sep)
|
w += len(part.sep)
|
||||||
}
|
}
|
||||||
|
|
||||||
w += ni.len
|
w += nj.len
|
||||||
}
|
}
|
||||||
|
|
||||||
part.lineEnds = append(part.lineEnds, len(part.items))
|
part.lineEnds = append(part.lineEnds, len(part.items))
|
||||||
n.parts[i] = part
|
n.parts[i] = part
|
||||||
default:
|
default:
|
||||||
for j := range part.items {
|
for j := range part.items {
|
||||||
part.items[j] = wrapNode(
|
part.items[j] = wrapNode(t, cf0, c0-t, c1-t, part.items[j])
|
||||||
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 {
|
for _, line := range lines {
|
||||||
w.blankLine()
|
w.line(1)
|
||||||
w.tabs(1)
|
|
||||||
for i, ni := range line {
|
for i, ni := range line {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write(part.sep)
|
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.
|
optional type information.
|
||||||
*/
|
*/
|
||||||
package notation
|
package notation
|
||||||
@ -40,18 +40,18 @@ type pending struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
|
parts []interface{}
|
||||||
len int
|
len int
|
||||||
wrapLen wrapLen
|
wrapLen wrapLen
|
||||||
fullWrap wrapLen
|
fullWrap wrapLen
|
||||||
wrap bool
|
wrap bool
|
||||||
parts []interface{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type str struct {
|
type str struct {
|
||||||
val string
|
val string
|
||||||
raw string
|
raw string
|
||||||
useRaw bool
|
|
||||||
rawLen wrapLen
|
rawLen wrapLen
|
||||||
|
useRaw bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type wrapMode int
|
type wrapMode int
|
||||||
@ -76,6 +76,11 @@ type writer struct {
|
|||||||
|
|
||||||
var stderr io.Writer = os.Stderr
|
var stderr io.Writer = os.Stderr
|
||||||
|
|
||||||
|
func nodeOf(parts ...interface{}) node {
|
||||||
|
return node{parts: parts}
|
||||||
|
}
|
||||||
|
|
||||||
|
// used only for debugging
|
||||||
func (n node) String() string {
|
func (n node) String() string {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
w := &writer{w: &b}
|
w := &writer{w: &b}
|
||||||
@ -116,10 +121,6 @@ func (w *writer) line(t int) {
|
|||||||
w.tabs(t)
|
w.tabs(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeOf(parts ...interface{}) node {
|
|
||||||
return node{parts: parts}
|
|
||||||
}
|
|
||||||
|
|
||||||
func config(name string, dflt int) int {
|
func config(name string, dflt int) int {
|
||||||
s := os.Getenv(name)
|
s := os.Getenv(name)
|
||||||
if s == "" {
|
if s == "" {
|
||||||
@ -166,12 +167,8 @@ func fprintValues(w io.Writer, o opts, v []interface{}) (int, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
n := reflectValue(
|
p := &pending{values: make(map[uintptr]nodeRef)}
|
||||||
o,
|
n := reflectValue(o, p, reflect.ValueOf(vi))
|
||||||
&pending{values: make(map[uintptr]nodeRef)},
|
|
||||||
reflect.ValueOf(vi),
|
|
||||||
)
|
|
||||||
|
|
||||||
if o&wrap != 0 {
|
if o&wrap != 0 {
|
||||||
n = nodeLen(tab, n)
|
n = nodeLen(tab, n)
|
||||||
n = wrapNode(tab, cols0, cols0, cols1, 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)
|
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.
|
// When multiple objects are printed, they'll be separated by a newline.
|
||||||
func Fprintw(w io.Writer, v ...interface{}) (int, error) {
|
func Fprintw(w io.Writer, v ...interface{}) (int, error) {
|
||||||
return fprintValues(w, wrap, v)
|
return fprintValues(w, wrap, v)
|
||||||
@ -221,7 +218,7 @@ func Fprintt(w io.Writer, v ...interface{}) (int, error) {
|
|||||||
return fprintValues(w, types, v)
|
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.
|
// 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) {
|
func Fprintwt(w io.Writer, v ...interface{}) (int, error) {
|
||||||
return fprintValues(w, wrap|types, v)
|
return fprintValues(w, wrap|types, v)
|
||||||
@ -233,7 +230,7 @@ func Fprintv(w io.Writer, v ...interface{}) (int, error) {
|
|||||||
return fprintValues(w, allTypes, v)
|
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.
|
// 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) {
|
func Fprintwv(w io.Writer, v ...interface{}) (int, error) {
|
||||||
return fprintValues(w, wrap|allTypes, v)
|
return fprintValues(w, wrap|allTypes, v)
|
||||||
@ -245,7 +242,7 @@ func Print(v ...interface{}) (int, error) {
|
|||||||
return printValues(none, v)
|
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.
|
// objects are printed, they'll be separated by a newline.
|
||||||
func Printw(v ...interface{}) (int, error) {
|
func Printw(v ...interface{}) (int, error) {
|
||||||
return printValues(wrap, v)
|
return printValues(wrap, v)
|
||||||
@ -257,7 +254,7 @@ func Printt(v ...interface{}) (int, error) {
|
|||||||
return printValues(types, v)
|
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.
|
// moderate type information. When multiple objects are printed, they'll be separated by a newline.
|
||||||
func Printwt(v ...interface{}) (int, error) {
|
func Printwt(v ...interface{}) (int, error) {
|
||||||
return printValues(wrap|types, v)
|
return printValues(wrap|types, v)
|
||||||
@ -269,7 +266,7 @@ func Printv(v ...interface{}) (int, error) {
|
|||||||
return printValues(allTypes, v)
|
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.
|
// verbose type information. When multiple objects are printed, they'll be separated by a newline.
|
||||||
func Printwv(v ...interface{}) (int, error) {
|
func Printwv(v ...interface{}) (int, error) {
|
||||||
return printValues(wrap|allTypes, v)
|
return printValues(wrap|allTypes, v)
|
||||||
@ -281,7 +278,7 @@ func Println(v ...interface{}) (int, error) {
|
|||||||
return printlnValues(none, v)
|
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.
|
// necessary. When multiple objects are printed, they'll be separated by a newline.
|
||||||
func Printlnw(v ...interface{}) (int, error) {
|
func Printlnw(v ...interface{}) (int, error) {
|
||||||
return printlnValues(wrap, v)
|
return printlnValues(wrap, v)
|
||||||
@ -293,7 +290,7 @@ func Printlnt(v ...interface{}) (int, error) {
|
|||||||
return printlnValues(types, v)
|
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
|
// necessary, and with moderate type information. When multiple objects are printed, they'll be separated by a
|
||||||
// newline.
|
// newline.
|
||||||
func Printlnwt(v ...interface{}) (int, error) {
|
func Printlnwt(v ...interface{}) (int, error) {
|
||||||
@ -306,7 +303,7 @@ func Printlnv(v ...interface{}) (int, error) {
|
|||||||
return printlnValues(allTypes, v)
|
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
|
// necessary, and with verbose type information. When multiple objects are printed, they'll be separated by a
|
||||||
// newline.
|
// newline.
|
||||||
func Printlnwv(v ...interface{}) (int, error) {
|
func Printlnwv(v ...interface{}) (int, error) {
|
||||||
@ -319,7 +316,7 @@ func Sprint(v ...interface{}) string {
|
|||||||
return sprintValues(none, v)
|
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.
|
// When multiple objects are provided, they'll be seprated by a newline.
|
||||||
func Sprintw(v ...interface{}) string {
|
func Sprintw(v ...interface{}) string {
|
||||||
return sprintValues(wrap, v)
|
return sprintValues(wrap, v)
|
||||||
@ -331,7 +328,7 @@ func Sprintt(v ...interface{}) string {
|
|||||||
return sprintValues(types, v)
|
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.
|
// and with moderate type information. When multiple objects are provided, they'll be seprated by a newline.
|
||||||
func Sprintwt(v ...interface{}) string {
|
func Sprintwt(v ...interface{}) string {
|
||||||
return sprintValues(wrap|types, v)
|
return sprintValues(wrap|types, v)
|
||||||
@ -343,7 +340,7 @@ func Sprintv(v ...interface{}) string {
|
|||||||
return sprintValues(allTypes, v)
|
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.
|
// and with verbose type information. When multiple objects are provided, they'll be seprated by a newline.
|
||||||
func Sprintwv(v ...interface{}) string {
|
func Sprintwv(v ...interface{}) string {
|
||||||
return sprintValues(wrap|allTypes, v)
|
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 {
|
for _, suppress := range suppressType {
|
||||||
if tn.parts[0] != suppress {
|
if tn.parts[0] == suppress {
|
||||||
continue
|
return nodeOf(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeOf(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeOf(tn, "(", 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 {
|
func reflectItems(o opts, p *pending, prefix string, r reflect.Value) node {
|
||||||
typ := r.Type()
|
typ := r.Type()
|
||||||
var items wrapper
|
var w wrapper
|
||||||
if typ.Elem().Name() == "uint8" {
|
if typ.Elem().Kind() == reflect.Uint8 {
|
||||||
items = wrapper{sep: " ", mode: line}
|
w.sep = " "
|
||||||
|
w.mode = line
|
||||||
for i := 0; i < r.Len(); i++ {
|
for i := 0; i < r.Len(); i++ {
|
||||||
items.items = append(
|
w.items = append(
|
||||||
items.items,
|
w.items,
|
||||||
nodeOf(fmt.Sprintf("%02x", r.Index(i).Uint())),
|
nodeOf(fmt.Sprintf("%02x", r.Index(i).Uint())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
items = wrapper{sep: ", ", suffix: ","}
|
w.sep = ", "
|
||||||
|
w.suffix = ","
|
||||||
itemOpts := o | skipTypes
|
itemOpts := o | skipTypes
|
||||||
for i := 0; i < r.Len(); i++ {
|
for i := 0; i < r.Len(); i++ {
|
||||||
items.items = append(
|
w.items = append(
|
||||||
items.items,
|
w.items,
|
||||||
reflectValue(itemOpts, p, r.Index(i)),
|
reflectValue(itemOpts, p, r.Index(i)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, t, _ := withType(o); !t {
|
if _, t, _ := withType(o); t {
|
||||||
return nodeOf(prefix, "{", items, "}")
|
return nodeOf(reflectType(typ), "{", w, "}")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeOf(reflectType(typ), "{", items, "}")
|
return nodeOf(prefix, "{", w, "}")
|
||||||
}
|
}
|
||||||
|
|
||||||
func reflectHidden(o opts, hidden string, r reflect.Value) node {
|
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)
|
return reflectNil(o, true, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, t, _ := withType(o); !t {
|
if _, t, _ := withType(o); t {
|
||||||
return nodeOf(hidden)
|
return reflectType(r.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
return reflectType(r.Type())
|
return nodeOf(hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reflectArray(o opts, p *pending, r reflect.Value) node {
|
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
|
var skeys []string
|
||||||
items := wrapper{sep: ", ", suffix: ","}
|
|
||||||
itemOpts := o | skipTypes
|
itemOpts := o | skipTypes
|
||||||
keys := r.MapKeys()
|
|
||||||
sv := make(map[string]reflect.Value)
|
sv := make(map[string]reflect.Value)
|
||||||
sn := make(map[string]node)
|
sn := make(map[string]node)
|
||||||
for _, key := range keys {
|
for _, key := range r.MapKeys() {
|
||||||
|
kn := reflectValue(itemOpts, p, key)
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
nk := reflectValue(itemOpts, p, key)
|
|
||||||
wr := writer{w: &b}
|
wr := writer{w: &b}
|
||||||
fprint(&wr, 0, nk)
|
fprint(&wr, 0, kn)
|
||||||
skey := b.String()
|
skey := b.String()
|
||||||
skeys = append(skeys, skey)
|
skeys = append(skeys, skey)
|
||||||
sv[skey] = key
|
sv[skey] = key
|
||||||
sn[skey] = nk
|
sn[skey] = kn
|
||||||
}
|
}
|
||||||
|
|
||||||
if o&randomMaps == 0 {
|
if o&randomMaps == 0 {
|
||||||
sort.Strings(skeys)
|
sort.Strings(skeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w := wrapper{sep: ", ", suffix: ","}
|
||||||
for _, skey := range skeys {
|
for _, skey := range skeys {
|
||||||
items.items = append(
|
vn := reflectValue(itemOpts, p, r.MapIndex(sv[skey]))
|
||||||
items.items,
|
w.items = append(
|
||||||
nodeOf(
|
w.items,
|
||||||
sn[skey],
|
nodeOf(sn[skey], ": ", vn),
|
||||||
": ",
|
|
||||||
reflectValue(
|
|
||||||
itemOpts,
|
|
||||||
p,
|
|
||||||
r.MapIndex(sv[skey]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, t, _ := withType(o); !t {
|
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 {
|
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()
|
rt := r.Type()
|
||||||
for i := 0; i < r.NumField(); i++ {
|
for i := 0; i < r.NumField(); i++ {
|
||||||
name := rt.Field(i).Name
|
name := rt.Field(i).Name
|
||||||
|
fv := reflectValue(fieldOpts, p, r.FieldByName(name))
|
||||||
wr.items = append(
|
wr.items = append(
|
||||||
wr.items,
|
wr.items,
|
||||||
nodeOf(
|
nodeOf(name, ": ", fv),
|
||||||
name,
|
|
||||||
": ",
|
|
||||||
reflectValue(
|
|
||||||
fieldOpts,
|
|
||||||
p,
|
|
||||||
r.FieldByName(name),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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) {
|
func checkPending(p *pending, r reflect.Value) (applyRef func(node) node, ref node, isPending bool) {
|
||||||
applyRef = func(n node) node { return n }
|
applyRef = func(n node) node { return n }
|
||||||
switch r.Kind() {
|
switch r.Kind() {
|
||||||
case reflect.Slice, reflect.Map:
|
case reflect.Slice, reflect.Map, reflect.Ptr:
|
||||||
case reflect.Ptr:
|
default:
|
||||||
if r.IsNil() {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
|
if r.IsNil() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,13 +64,8 @@ func reflectInterfaceType(t reflect.Type) node {
|
|||||||
wr := wrapper{sep: "; "}
|
wr := wrapper{sep: "; "}
|
||||||
for i := 0; i < t.NumMethod(); i++ {
|
for i := 0; i < t.NumMethod(); i++ {
|
||||||
method := t.Method(i)
|
method := t.Method(i)
|
||||||
wr.items = append(
|
mn := nodeOf(method.Name, reflectFuncBaseType(method.Type))
|
||||||
wr.items,
|
wr.items = append(wr.items, mn)
|
||||||
nodeOf(
|
|
||||||
method.Name,
|
|
||||||
reflectFuncBaseType(method.Type),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeOf("interface{", wr, "}")
|
return nodeOf("interface{", wr, "}")
|
||||||
@ -92,14 +87,8 @@ func reflectStructType(t reflect.Type) node {
|
|||||||
wr := wrapper{sep: "; "}
|
wr := wrapper{sep: "; "}
|
||||||
for i := 0; i < t.NumField(); i++ {
|
for i := 0; i < t.NumField(); i++ {
|
||||||
fi := t.Field(i)
|
fi := t.Field(i)
|
||||||
wr.items = append(
|
fn := nodeOf(fi.Name, " ", reflectType(fi.Type))
|
||||||
wr.items,
|
wr.items = append(wr.items, fn)
|
||||||
nodeOf(
|
|
||||||
fi.Name,
|
|
||||||
" ",
|
|
||||||
reflectType(fi.Type),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeOf("struct{", wr, "}")
|
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) {
|
t.Run("no line breaks", func(t *testing.T) {
|
||||||
const expect = `"foobarbazquxquuxquzquuz"`
|
const expect = `"foobarbazquxquuxquzquuz"`
|
||||||
defer withEnv(t, "TABWIDTH=2", "LINEWIDTH=9", "LINEWIDTH1=12")()
|
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) {
|
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 }
|
type typ struct{ f0, f1, f2 *typ }
|
||||||
v0 := new(typ)
|
v0 := new(typ)
|
||||||
v1 := new(typ)
|
v1 := new(typ)
|
||||||
|
Loading…
Reference in New Issue
Block a user