Skip to content

Commit

Permalink
hclwrite: add space between "in" keyword and expr in for expression
Browse files Browse the repository at this point in the history
Our normal ruleset thinks that the "in" keyword here is a variable
reference and so writes it as "in[y]". Since there's never any reason for
a variable to appear immediately after another variable, we can check
for a preceding identifier as a heuristic to recognize whether in is
probably being used as a keyword rather than as a variable.

This is not exact, but the only time this should be a false positive is
if there were a syntax error in the input, and we don't make any
guarantees about the result in that case anyway.

This fixes hashicorp#52.
  • Loading branch information
apparentlymart committed Dec 14, 2018
1 parent bafa0c5 commit 002296d
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 1 deletion.
17 changes: 16 additions & 1 deletion hclwrite/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"github.com/hashicorp/hcl2/hcl/hclsyntax"
)

var inKeyword = hclsyntax.Keyword([]byte{'i', 'n'})

// placeholder token used when we don't have a token but we don't want
// to pass a real "nil" and complicate things with nil pointer checks
var nilToken = &Token{
Expand Down Expand Up @@ -247,6 +249,15 @@ func spaceAfterToken(subject, before, after *Token) bool {
// No extra spaces within templates
return false

case inKeyword.TokenMatches(subject.asHCLSyntax()) && before.Type == hclsyntax.TokenIdent:
// This is a special case for inside for expressions where a user
// might want to use a literal tuple constructor:
// [for x in [foo]: x]
// ... in that case, we would normally produce in[foo] thinking that
// in is a reference, but we'll recognize it as a keyword here instead
// to make the result less confusing.
return true

case after.Type == hclsyntax.TokenOBrack && (subject.Type == hclsyntax.TokenIdent || subject.Type == hclsyntax.TokenNumberLit || tokenBracketChange(subject) < 0):
return false

Expand Down Expand Up @@ -283,7 +294,7 @@ func spaceAfterToken(subject, before, after *Token) bool {
return true
}

case subject.Type == hclsyntax.TokenOBrace || (after != nil && after.Type == hclsyntax.TokenCBrace):
case subject.Type == hclsyntax.TokenOBrace || after.Type == hclsyntax.TokenCBrace:
// Unlike other bracket types, braces have spaces on both sides of them,
// both in single-line nested blocks foo { bar = baz } and in object
// constructor expressions foo = { bar = baz }.
Expand All @@ -294,6 +305,10 @@ func spaceAfterToken(subject, before, after *Token) bool {
}
return true

case after.Type == hclsyntax.TokenColon:
// Never spaces before colons
return false

case tokenBracketChange(subject) > 0:
// No spaces after open brackets
return false
Expand Down
8 changes: 8 additions & 0 deletions hclwrite/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ foo(
`[ [ ] ]`,
`[[]]`,
},
{
`[for x in y: x]`,
`[for x in y: x]`,
},
{
`[for x in [y]: x]`,
`[for x in [y]: x]`,
},
{
`
[
Expand Down
18 changes: 18 additions & 0 deletions hclwrite/tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"io"

"github.com/apparentlymart/go-textseg/textseg"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
)

Expand All @@ -22,6 +23,23 @@ type Token struct {
SpacesBefore int
}

// asHCLSyntax returns the receiver expressed as an incomplete hclsyntax.Token.
// A complete token is not possible since we don't have source location
// information here, and so this method is unexported so we can be sure it will
// only be used for internal purposes where we know the range isn't important.
//
// This is primarily intended to allow us to re-use certain functionality from
// hclsyntax rather than re-implementing it against our own token type here.
func (t *Token) asHCLSyntax() hclsyntax.Token {
return hclsyntax.Token{
Type: t.Type,
Bytes: t.Bytes,
Range: hcl.Range{
Filename: "<invalid>",
},
}
}

// Tokens is a flat list of tokens.
type Tokens []*Token

Expand Down

0 comments on commit 002296d

Please sign in to comment.