diff --git a/ast/ast.go b/ast/ast.go index 3e5bc6c..04ef380 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -97,6 +97,9 @@ func (o *ObjectKey) Pos() token.Pos { // token.NUMBER, token.FLOAT, token.BOOL and token.STRING type LiteralType struct { Token token.Token + + // associated line comment, only when used in a list + LineComment *CommentGroup } func (l *LiteralType) Pos() token.Pos { diff --git a/parser/parser.go b/parser/parser.go index 648f871..7381bd8 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -15,7 +15,8 @@ type Parser struct { sc *scanner.Scanner // Last read token - tok token.Token + tok token.Token + commaPrev token.Token comments []*ast.CommentGroup leadComment *ast.CommentGroup // last lead comment @@ -147,7 +148,6 @@ func (p *Parser) objectItem() (*ast.ObjectItem, error) { o.LineComment = p.lineComment p.lineComment = nil } - p.unscan() return o, nil } @@ -253,6 +253,17 @@ func (p *Parser) listType() (*ast.ListType, error) { l.Add(node) case token.COMMA: // get next list item or we are at the end + // do a look-ahead for line comment + p.scan() + if p.lineComment != nil { + lit, ok := l.List[len(l.List)-1].(*ast.LiteralType) + if ok { + lit.LineComment = p.lineComment + l.List[len(l.List)-1] = lit + p.lineComment = nil + } + } + p.unscan() continue case token.BOOL: // TODO(arslan) should we support? not supported by HCL yet @@ -299,7 +310,8 @@ func (p *Parser) scan() token.Token { var comment *ast.CommentGroup var endline int - // fmt.Printf("p.tok.Pos.Line = %+v prev: %d \n", p.tok.Pos.Line, prev.Pos.Line) + // fmt.Printf("p.tok.Pos.Line = %+v prev: %d endline %d \n", + // p.tok.Pos.Line, prev.Pos.Line, endline) if p.tok.Pos.Line == prev.Pos.Line { // The comment is on same line as the previous token; it // cannot be a lead comment but may be a line comment. diff --git a/printer/nodes.go b/printer/nodes.go index 8ca8050..2b8d6ae 100644 --- a/printer/nodes.go +++ b/printer/nodes.go @@ -57,6 +57,14 @@ func (p *printer) collectComments(node ast.Node) { // assigned to any kind of node. ast.Walk(node, func(nn ast.Node) bool { switch t := nn.(type) { + case *ast.LiteralType: + 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 { @@ -83,6 +91,7 @@ func (p *printer) collectComments(node ast.Node) { } sort.Sort(ByPosition(p.standaloneComments)) + } // output prints creates b printable HCL output and returns it. @@ -98,8 +107,6 @@ func (p *printer) output(n interface{}) []byte { var commented bool for { // TODO(arslan): refactor below comment printing, we have the same in objectType - - // print upper leve stand alone comments for _, c := range p.standaloneComments { for _, comment := range c.List { if index != len(t.Items) { @@ -115,7 +122,6 @@ func (p *printer) output(n interface{}) []byte { } buf.WriteString(comment.Text) - // TODO(arslan): do not print new lines if the comments are one liner buf.WriteByte(newline) if index != len(t.Items) { @@ -153,6 +159,9 @@ func (p *printer) output(n interface{}) []byte { return buf.Bytes() } +// 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 @@ -187,6 +196,8 @@ func (p *printer) objectItem(o *ast.ObjectItem) []byte { 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 @@ -364,32 +375,53 @@ func (p *printer) alignedItems(items []*ast.ObjectItem) []byte { return buf.Bytes() } -func (p *printer) literal(l *ast.LiteralType) []byte { - return []byte(l.Token.Text) -} - -// printList prints a HCL list +// list returns the printable HCL form of an list type. func (p *printer) list(l *ast.ListType) []byte { var buf bytes.Buffer buf.WriteString("[") + 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 + } + } + } + for i, item := range l.List { if item.Pos().Line != l.Lbrack.Line { // multiline list, add newline before we add each item buf.WriteByte(newline) // also indent each line - buf.Write(p.indent(p.output(item))) + val := p.output(item) + curLen := len(val) + buf.Write(p.indent(val)) + buf.WriteString(",") + + if lit, ok := item.(*ast.LiteralType); ok && lit.LineComment != nil { + for i := 0; i < longestLine-curLen+1; i++ { + buf.WriteByte(blank) + } + + for _, comment := range lit.LineComment.List { + buf.WriteString(comment.Text) + } + } + + if i == len(l.List)-1 { + buf.WriteByte(newline) + } } else { buf.Write(p.output(item)) + if i != len(l.List)-1 { + buf.WriteString(",") + buf.WriteByte(blank) + } } - if i != len(l.List)-1 { - buf.WriteString(",") - buf.WriteByte(blank) - } else if item.Pos().Line != l.Lbrack.Line { - buf.WriteString(",") - buf.WriteByte(newline) - } } buf.WriteString("]") diff --git a/printer/testdata/comment_aligned.golden b/printer/testdata/comment_aligned.golden index d4b12b0..16fc6ff 100644 --- a/printer/testdata/comment_aligned.golden +++ b/printer/testdata/comment_aligned.golden @@ -17,4 +17,9 @@ aligned = { default = { bar = "example" } + + security_groups = [ + "foo", # kenya 1 + "${aws_security_group.firewall.foo}", # kenya 2 + ] } \ No newline at end of file diff --git a/printer/testdata/comment_aligned.input b/printer/testdata/comment_aligned.input index 81ba17d..2233738 100644 --- a/printer/testdata/comment_aligned.input +++ b/printer/testdata/comment_aligned.input @@ -13,4 +13,9 @@ aligned { default = { bar = "example" } + +security_groups = [ + "foo", # kenya 1 + "${aws_security_group.firewall.foo}", # kenya 2 +] } diff --git a/printer/testdata/complexhcl.golden b/printer/testdata/complexhcl.golden index 1e9562e..3bf4e06 100644 --- a/printer/testdata/complexhcl.golden +++ b/printer/testdata/complexhcl.golden @@ -22,7 +22,7 @@ resource aws_instance "web" { ami = "${var.foo}" security_groups = [ - "foo", + "foo", "${aws_security_group.firewall.foo}", ] diff --git a/printer/testdata/list.golden b/printer/testdata/list.golden index 0b949f1..385912f 100644 --- a/printer/testdata/list.golden +++ b/printer/testdata/list.golden @@ -11,8 +11,8 @@ foo = ["fatih", "zeynep", ] foo = [ - "vim-go", - "golang", + "vim-go", + "golang", "hcl", ] @@ -21,7 +21,7 @@ foo = [] foo = [1, 2, 3, 4] foo = [ - "kenya", - "ethiopia", + "kenya", + "ethiopia", "columbia", ] \ No newline at end of file