File: dump.go

package info (click to toggle)
golang-github-apparentlymart-go-dump 0.0~git20190214.042adf3-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 72 kB
  • sloc: makefile: 2
file content (124 lines) | stat: -rw-r--r-- 2,945 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Package dump contains some helper functions for printing and formatting
// values.
package dump

import (
	"bytes"
	"fmt"
	"go/scanner"
	"go/token"
	"strings"
)

// Value produces a formatted string representation of the given value.
//
// The result is a pretty-printed version of the result of the fmt.GoStringer
// implementation for the given value. The pretty-printing expects the
// GoString result to be a valid Go expression; if it is not then the result
// may be sub-optimal but this function will still make a best effort.
//
// This function is intended primarily as a helper for writing unit tests, and
// so it is not optimized for performance in any way.
func Value(v interface{}) string {
	return prettyPrint(fmt.Sprintf("%#v", v))
}

func prettyPrint(s string) string {
	var buf bytes.Buffer
	fs := token.NewFileSet()
	f := fs.AddFile("", -1, len(s))
	sc := &scanner.Scanner{}
	sc.Init(f, []byte(s), nil, scanner.ScanComments)

	type Token struct {
		Type token.Token
		Str  string
	}

	var tokens []Token
	for {
		_, tok, lit := sc.Scan()

		switch tok {
		case token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.COMMENT, token.SEMICOLON, token.ILLEGAL:
			// "lit" is already populated
		default:
			lit = tok.String()
		}

		tokens = append(tokens, Token{
			Type: tok,
			Str:  lit,
		})

		if tok == token.EOF {
			break
		}
	}

	indent := 0
	otherBrackets := 0
	emptyBraces := false
	for i, tok := range tokens {
		if tok.Type == token.EOF {
			break
		}
		nextTok := tokens[i+1]

		switch {
		case tok.Type == token.LBRACE:
			if otherBrackets > 0 {
				buf.WriteString("{")
				continue
			}
			if nextTok.Type == token.RBRACE {
				buf.WriteString("{")
				emptyBraces = true
				continue
			}
			indent++
			buf.WriteString("{\n" + strings.Repeat("  ", indent))
		case tok.Type == token.RBRACE:
			if otherBrackets > 0 {
				buf.WriteString("}")
				continue
			}
			if emptyBraces {
				buf.WriteString("}")
				emptyBraces = false
				continue
			}
			indent--
			sp := strings.Repeat("  ", indent)
			if nextTok.Type == token.SEMICOLON || nextTok.Type == token.COMMA || nextTok.Type == token.RBRACE {
				buf.WriteString(fmt.Sprintf("\n%s}", sp))
			} else {
				buf.WriteString(fmt.Sprintf("\n%s}\n%s", sp, sp))
			}
		case tok.Type == token.LBRACK || tok.Type == token.LPAREN:
			buf.WriteString(tok.Str)
			otherBrackets++
		case tok.Type == token.RBRACK || tok.Type == token.RPAREN:
			buf.WriteString(tok.Str)
			otherBrackets--
		case tok.Type == token.COMMA:
			if otherBrackets > 0 {
				buf.WriteString(", ")
				continue
			}
			buf.WriteString(",\n" + strings.Repeat("  ", indent))
		case tok.Type == token.SEMICOLON:
			if otherBrackets > 0 {
				buf.WriteString("; ")
				continue
			}
			buf.WriteString("\n" + strings.Repeat("  ", indent))
		case tok.Type == token.COLON:
			buf.WriteString(": ")
		default:
			buf.WriteString(tok.Str)
		}
	}

	return buf.String()
}