aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go')
-rw-r--r--vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go789
1 files changed, 0 insertions, 789 deletions
diff --git a/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go b/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go
deleted file mode 100644
index 7c038d1..0000000
--- a/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go
+++ /dev/null
@@ -1,789 +0,0 @@
-package printer
-
-import (
- "bytes"
- "fmt"
- "sort"
-
- "github.com/hashicorp/hcl/hcl/ast"
- "github.com/hashicorp/hcl/hcl/token"
-)
-
-const (
- blank = byte(' ')
- newline = byte('\n')
- tab = byte('\t')
- infinity = 1 << 30 // offset or line
-)
-
-var (
- unindent = []byte("\uE123") // in the private use space
-)
-
-type printer struct {
- cfg Config
- prev token.Pos
-
- comments []*ast.CommentGroup // may be nil, contains all comments
- standaloneComments []*ast.CommentGroup // contains all standalone comments (not assigned to any node)
-
- enableTrace bool
- indentTrace int
-}
-
-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()) }
-
-// collectComments comments all standalone comments which are not lead or line
-// comment
-func (p *printer) collectComments(node ast.Node) {
- // first collect all comments. This is already stored in
- // ast.File.(comments)
- ast.Walk(node, func(nn ast.Node) (ast.Node, bool) {
- switch t := nn.(type) {
- case *ast.File:
- p.comments = t.Comments
- return nn, false
- }
- return nn, true
- })
-
- standaloneComments := make(map[token.Pos]*ast.CommentGroup, 0)
- for _, c := range p.comments {
- standaloneComments[c.Pos()] = c
- }
-
- // 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) (ast.Node, bool) {
- switch t := nn.(type) {
- case *ast.LiteralType:
- if t.LeadComment != nil {
- for _, comment := range t.LeadComment.List {
- if _, ok := standaloneComments[comment.Pos()]; ok {
- delete(standaloneComments, comment.Pos())
- }
- }
- }
-
- if t.LineComment != nil {
- for _, comment := range t.LineComment.List {
- if _, ok := standaloneComments[comment.Pos()]; ok {
- delete(standaloneComments, comment.Pos())
- }
- }
- }
- case *ast.ObjectItem:
- if t.LeadComment != nil {
- for _, comment := range t.LeadComment.List {
- if _, ok := standaloneComments[comment.Pos()]; ok {
- delete(standaloneComments, comment.Pos())
- }
- }
- }
-
- if t.LineComment != nil {
- for _, comment := range t.LineComment.List {
- if _, ok := standaloneComments[comment.Pos()]; ok {
- delete(standaloneComments, comment.Pos())
- }
- }
- }
- }
-
- return nn, true
- })
-
- for _, c := range standaloneComments {
- p.standaloneComments = append(p.standaloneComments, c)
- }
-
- sort.Sort(ByPosition(p.standaloneComments))
-}
-
-// output prints creates b printable HCL output and returns it.
-func (p *printer) output(n interface{}) []byte {
- var buf bytes.Buffer
-
- switch t := n.(type) {
- case *ast.File:
- // File doesn't trace so we add the tracing here
- defer un(trace(p, "File"))
- return p.output(t.Node)
- case *ast.ObjectList:
- defer un(trace(p, "ObjectList"))
-
- var index int
- for {
- // Determine the location of the next actual non-comment
- // item. If we're at the end, the next item is at "infinity"
- var nextItem token.Pos
- if index != len(t.Items) {
- nextItem = t.Items[index].Pos()
- } else {
- nextItem = token.Pos{Offset: infinity, Line: infinity}
- }
-
- // Go through the standalone comments in the file and print out
- // the comments that we should be for this object item.
- for _, c := range p.standaloneComments {
- // Go through all the comments in the group. The group
- // should be printed together, not separated by double newlines.
- printed := false
- newlinePrinted := false
- for _, comment := range c.List {
- // We only care about comments after the previous item
- // we've printed so that comments are printed in the
- // correct locations (between two objects for example).
- // And before the next item.
- if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) {
- // if we hit the end add newlines so we can print the comment
- // we don't do this if prev is invalid which means the
- // beginning of the file since the first comment should
- // be at the first line.
- if !newlinePrinted && p.prev.IsValid() && index == len(t.Items) {
- buf.Write([]byte{newline, newline})
- newlinePrinted = true
- }
-
- // Write the actual comment.
- buf.WriteString(comment.Text)
- buf.WriteByte(newline)
-
- // Set printed to true to note that we printed something
- printed = true
- }
- }
-
- // If we're not at the last item, write a new line so
- // that there is a newline separating this comment from
- // the next object.
- if printed && index != len(t.Items) {
- buf.WriteByte(newline)
- }
- }
-
- if index == len(t.Items) {
- break
- }
-
- buf.Write(p.output(t.Items[index]))
- if index != len(t.Items)-1 {
- // Always write a newline to separate us from the next item
- buf.WriteByte(newline)
-
- // Need to determine if we're going to separate the next item
- // with a blank line. The logic here is simple, though there
- // are a few conditions:
- //
- // 1. The next object is more than one line away anyways,
- // so we need an empty line.
- //
- // 2. The next object is not a "single line" object, so
- // we need an empty line.
- //
- // 3. This current object is not a single line object,
- // so we need an empty line.
- current := t.Items[index]
- next := t.Items[index+1]
- if next.Pos().Line != t.Items[index].Pos().Line+1 ||
- !p.isSingleLineObject(next) ||
- !p.isSingleLineObject(current) {
- buf.WriteByte(newline)
- }
- }
- index++
- }
- case *ast.ObjectKey:
- buf.WriteString(t.Token.Text)
- case *ast.ObjectItem:
- p.prev = t.Pos()
- buf.Write(p.objectItem(t))
- case *ast.LiteralType:
- buf.Write(p.literalType(t))
- case *ast.ListType:
- buf.Write(p.list(t))
- case *ast.ObjectType:
- buf.Write(p.objectType(t))
- default:
- fmt.Printf(" unknown type: %T\n", n)
- }
-
- return buf.Bytes()
-}
-
-func (p *printer) literalType(lit *ast.LiteralType) []byte {
- result := []byte(lit.Token.Text)
- switch lit.Token.Type {
- case token.HEREDOC:
- // Clear the trailing newline from heredocs
- if result[len(result)-1] == '\n' {
- result = result[:len(result)-1]
- }
-
- // Poison lines 2+ so that we don't indent them
- result = p.heredocIndent(result)
- case token.STRING:
- // If this is a multiline string, poison lines 2+ so we don't
- // indent them.
- if bytes.IndexRune(result, '\n') >= 0 {
- result = p.heredocIndent(result)
- }
- }
-
- return result
-}
-
-// objectItem returns the printable HCL form of an object item. An object type
-// starts with one/multiple keys and has a value. The value might be of any
-// type.
-func (p *printer) objectItem(o *ast.ObjectItem) []byte {
- defer un(trace(p, fmt.Sprintf("ObjectItem: %s", o.Keys[0].Token.Text)))
- var buf bytes.Buffer
-
- if o.LeadComment != nil {
- for _, comment := range o.LeadComment.List {
- buf.WriteString(comment.Text)
- buf.WriteByte(newline)
- }
- }
-
- // If key and val are on different lines, treat line comments like lead comments.
- if o.LineComment != nil && o.Val.Pos().Line != o.Keys[0].Pos().Line {
- for _, comment := range o.LineComment.List {
- buf.WriteString(comment.Text)
- buf.WriteByte(newline)
- }
- }
-
- for i, k := range o.Keys {
- buf.WriteString(k.Token.Text)
- buf.WriteByte(blank)
-
- // reach end of key
- if o.Assign.IsValid() && i == len(o.Keys)-1 && len(o.Keys) == 1 {
- buf.WriteString("=")
- buf.WriteByte(blank)
- }
- }
-
- buf.Write(p.output(o.Val))
-
- if o.LineComment != nil && o.Val.Pos().Line == o.Keys[0].Pos().Line {
- buf.WriteByte(blank)
- for _, comment := range o.LineComment.List {
- buf.WriteString(comment.Text)
- }
- }
-
- return buf.Bytes()
-}
-
-// objectType returns the printable HCL form of an object type. An object type
-// begins with a brace and ends with a brace.
-func (p *printer) objectType(o *ast.ObjectType) []byte {
- defer un(trace(p, "ObjectType"))
- var buf bytes.Buffer
- buf.WriteString("{")
-
- var index int
- var nextItem token.Pos
- var commented, newlinePrinted bool
- for {
- // Determine the location of the next actual non-comment
- // item. If we're at the end, the next item is the closing brace
- if index != len(o.List.Items) {
- nextItem = o.List.Items[index].Pos()
- } else {
- nextItem = o.Rbrace
- }
-
- // Go through the standalone comments in the file and print out
- // the comments that we should be for this object item.
- for _, c := range p.standaloneComments {
- printed := false
- var lastCommentPos token.Pos
- for _, comment := range c.List {
- // We only care about comments after the previous item
- // we've printed so that comments are printed in the
- // correct locations (between two objects for example).
- // And before the next item.
- if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) {
- // If there are standalone comments and the initial newline has not
- // been printed yet, do it now.
- if !newlinePrinted {
- newlinePrinted = true
- buf.WriteByte(newline)
- }
-
- // add newline if it's between other printed nodes
- if index > 0 {
- commented = true
- buf.WriteByte(newline)
- }
-
- // Store this position
- lastCommentPos = comment.Pos()
-
- // output the comment itself
- buf.Write(p.indent(p.heredocIndent([]byte(comment.Text))))
-
- // Set printed to true to note that we printed something
- printed = true
-
- /*
- if index != len(o.List.Items) {
- buf.WriteByte(newline) // do not print on the end
- }
- */
- }
- }
-
- // Stuff to do if we had comments
- if printed {
- // Always write a newline
- buf.WriteByte(newline)
-
- // If there is another item in the object and our comment
- // didn't hug it directly, then make sure there is a blank
- // line separating them.
- if nextItem != o.Rbrace && nextItem.Line != lastCommentPos.Line+1 {
- buf.WriteByte(newline)
- }
- }
- }
-
- if index == len(o.List.Items) {
- p.prev = o.Rbrace
- break
- }
-
- // At this point we are sure that it's not a totally empty block: print
- // the initial newline if it hasn't been printed yet by the previous
- // block about standalone comments.
- if !newlinePrinted {
- buf.WriteByte(newline)
- newlinePrinted = true
- }
-
- // check if we have adjacent one liner items. If yes we'll going to align
- // the comments.
- var aligned []*ast.ObjectItem
- for _, item := range o.List.Items[index:] {
- // we don't group one line lists
- if len(o.List.Items) == 1 {
- break
- }
-
- // one means a oneliner with out any lead comment
- // two means a oneliner with lead comment
- // anything else might be something else
- cur := lines(string(p.objectItem(item)))
- if cur > 2 {
- break
- }
-
- curPos := item.Pos()
-
- nextPos := token.Pos{}
- if index != len(o.List.Items)-1 {
- nextPos = o.List.Items[index+1].Pos()
- }
-
- prevPos := token.Pos{}
- if index != 0 {
- prevPos = o.List.Items[index-1].Pos()
- }
-
- // 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 {
- aligned = append(aligned, item)
- index++
- continue
- }
-
- if curPos.Line-1 == prevPos.Line {
- aligned = append(aligned, item)
- index++
-
- // 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
- }
-
- break
- }
-
- // put newlines if the items are between other non aligned items.
- // newlines are also added if there is a standalone comment already, so
- // check it too
- if !commented && index != len(aligned) {
- buf.WriteByte(newline)
- }
-
- if len(aligned) >= 1 {
- p.prev = aligned[len(aligned)-1].Pos()
-
- items := p.alignedItems(aligned)
- buf.Write(p.indent(items))
- } else {
- p.prev = o.List.Items[index].Pos()
-
- buf.Write(p.indent(p.objectItem(o.List.Items[index])))
- index++
- }
-
- buf.WriteByte(newline)
- }
-
- buf.WriteString("}")
- return buf.Bytes()
-}
-
-func (p *printer) alignedItems(items []*ast.ObjectItem) []byte {
- var buf bytes.Buffer
-
- // find the longest key and value length, needed for alignment
- var longestKeyLen int // longest key length
- var longestValLen int // longest value length
- for _, item := range items {
- key := len(item.Keys[0].Token.Text)
- val := len(p.output(item.Val))
-
- if key > longestKeyLen {
- longestKeyLen = key
- }
-
- if val > longestValLen {
- longestValLen = val
- }
- }
-
- for i, item := range items {
- if item.LeadComment != nil {
- for _, comment := range item.LeadComment.List {
- buf.WriteString(comment.Text)
- buf.WriteByte(newline)
- }
- }
-
- for i, k := range item.Keys {
- keyLen := len(k.Token.Text)
- buf.WriteString(k.Token.Text)
- for i := 0; i < longestKeyLen-keyLen+1; i++ {
- buf.WriteByte(blank)
- }
-
- // reach end of key
- if i == len(item.Keys)-1 && len(item.Keys) == 1 {
- buf.WriteString("=")
- buf.WriteByte(blank)
- }
- }
-
- val := p.output(item.Val)
- valLen := len(val)
- buf.Write(val)
-
- if item.Val.Pos().Line == item.Keys[0].Pos().Line && item.LineComment != nil {
- for i := 0; i < longestValLen-valLen+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()
-}
-
-// list returns the printable HCL form of an list type.
-func (p *printer) list(l *ast.ListType) []byte {
- if p.isSingleLineList(l) {
- return p.singleLineList(l)
- }
-
- var buf bytes.Buffer
- buf.WriteString("[")
- buf.WriteByte(newline)
-
- var longestLine int
- for _, item := range l.List {
- // for now we assume that the list only contains literal types
- if lit, ok := item.(*ast.LiteralType); ok {
- lineLen := len(lit.Token.Text)
- if lineLen > longestLine {
- longestLine = lineLen
- }
- }
- }
-
- haveEmptyLine := false
- for i, item := range l.List {
- // If we have a lead comment, then we want to write that first
- leadComment := false
- if lit, ok := item.(*ast.LiteralType); ok && lit.LeadComment != nil {
- leadComment = true
-
- // Ensure an empty line before every element with a
- // lead comment (except the first item in a list).
- if !haveEmptyLine && i != 0 {
- buf.WriteByte(newline)
- }
-
- for _, comment := range lit.LeadComment.List {
- buf.Write(p.indent([]byte(comment.Text)))
- buf.WriteByte(newline)
- }
- }
-
- // also indent each line
- val := p.output(item)
- curLen := len(val)
- buf.Write(p.indent(val))
-
- // if this item is a heredoc, then we output the comma on
- // the next line. This is the only case this happens.
- comma := []byte{','}
- if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC {
- buf.WriteByte(newline)
- comma = p.indent(comma)
- }
-
- buf.Write(comma)
-
- if lit, ok := item.(*ast.LiteralType); ok && lit.LineComment != nil {
- // if the next item doesn't have any comments, do not align
- buf.WriteByte(blank) // align one space
- for i := 0; i < longestLine-curLen; i++ {
- buf.WriteByte(blank)
- }
-
- for _, comment := range lit.LineComment.List {
- buf.WriteString(comment.Text)
- }
- }
-
- buf.WriteByte(newline)
-
- // Ensure an empty line after every element with a
- // lead comment (except the first item in a list).
- haveEmptyLine = leadComment && i != len(l.List)-1
- if haveEmptyLine {
- buf.WriteByte(newline)
- }
- }
-
- buf.WriteString("]")
- return buf.Bytes()
-}
-
-// isSingleLineList returns true if:
-// * they were previously formatted entirely on one line
-// * they consist entirely of literals
-// * there are either no heredoc strings or the list has exactly one element
-// * there are no line comments
-func (printer) isSingleLineList(l *ast.ListType) bool {
- for _, item := range l.List {
- if item.Pos().Line != l.Lbrack.Line {
- return false
- }
-
- lit, ok := item.(*ast.LiteralType)
- if !ok {
- return false
- }
-
- if lit.Token.Type == token.HEREDOC && len(l.List) != 1 {
- return false
- }
-
- if lit.LineComment != nil {
- return false
- }
- }
-
- return true
-}
-
-// singleLineList prints a simple single line list.
-// For a definition of "simple", see isSingleLineList above.
-func (p *printer) singleLineList(l *ast.ListType) []byte {
- buf := &bytes.Buffer{}
-
- buf.WriteString("[")
- for i, item := range l.List {
- if i != 0 {
- buf.WriteString(", ")
- }
-
- // Output the item itself
- buf.Write(p.output(item))
-
- // The heredoc marker needs to be at the end of line.
- if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC {
- buf.WriteByte(newline)
- }
- }
-
- buf.WriteString("]")
- return buf.Bytes()
-}
-
-// indent indents the lines of the given buffer for each non-empty line
-func (p *printer) indent(buf []byte) []byte {
- var prefix []byte
- if p.cfg.SpacesWidth != 0 {
- for i := 0; i < p.cfg.SpacesWidth; i++ {
- prefix = append(prefix, blank)
- }
- } else {
- prefix = []byte{tab}
- }
-
- var res []byte
- bol := true
- for _, c := range buf {
- if bol && c != '\n' {
- res = append(res, prefix...)
- }
-
- res = append(res, c)
- bol = c == '\n'
- }
- return res
-}
-
-// unindent removes all the indentation from the tombstoned lines
-func (p *printer) unindent(buf []byte) []byte {
- var res []byte
- for i := 0; i < len(buf); i++ {
- skip := len(buf)-i <= len(unindent)
- if !skip {
- skip = !bytes.Equal(unindent, buf[i:i+len(unindent)])
- }
- if skip {
- res = append(res, buf[i])
- continue
- }
-
- // We have a marker. we have to backtrace here and clean out
- // any whitespace ahead of our tombstone up to a \n
- for j := len(res) - 1; j >= 0; j-- {
- if res[j] == '\n' {
- break
- }
-
- res = res[:j]
- }
-
- // Skip the entire unindent marker
- i += len(unindent) - 1
- }
-
- return res
-}
-
-// heredocIndent marks all the 2nd and further lines as unindentable
-func (p *printer) heredocIndent(buf []byte) []byte {
- var res []byte
- bol := false
- for _, c := range buf {
- if bol && c != '\n' {
- res = append(res, unindent...)
- }
- res = append(res, c)
- bol = c == '\n'
- }
- return res
-}
-
-// isSingleLineObject tells whether the given object item is a single
-// line object such as "obj {}".
-//
-// A single line object:
-//
-// * has no lead comments (hence multi-line)
-// * has no assignment
-// * has no values in the stanza (within {})
-//
-func (p *printer) isSingleLineObject(val *ast.ObjectItem) bool {
- // If there is a lead comment, can't be one line
- if val.LeadComment != nil {
- return false
- }
-
- // If there is assignment, we always break by line
- if val.Assign.IsValid() {
- return false
- }
-
- // If it isn't an object type, then its not a single line object
- ot, ok := val.Val.(*ast.ObjectType)
- if !ok {
- return false
- }
-
- // If the object has no items, it is single line!
- return len(ot.List.Items) == 0
-}
-
-func lines(txt string) int {
- endline := 1
- for i := 0; i < len(txt); i++ {
- if txt[i] == '\n' {
- endline++
- }
- }
- return endline
-}
-
-// ----------------------------------------------------------------------------
-// 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(")")
-}