2015-10-24 21:04:31 +00:00
|
|
|
package printer
|
2015-10-24 22:23:50 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2015-10-31 12:01:49 +00:00
|
|
|
"sort"
|
2015-10-24 22:23:50 +00:00
|
|
|
|
|
|
|
"github.com/fatih/hcl/ast"
|
2015-10-30 22:15:36 +00:00
|
|
|
"github.com/fatih/hcl/token"
|
2015-10-24 22:23:50 +00:00
|
|
|
)
|
|
|
|
|
2015-10-25 13:08:09 +00:00
|
|
|
const (
|
|
|
|
blank = byte(' ')
|
|
|
|
newline = byte('\n')
|
|
|
|
tab = byte('\t')
|
|
|
|
)
|
|
|
|
|
2015-10-30 22:15:36 +00:00
|
|
|
type printer struct {
|
2015-10-31 12:01:49 +00:00
|
|
|
cfg Config
|
|
|
|
prev ast.Node
|
|
|
|
|
2015-10-30 22:15:36 +00:00
|
|
|
comments []*ast.CommentGroup // may be nil, contains all comments
|
|
|
|
standaloneComments []*ast.CommentGroup // contains all standalone comments (not assigned to any node)
|
2015-10-31 12:01:49 +00:00
|
|
|
|
|
|
|
enableTrace bool
|
|
|
|
indentTrace int
|
2015-10-30 22:15:36 +00:00
|
|
|
}
|
|
|
|
|
2015-10-31 12:01:49 +00:00
|
|
|
type ByPosition []*ast.CommentGroup
|
|
|
|
|
|
|
|
func (b ByPosition) Len() int { return len(b) }
|
|
|
|
func (b ByPosition) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
|
|
|
func (b ByPosition) Less(i, j int) bool { return b[i].Pos().Before(b[j].Pos()) }
|
|
|
|
|
2015-10-30 22:15:36 +00:00
|
|
|
func (p *printer) collectComments(node ast.Node) {
|
2015-10-30 22:19:32 +00:00
|
|
|
// first collect all comments. This is already stored in
|
|
|
|
// ast.File.(comments)
|
2015-10-30 22:15:36 +00:00
|
|
|
ast.Walk(node, func(nn ast.Node) bool {
|
|
|
|
switch t := nn.(type) {
|
|
|
|
case *ast.File:
|
|
|
|
p.comments = t.Comments
|
2015-10-30 22:19:32 +00:00
|
|
|
return false
|
2015-10-30 22:15:36 +00:00
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
standaloneComments := make(map[token.Pos]*ast.CommentGroup, 0)
|
|
|
|
for _, c := range p.comments {
|
|
|
|
standaloneComments[c.Pos()] = c
|
|
|
|
}
|
2015-10-30 22:19:32 +00:00
|
|
|
|
|
|
|
// next remove all lead and line comments from the overall comment map.
|
|
|
|
// This will give us comments which are standalone, comments which are not
|
|
|
|
// assigned to any kind of node.
|
|
|
|
ast.Walk(node, func(nn ast.Node) bool {
|
|
|
|
switch t := nn.(type) {
|
|
|
|
case *ast.ObjectItem:
|
|
|
|
if t.LeadComment != nil {
|
|
|
|
for _, comment := range t.LeadComment.List {
|
|
|
|
if _, ok := standaloneComments[comment.Pos()]; ok {
|
|
|
|
delete(standaloneComments, comment.Pos())
|
|
|
|
}
|
|
|
|
}
|
2015-10-30 22:15:36 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 22:19:32 +00:00
|
|
|
if t.LineComment != nil {
|
|
|
|
for _, comment := range t.LineComment.List {
|
|
|
|
if _, ok := standaloneComments[comment.Pos()]; ok {
|
|
|
|
delete(standaloneComments, comment.Pos())
|
|
|
|
}
|
|
|
|
}
|
2015-10-30 22:15:36 +00:00
|
|
|
}
|
|
|
|
}
|
2015-10-30 22:19:32 +00:00
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
2015-10-30 22:15:36 +00:00
|
|
|
|
|
|
|
for _, c := range standaloneComments {
|
2015-10-31 12:01:49 +00:00
|
|
|
p.standaloneComments = append(p.standaloneComments, c)
|
|
|
|
}
|
|
|
|
sort.Sort(ByPosition(p.standaloneComments))
|
|
|
|
|
|
|
|
fmt.Printf("standaloneComments = %+v\n", len(p.standaloneComments))
|
|
|
|
for _, c := range p.standaloneComments {
|
2015-10-30 22:19:32 +00:00
|
|
|
for _, comment := range c.List {
|
|
|
|
fmt.Printf("comment = %+v\n", comment)
|
|
|
|
}
|
2015-10-30 22:15:36 +00:00
|
|
|
}
|
2015-10-31 12:01:49 +00:00
|
|
|
|
2015-10-30 22:15:36 +00:00
|
|
|
}
|
|
|
|
|
2015-10-31 12:01:49 +00:00
|
|
|
var count int
|
|
|
|
|
|
|
|
// output prints creates b printable HCL output and returns it.
|
2015-10-26 22:23:22 +00:00
|
|
|
func (p *printer) output(n interface{}) []byte {
|
2015-10-24 22:23:50 +00:00
|
|
|
var buf bytes.Buffer
|
2015-10-31 12:01:49 +00:00
|
|
|
count++
|
2015-10-24 22:23:50 +00:00
|
|
|
|
|
|
|
switch t := n.(type) {
|
2015-10-30 19:51:35 +00:00
|
|
|
case *ast.File:
|
|
|
|
return p.output(t.Node)
|
2015-10-24 22:23:50 +00:00
|
|
|
case *ast.ObjectList:
|
2015-10-25 13:08:09 +00:00
|
|
|
for i, item := range t.Items {
|
2015-10-31 12:01:49 +00:00
|
|
|
fmt.Printf("[%d] item: %s\n", i, item.Keys[0].Token.Text)
|
|
|
|
buf.Write(p.output(item))
|
2015-10-25 13:08:09 +00:00
|
|
|
if i != len(t.Items)-1 {
|
2015-10-25 13:12:08 +00:00
|
|
|
buf.Write([]byte{newline, newline})
|
2015-10-25 13:08:09 +00:00
|
|
|
}
|
2015-10-24 22:23:50 +00:00
|
|
|
}
|
|
|
|
case *ast.ObjectKey:
|
2015-10-25 13:08:09 +00:00
|
|
|
buf.WriteString(t.Token.Text)
|
2015-10-24 22:23:50 +00:00
|
|
|
case *ast.ObjectItem:
|
2015-10-31 12:01:49 +00:00
|
|
|
for _, c := range p.standaloneComments {
|
|
|
|
for _, comment := range c.List {
|
|
|
|
fmt.Printf("[%d] OBJECTITEM p.prev = %+v\n", count, p.prev.Pos())
|
|
|
|
fmt.Printf("[%d] OBJECTITEM comment.Pos() = %+v\n", count, comment.Pos())
|
|
|
|
fmt.Printf("[%d] OBJECTTYPE t.Pos() = %+v\n", count, t.Pos())
|
|
|
|
if comment.Pos().After(p.prev.Pos()) && comment.Pos().Before(t.Pos()) {
|
|
|
|
buf.WriteString(comment.Text)
|
2015-10-31 14:45:43 +00:00
|
|
|
// TODO(arslan): do not print new lines if the comments are one lines
|
2015-10-31 12:01:49 +00:00
|
|
|
buf.WriteByte(newline)
|
|
|
|
buf.WriteByte(newline)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p.prev = t
|
2015-10-25 15:10:34 +00:00
|
|
|
buf.Write(p.objectItem(t))
|
2015-10-24 22:23:50 +00:00
|
|
|
case *ast.LiteralType:
|
2015-10-25 13:08:09 +00:00
|
|
|
buf.WriteString(t.Token.Text)
|
2015-10-24 22:23:50 +00:00
|
|
|
case *ast.ListType:
|
2015-10-25 15:10:34 +00:00
|
|
|
buf.Write(p.list(t))
|
2015-10-24 22:23:50 +00:00
|
|
|
case *ast.ObjectType:
|
2015-10-25 15:10:34 +00:00
|
|
|
buf.Write(p.objectType(t))
|
2015-10-24 22:23:50 +00:00
|
|
|
default:
|
|
|
|
fmt.Printf(" unknown type: %T\n", n)
|
|
|
|
}
|
|
|
|
|
2015-10-31 12:01:49 +00:00
|
|
|
// if item, ok := n.(ast.Node); ok {
|
|
|
|
// p.prev = item
|
|
|
|
// }
|
|
|
|
|
2015-10-24 22:23:50 +00:00
|
|
|
return buf.Bytes()
|
|
|
|
}
|
|
|
|
|
2015-10-25 15:10:34 +00:00
|
|
|
func (p *printer) objectItem(o *ast.ObjectItem) []byte {
|
2015-10-31 12:01:49 +00:00
|
|
|
defer un(trace(p, fmt.Sprintf("ObjectItem: %s", o.Keys[0].Token.Text)))
|
2015-10-24 22:23:50 +00:00
|
|
|
var buf bytes.Buffer
|
|
|
|
|
2015-10-26 22:23:22 +00:00
|
|
|
if o.LeadComment != nil {
|
2015-10-26 22:42:05 +00:00
|
|
|
for _, comment := range o.LeadComment.List {
|
|
|
|
buf.WriteString(comment.Text)
|
|
|
|
buf.WriteByte(newline)
|
|
|
|
}
|
2015-10-26 22:23:22 +00:00
|
|
|
}
|
|
|
|
|
2015-10-24 22:23:50 +00:00
|
|
|
for i, k := range o.Keys {
|
|
|
|
buf.WriteString(k.Token.Text)
|
2015-10-25 13:08:09 +00:00
|
|
|
buf.WriteByte(blank)
|
2015-10-24 22:23:50 +00:00
|
|
|
|
|
|
|
// reach end of key
|
2015-10-25 15:59:29 +00:00
|
|
|
if i == len(o.Keys)-1 && len(o.Keys) == 1 {
|
2015-10-24 22:23:50 +00:00
|
|
|
buf.WriteString("=")
|
2015-10-25 13:08:09 +00:00
|
|
|
buf.WriteByte(blank)
|
2015-10-24 22:23:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-25 15:10:34 +00:00
|
|
|
buf.Write(p.output(o.Val))
|
2015-10-26 22:23:22 +00:00
|
|
|
|
|
|
|
if o.Val.Pos().Line == o.Keys[0].Pos().Line && o.LineComment != nil {
|
|
|
|
buf.WriteByte(blank)
|
2015-10-26 22:42:05 +00:00
|
|
|
for _, comment := range o.LineComment.List {
|
|
|
|
buf.WriteString(comment.Text)
|
|
|
|
}
|
2015-10-26 22:23:22 +00:00
|
|
|
}
|
|
|
|
|
2015-10-24 22:23:50 +00:00
|
|
|
return buf.Bytes()
|
|
|
|
}
|
|
|
|
|
2015-10-31 12:01:49 +00:00
|
|
|
func (p *printer) objectType(o *ast.ObjectType) []byte {
|
|
|
|
defer un(trace(p, "ObjectType"))
|
2015-10-27 20:51:44 +00:00
|
|
|
var buf bytes.Buffer
|
2015-10-31 12:01:49 +00:00
|
|
|
buf.WriteString("{")
|
|
|
|
buf.WriteByte(newline)
|
2015-10-27 20:51:44 +00:00
|
|
|
|
2015-10-31 14:45:43 +00:00
|
|
|
var index int
|
|
|
|
var nextItem token.Pos
|
|
|
|
for {
|
|
|
|
for _, c := range p.standaloneComments {
|
|
|
|
for _, comment := range c.List {
|
|
|
|
fmt.Printf("[%d] OBJECTTYPE p.prev = %+v\n", count, p.prev.Pos())
|
|
|
|
fmt.Printf("[%d] OBJECTTYPE comment.Pos() = %+v\n", count, comment.Pos())
|
|
|
|
|
|
|
|
if index != len(o.List.Items) {
|
|
|
|
nextItem = o.List.Items[index].Pos()
|
|
|
|
} else {
|
|
|
|
nextItem = o.Rbrace
|
|
|
|
|
|
|
|
}
|
|
|
|
fmt.Printf("[%d] OBJECTTYPE nextItem = %+v\n", count, nextItem)
|
|
|
|
if comment.Pos().After(p.prev.Pos()) && comment.Pos().Before(nextItem) {
|
|
|
|
buf.Write(p.indent([]byte(comment.Text))) // TODO(arslan): indent
|
|
|
|
buf.WriteByte(newline)
|
|
|
|
buf.WriteByte(newline)
|
|
|
|
}
|
2015-10-30 18:49:10 +00:00
|
|
|
}
|
|
|
|
}
|
2015-10-27 20:51:44 +00:00
|
|
|
|
2015-10-31 14:45:43 +00:00
|
|
|
if index == len(o.List.Items) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2015-10-27 23:40:51 +00:00
|
|
|
// check if we have adjacent one liner items. If yes we'll going to align
|
|
|
|
// the comments.
|
2015-10-30 18:49:10 +00:00
|
|
|
var aligned []*ast.ObjectItem
|
2015-10-31 14:45:43 +00:00
|
|
|
for _, item := range o.List.Items[index:] {
|
2015-10-27 23:40:51 +00:00
|
|
|
// we don't group one line lists
|
|
|
|
if len(o.List.Items) == 1 {
|
2015-10-27 21:59:54 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2015-10-30 18:49:10 +00:00
|
|
|
// one means a oneliner with out any lead comment
|
|
|
|
// two means a oneliner with lead comment
|
|
|
|
// anything else might be something else
|
2015-10-27 23:40:51 +00:00
|
|
|
cur := lines(string(p.objectItem(item)))
|
2015-10-30 18:49:10 +00:00
|
|
|
if cur > 2 {
|
2015-10-27 23:40:51 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2015-10-31 14:45:43 +00:00
|
|
|
curPos := item.Pos()
|
|
|
|
|
|
|
|
nextPos := token.Pos{}
|
2015-10-27 23:40:51 +00:00
|
|
|
if index != len(o.List.Items)-1 {
|
2015-10-31 14:45:43 +00:00
|
|
|
nextPos = o.List.Items[index+1].Pos()
|
2015-10-27 23:40:51 +00:00
|
|
|
}
|
|
|
|
|
2015-10-31 14:45:43 +00:00
|
|
|
prevPos := token.Pos{}
|
2015-10-27 23:40:51 +00:00
|
|
|
if index != 0 {
|
2015-10-31 14:45:43 +00:00
|
|
|
prevPos = o.List.Items[index-1].Pos()
|
2015-10-27 23:40:51 +00:00
|
|
|
}
|
|
|
|
|
2015-10-31 14:45:43 +00:00
|
|
|
// fmt.Println("DEBUG ----------------")
|
|
|
|
// fmt.Printf("prev = %+v prevPos: %s\n", prev, prevPos)
|
|
|
|
// fmt.Printf("cur = %+v curPos: %s\n", cur, curPos)
|
|
|
|
// fmt.Printf("next = %+v nextPos: %s\n", next, nextPos)
|
|
|
|
|
|
|
|
if curPos.Line+1 == nextPos.Line {
|
2015-10-30 18:49:10 +00:00
|
|
|
aligned = append(aligned, item)
|
2015-10-27 23:40:51 +00:00
|
|
|
index++
|
2015-10-31 14:45:43 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if curPos.Line-1 == prevPos.Line {
|
2015-10-30 18:49:10 +00:00
|
|
|
aligned = append(aligned, item)
|
2015-10-27 21:59:54 +00:00
|
|
|
index++
|
2015-10-31 14:45:43 +00:00
|
|
|
|
|
|
|
// finish if we have a new line or comment next. This happens
|
|
|
|
// if the next item is not adjacent
|
|
|
|
if curPos.Line+1 != nextPos.Line {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
continue
|
2015-10-27 21:59:54 +00:00
|
|
|
}
|
2015-10-31 14:45:43 +00:00
|
|
|
|
|
|
|
break
|
2015-10-27 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
2015-10-31 14:45:43 +00:00
|
|
|
fmt.Printf("==================> len(aligned) = %+v\n", len(aligned))
|
|
|
|
for _, b := range aligned {
|
|
|
|
fmt.Printf("b = %+v\n", b)
|
|
|
|
}
|
2015-10-30 18:49:10 +00:00
|
|
|
|
|
|
|
// put newlines if the items are between other non aligned items
|
|
|
|
if index != len(aligned) {
|
|
|
|
buf.WriteByte(newline)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(aligned) >= 1 {
|
2015-10-31 12:01:49 +00:00
|
|
|
p.prev = aligned[len(aligned)-1]
|
2015-10-27 23:40:51 +00:00
|
|
|
|
2015-10-31 12:01:49 +00:00
|
|
|
items := p.alignedItems(aligned)
|
2015-10-27 21:59:54 +00:00
|
|
|
buf.Write(p.indent(items))
|
2015-10-30 18:49:10 +00:00
|
|
|
} else {
|
2015-10-31 12:01:49 +00:00
|
|
|
p.prev = o.List.Items[index]
|
|
|
|
|
2015-10-30 18:49:10 +00:00
|
|
|
buf.Write(p.indent(p.objectItem(o.List.Items[index])))
|
|
|
|
index++
|
2015-10-27 20:51:44 +00:00
|
|
|
}
|
|
|
|
|
2015-10-30 18:49:10 +00:00
|
|
|
buf.WriteByte(newline)
|
|
|
|
|
2015-10-25 13:08:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
buf.WriteString("}")
|
|
|
|
return buf.Bytes()
|
|
|
|
}
|
|
|
|
|
2015-10-31 12:01:49 +00:00
|
|
|
func (p *printer) alignedItems(items []*ast.ObjectItem) []byte {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
|
|
|
var longestLine int
|
|
|
|
for _, item := range items {
|
|
|
|
lineLen := len(item.Keys[0].Token.Text) + len(p.output(item.Val))
|
|
|
|
if lineLen > longestLine {
|
|
|
|
longestLine = lineLen
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, item := range items {
|
|
|
|
if item.LeadComment != nil {
|
|
|
|
for _, comment := range item.LeadComment.List {
|
|
|
|
buf.WriteString(comment.Text)
|
|
|
|
buf.WriteByte(newline)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
curLen := 0
|
|
|
|
for i, k := range item.Keys {
|
|
|
|
buf.WriteString(k.Token.Text)
|
|
|
|
buf.WriteByte(blank)
|
|
|
|
|
|
|
|
// reach end of key
|
|
|
|
if i == len(item.Keys)-1 && len(item.Keys) == 1 {
|
|
|
|
buf.WriteString("=")
|
|
|
|
buf.WriteByte(blank)
|
|
|
|
}
|
|
|
|
|
|
|
|
curLen = len(k.Token.Text) // two blanks and one assign
|
|
|
|
}
|
|
|
|
val := p.output(item.Val)
|
|
|
|
buf.Write(val)
|
|
|
|
curLen += len(val)
|
|
|
|
|
|
|
|
if item.Val.Pos().Line == item.Keys[0].Pos().Line && item.LineComment != nil {
|
|
|
|
for i := 0; i < longestLine-curLen+1; i++ {
|
|
|
|
buf.WriteByte(blank)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, comment := range item.LineComment.List {
|
|
|
|
buf.WriteString(comment.Text)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// do not print for the last item
|
|
|
|
if i != len(items)-1 {
|
|
|
|
buf.WriteByte(newline)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) literal(l *ast.LiteralType) []byte {
|
|
|
|
return []byte(l.Token.Text)
|
|
|
|
}
|
|
|
|
|
2015-10-25 15:10:34 +00:00
|
|
|
// printList prints a HCL list
|
|
|
|
func (p *printer) list(l *ast.ListType) []byte {
|
2015-10-24 22:23:50 +00:00
|
|
|
var buf bytes.Buffer
|
|
|
|
buf.WriteString("[")
|
|
|
|
|
|
|
|
for i, item := range l.List {
|
|
|
|
if item.Pos().Line != l.Lbrack.Line {
|
2015-10-25 16:09:22 +00:00
|
|
|
// multiline list, add newline before we add each item
|
2015-10-25 13:08:09 +00:00
|
|
|
buf.WriteByte(newline)
|
2015-10-25 16:09:22 +00:00
|
|
|
// also indent each line
|
|
|
|
buf.Write(p.indent(p.output(item)))
|
|
|
|
} else {
|
|
|
|
buf.Write(p.output(item))
|
2015-10-24 22:23:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if i != len(l.List)-1 {
|
|
|
|
buf.WriteString(",")
|
2015-10-25 13:08:09 +00:00
|
|
|
buf.WriteByte(blank)
|
2015-10-24 22:23:50 +00:00
|
|
|
} else if item.Pos().Line != l.Lbrack.Line {
|
|
|
|
buf.WriteString(",")
|
2015-10-25 13:08:09 +00:00
|
|
|
buf.WriteByte(newline)
|
2015-10-24 22:23:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.WriteString("]")
|
|
|
|
return buf.Bytes()
|
|
|
|
}
|
2015-10-25 13:08:09 +00:00
|
|
|
|
2015-10-25 15:10:34 +00:00
|
|
|
// indent indents the lines of the given buffer for each non-empty line
|
2015-10-25 15:02:40 +00:00
|
|
|
func (p *printer) indent(buf []byte) []byte {
|
2015-10-25 15:10:34 +00:00
|
|
|
var prefix []byte
|
|
|
|
if p.cfg.SpacesWidth != 0 {
|
|
|
|
for i := 0; i < p.cfg.SpacesWidth; i++ {
|
2015-10-25 15:02:40 +00:00
|
|
|
prefix = append(prefix, blank)
|
|
|
|
}
|
2015-10-25 15:10:34 +00:00
|
|
|
} else {
|
|
|
|
prefix = []byte{tab}
|
2015-10-25 15:02:40 +00:00
|
|
|
}
|
|
|
|
|
2015-10-25 14:51:18 +00:00
|
|
|
var res []byte
|
|
|
|
bol := true
|
|
|
|
for _, c := range buf {
|
|
|
|
if bol && c != '\n' {
|
2015-10-25 15:02:40 +00:00
|
|
|
res = append(res, prefix...)
|
2015-10-25 13:08:09 +00:00
|
|
|
}
|
2015-10-25 14:51:18 +00:00
|
|
|
res = append(res, c)
|
|
|
|
bol = c == '\n'
|
2015-10-25 13:08:09 +00:00
|
|
|
}
|
2015-10-25 14:51:18 +00:00
|
|
|
return res
|
2015-10-25 13:08:09 +00:00
|
|
|
}
|
2015-10-27 21:59:54 +00:00
|
|
|
|
|
|
|
func lines(txt string) int {
|
2015-10-30 18:49:10 +00:00
|
|
|
endline := 1
|
2015-10-27 21:59:54 +00:00
|
|
|
for i := 0; i < len(txt); i++ {
|
|
|
|
if txt[i] == '\n' {
|
|
|
|
endline++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return endline
|
|
|
|
}
|
2015-10-31 12:01:49 +00:00
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Tracing support
|
|
|
|
|
|
|
|
func (p *printer) printTrace(a ...interface{}) {
|
|
|
|
if !p.enableTrace {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . "
|
|
|
|
const n = len(dots)
|
|
|
|
i := 2 * p.indentTrace
|
|
|
|
for i > n {
|
|
|
|
fmt.Print(dots)
|
|
|
|
i -= n
|
|
|
|
}
|
|
|
|
// i <= n
|
|
|
|
fmt.Print(dots[0:i])
|
|
|
|
fmt.Println(a...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func trace(p *printer, msg string) *printer {
|
|
|
|
p.printTrace(msg, "(")
|
|
|
|
p.indentTrace++
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
// Usage pattern: defer un(trace(p, "..."))
|
|
|
|
func un(p *printer) {
|
|
|
|
p.indentTrace--
|
|
|
|
p.printTrace(")")
|
|
|
|
}
|