| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- // Copyright 2013 Google Inc. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package pretty
- import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "strconv"
- "strings"
- )
- // a formatter stores stateful formatting information as well as being
- // an io.Writer for simplicity.
- type formatter struct {
- *bufio.Writer
- *Config
- // Self-referential structure tracking
- tagNumbers map[int]int // tagNumbers[id] = <#n>
- }
- // newFormatter creates a new buffered formatter. For the output to be written
- // to the given writer, this must be accompanied by a call to write (or Flush).
- func newFormatter(cfg *Config, w io.Writer) *formatter {
- return &formatter{
- Writer: bufio.NewWriter(w),
- Config: cfg,
- tagNumbers: make(map[int]int),
- }
- }
- func (f *formatter) write(n node) {
- defer f.Flush()
- n.format(f, "")
- }
- func (f *formatter) tagFor(id int) int {
- if tag, ok := f.tagNumbers[id]; ok {
- return tag
- }
- if f.tagNumbers == nil {
- return 0
- }
- tag := len(f.tagNumbers) + 1
- f.tagNumbers[id] = tag
- return tag
- }
- type node interface {
- format(f *formatter, indent string)
- }
- func (f *formatter) compactString(n node) string {
- switch k := n.(type) {
- case stringVal:
- return string(k)
- case rawVal:
- return string(k)
- }
- buf := new(bytes.Buffer)
- f2 := newFormatter(&Config{Compact: true}, buf)
- f2.tagNumbers = f.tagNumbers // reuse tagNumbers just in case
- f2.write(n)
- return buf.String()
- }
- type stringVal string
- func (str stringVal) format(f *formatter, indent string) {
- f.WriteString(strconv.Quote(string(str)))
- }
- type rawVal string
- func (r rawVal) format(f *formatter, indent string) {
- f.WriteString(string(r))
- }
- type keyval struct {
- key string
- val node
- }
- type keyvals []keyval
- func (l keyvals) format(f *formatter, indent string) {
- f.WriteByte('{')
- switch {
- case f.Compact:
- // All on one line:
- for i, kv := range l {
- if i > 0 {
- f.WriteByte(',')
- }
- f.WriteString(kv.key)
- f.WriteByte(':')
- kv.val.format(f, indent)
- }
- case f.Diffable:
- f.WriteByte('\n')
- inner := indent + " "
- // Each value gets its own line:
- for _, kv := range l {
- f.WriteString(inner)
- f.WriteString(kv.key)
- f.WriteString(": ")
- kv.val.format(f, inner)
- f.WriteString(",\n")
- }
- f.WriteString(indent)
- default:
- keyWidth := 0
- for _, kv := range l {
- if kw := len(kv.key); kw > keyWidth {
- keyWidth = kw
- }
- }
- alignKey := indent + " "
- alignValue := strings.Repeat(" ", keyWidth)
- inner := alignKey + alignValue + " "
- // First and last line shared with bracket:
- for i, kv := range l {
- if i > 0 {
- f.WriteString(",\n")
- f.WriteString(alignKey)
- }
- f.WriteString(kv.key)
- f.WriteString(": ")
- f.WriteString(alignValue[len(kv.key):])
- kv.val.format(f, inner)
- }
- }
- f.WriteByte('}')
- }
- type list []node
- func (l list) format(f *formatter, indent string) {
- if max := f.ShortList; max > 0 {
- short := f.compactString(l)
- if len(short) <= max {
- f.WriteString(short)
- return
- }
- }
- f.WriteByte('[')
- switch {
- case f.Compact:
- // All on one line:
- for i, v := range l {
- if i > 0 {
- f.WriteByte(',')
- }
- v.format(f, indent)
- }
- case f.Diffable:
- f.WriteByte('\n')
- inner := indent + " "
- // Each value gets its own line:
- for _, v := range l {
- f.WriteString(inner)
- v.format(f, inner)
- f.WriteString(",\n")
- }
- f.WriteString(indent)
- default:
- inner := indent + " "
- // First and last line shared with bracket:
- for i, v := range l {
- if i > 0 {
- f.WriteString(",\n")
- f.WriteString(inner)
- }
- v.format(f, inner)
- }
- }
- f.WriteByte(']')
- }
- type ref struct {
- id int
- }
- func (r ref) format(f *formatter, indent string) {
- fmt.Fprintf(f, "<see #%d>", f.tagFor(r.id))
- }
- type target struct {
- id int
- value node
- }
- func (t target) format(f *formatter, indent string) {
- tag := fmt.Sprintf("<#%d> ", f.tagFor(t.id))
- switch {
- case f.Diffable, f.Compact:
- // no indent changes
- default:
- indent += strings.Repeat(" ", len(tag))
- }
- f.WriteString(tag)
- t.value.format(f, indent)
- }
|