-
Notifications
You must be signed in to change notification settings - Fork 0
/
printer.go
132 lines (127 loc) · 3.55 KB
/
printer.go
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
125
126
127
128
129
130
131
132
package treetop
import (
"fmt"
"io"
"reflect"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
)
// SprintViewInfo will create a string preview of view
func SprintViewInfo(v *View) string {
if v == nil {
return "nil"
}
handlerInfo := "nil"
if v.HandlerFunc != nil {
handlerInfo = runtime.
FuncForPC(reflect.
ValueOf(v.HandlerFunc).
Pointer()).
Name()
}
if v.Defines == "" {
return fmt.Sprintf(
"View(%s, %v)",
strconv.Quote(previewString(v.Template, 20, 30)),
previewString(handlerInfo, 20, 30),
)
}
return fmt.Sprintf(
"SubView(%#v, %s, %v)",
v.Defines,
strconv.Quote(previewString(v.Template, 20, 30)),
previewString(handlerInfo, 20, 30),
)
}
// previewString previews an arbitrary string on a single line.
// All whitespace will be stripped and it will be quoted and escaped.
// A middle ellipsis will be inserted if the string is too long.
func previewString(str string, before, after int) string {
re := regexp.MustCompile(`\s`)
str = re.ReplaceAllString(str, "")
if len(str) > before+after+2 {
return str[:before] + "……" + str[len(str)-after:]
}
return str
}
// SprintViewTree create a string with a tree representation of a a view hierarchy.
//
// For example, the view definition 'v'
//
// v := NewView("base.html", Constant("base!"))
// a := v.NewDefaultSubView("A", "A.html", Constant("A!"))
// a.NewDefaultSubView("A1", "A1.html", Constant("A1!"))
// a.NewDefaultSubView("A2", "A2.html", Constant("A2!"))
// b := v.NewDefaultSubView("B", "B.html", Constant("B!"))
// b.NewDefaultSubView("B1", "B1.html", Constant("B1!"))
// b.NewDefaultSubView("B2", "B2.html", Constant("B2!"))
//
// fmt.Println(treetop.SprintViewTree(v))
//
// will be outputted as the string
//
// - View("base.html", github.com/rur/treetop.Constant.func1)
// |- A: SubView("A", "A.html", github.com/rur/treetop.Constant.func1)
// | |- A1: SubView("A1", "A1.html", github.com/rur/treetop.Constant.func1)
// | '- A2: SubView("A2", "A2.html", github.com/rur/treetop.Constant.func1)
// |
// '- B: SubView("B", "B.html", github.com/rur/treetop.Constant.func1)
// |- B1: SubView("B1", "B1.html", github.com/rur/treetop.Constant.func1)
// '- B2: SubView("B2", "B2.html", github.com/rur/treetop.Constant.func1)
func SprintViewTree(v *View) string {
if v == nil {
return "- nil"
}
str := strings.Builder{}
str.WriteString("- ")
str.WriteString(SprintViewInfo(v))
fprintViewTree(&str, []byte(" "), v.SubViews)
return str.String()
}
// fprintViewTree delves recursively into view and sub views and writes
// a tree prepresentation of the supplied view
func fprintViewTree(w io.Writer, prefix []byte, views map[string]*View) {
subCount := len(views)
keys := make([]string, subCount)
{
i := 0
for k := range views {
keys[i] = k
i++
}
sort.Strings(keys)
}
for i, k := range keys {
last := i == len(keys)-1
sub := views[k]
w.Write(append([]byte{'\n'}, prefix...))
if last {
w.Write([]byte("'- " + k + ": " + SprintViewInfo(sub)))
} else {
w.Write([]byte("|- " + k + ": " + SprintViewInfo(sub)))
}
if sub != nil {
var subPrefix []byte
if last {
subPrefix = append(prefix, []byte(" ")...)
} else {
subPrefix = append(prefix, []byte("| ")...)
}
fprintViewTree(w, subPrefix, sub.SubViews)
}
if last && (sub == nil || len(sub.SubViews) == 0) {
// add padding to mark end of a branch
// add the prefix to padding, without trailing spaces
for j := len(prefix) - 1; j > -1; j-- {
if prefix[j] != ' ' {
w.Write([]byte("\n"))
w.Write(prefix[:j+1])
break
}
}
}
}
}