Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

printer: Simplify the formatting of single-line lists. #243

Merged
merged 2 commits into from
Apr 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 91 additions & 89 deletions hcl/printer/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,8 +517,13 @@ func (p *printer) alignedItems(items []*ast.ObjectItem) []byte {

// 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 {
Expand All @@ -531,115 +536,112 @@ func (p *printer) list(l *ast.ListType) []byte {
}
}

insertSpaceBeforeItem := false
lastHadLeadComment := false
haveEmptyLine := false
for i, item := range l.List {
// Keep track of whether this item is a heredoc since that has
// unique behavior.
heredoc := false
if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC {
heredoc = true
}

if item.Pos().Line != l.Lbrack.Line {
// multiline list, add newline before we add each item
buf.WriteByte(newline)
insertSpaceBeforeItem = false

// 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

// If this isn't the first item and the previous element
// didn't have a lead comment, then we need to add an extra
// newline to properly space things out. If it did have a
// lead comment previously then this would be done
// automatically.
if i > 0 && !lastHadLeadComment {
buf.WriteByte(newline)
}

for _, comment := range lit.LeadComment.List {
buf.Write(p.indent([]byte(comment.Text)))
buf.WriteByte(newline)
}
// 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)
}

// 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 heredoc {
for _, comment := range lit.LeadComment.List {
buf.Write(p.indent([]byte(comment.Text)))
buf.WriteByte(newline)
comma = p.indent(comma)
}
}

buf.Write(comma)
// also indent each line
val := p.output(item)
curLen := len(val)
buf.Write(p.indent(val))

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)
}
// 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)
}

for _, comment := range lit.LineComment.List {
buf.WriteString(comment.Text)
}
}
buf.Write(comma)

lastItem := i == len(l.List)-1
if lastItem {
buf.WriteByte(newline)
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)
}

if leadComment && !lastItem {
buf.WriteByte(newline)
for _, comment := range lit.LineComment.List {
buf.WriteString(comment.Text)
}
}

lastHadLeadComment = leadComment
} else {
if insertSpaceBeforeItem {
buf.WriteByte(blank)
insertSpaceBeforeItem = false
}
buf.WriteByte(newline)

// Output the item itself
// also indent each line
val := p.output(item)
curLen := len(val)
buf.Write(val)
// 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)
}
}

// If this is a heredoc item we always have to output a newline
// so that it parses properly.
if heredoc {
buf.WriteByte(newline)
}
buf.WriteString("]")
return buf.Bytes()
}

// If this isn't the last element, write a comma.
if i != len(l.List)-1 {
buf.WriteString(",")
insertSpaceBeforeItem = true
}
// 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
}

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)
}
lit, ok := item.(*ast.LiteralType)
if !ok {
return false
}

for _, comment := range lit.LineComment.List {
buf.WriteString(comment.Text)
}
}
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("]")
Expand Down
1 change: 1 addition & 0 deletions hcl/printer/printer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func TestFormatValidOutput(t *testing.T) {
"#\x00",
"#\ue123t",
"x=//\n0y=<<_\n_\n",
"y=[1,//\n]",
"Y=<<4\n4/\n\n\n/4/@=4/\n\n\n/4000000004\r\r\n00004\n",
"x=<<_\n_\r\r\n_\n",
"X=<<-\n\r\r\n",
Expand Down
7 changes: 5 additions & 2 deletions hcl/printer/testdata/list.golden
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ foo = ["fatih", "arslan"]

foo = ["bar", "qaz"]

foo = ["zeynep",
foo = [
"zeynep",
"arslan",
]

foo = ["fatih", "zeynep",
foo = [
"fatih",
"zeynep",
"arslan",
]

Expand Down
10 changes: 8 additions & 2 deletions hcl/printer/testdata/list_comment.golden
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
foo = [1, # Hello
foo = [
1, # Hello
2,
]

foo = [1, # Hello
foo = [
1, # Hello
2, # World
]

foo = [
1, # Hello
]
3 changes: 3 additions & 0 deletions hcl/printer/testdata/list_comment.input
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ foo = [1, # Hello
foo = [1, # Hello
2, # World
]

foo = [1, # Hello
]