Commit Graph

60 Commits

Author SHA1 Message Date
Martin Atkins 2eaeb36cb3 Use Unicode 13 text segmentation rules
HCL uses a number of upstream libraries that implement algorithms defined
in Unicode. This commit is updating those libraries all to versions that
have Unicode 13 support.

The main implication of this for HCL directly is that when it returns
column numbers in source locations it will count characters using the
Unicode 13 definition of "character", which includes various new
multi-codeunit characters added in Unicode 13.

These new version dependencies will also make Unicode 13 support available
for other functionality that HCL callers might use, such as the stdlib
functions in upstream cty, even though HCL itself does not directly use
those.
2021-02-23 09:05:19 -08:00
Varun Sivapalan 61e260fbae
hclwrite: do not add space after a boolean NOT operator 2020-12-02 16:10:12 -08:00
Martin Atkins f1f3985230 hclsyntax: Explicit AST node for parentheses
So far the expression parentheses syntax has been handled entirely in the
parser and has been totally invisible in the AST. That's fine for typical
expression evaluation, but over the years it's led to a few quirky
behaviors in less common situations where we've assumed that all
expressions are covered by the AST itself or by the source ranges that the
AST captures.

In particular, hclwrite assumes that all expressions will have source
ranges that cover their tokens, and it generates an incorrect physical
syntax tree when the AST doesn't uphold that.

After resisting through a few other similar bugs, this commit finally
introduces an explicit AST node for parentheses, which makes the
parentheses explicit in the AST and captures the larger source range that
includes the TokenOParen and the TokenCParen.

This means that parentheses will now be visible as a distinct node when
walking the AST, as reflected in the updated tests here. That may cause
downstream applications that traverse the tree to exhibit different
behaviors but we're not considering that as a "breaking change" because
the Walk function doesn't make any guarantees about the specific AST
shape.
2020-12-02 12:03:00 -08:00
Alisdair McDiarmid 1818f36094 hclwrite: Allow blank quoted string block labels
The hclsyntax package permits block labels to be blank quoted strings,
and defers to the application to determine if this is valid or not. This
commit updates hclwrite to allow the same behaviour.

This is in response to an upstream bug report on Terraform, which uses
hclwrite to implement its fmt subcommand. Given a block with a blank
quoted string label, it currently deletes the label when formatting.
This is inconsistent with Terraform's behaviour when parsing HCL, which
treats empty labels differently from missing labels.
2020-11-18 09:21:23 -05:00
Martin Atkins 7d8f0ff870 hclwrite: Make block labels a node in their own right
All of the other subdivisions of a block were already nodes, but we'd
represented the labels as an undifferentiated set of nodes belonging
directly to the block's child node list.

Now that we support replacing the labels in the public API, that's a good
excuse to refactor this slightly to make the labels their own node. As
well as being consistent with everything else in Block, this also makes
it easier to implement the Block.SetLabels operation because we can
just  change the children of the labels node, rather than having to
carefully identify and extract the individual child nodes of the block
that happen to represent labels.

Internally this models the labels in a similar sort of way as the content
of a body, although we've kept the public API directly on the Block type
here because that's a more straightforward model for the use-cases we
currently know and matches better with the API of hcl.Block. This is just
an internal change for consistency.

I also added a few tests for having comments interspersed with labels
while I was here, because that helped to better exercise the new
parseBlockLabels function.
2020-08-21 11:30:32 -07:00
Masayuki Morita 143a545916 hclwrite: Fix a bug that Block.open/close positions were not recorded in parser
While implementing Block.SetLabels(), I found a new hclwrite parser bug.

The NewBlock() method records positions of TokenOBrace / TokenCBrace.
Nevertheless when generating blocks via hclwrite.ParseConfig(),
they were not recorded.

The position of TokenOBrace is needed for Block.SetLabels(),
so I also fixed this existing bug.
2020-08-21 11:30:32 -07:00
Masayuki Morita c3cbe9a9e2 hclwrite: Allow updating block type and labels
Fixes #338

Add methods to update block type and labels to enable us to refactor HCL
configurations such as renaming Terraform resources.

- `*Block.SetType(typeName string)`
- `*Block.SetLabels(labels []string)`

Some additional notes about SetLabels:

Since we cannot assume that old and new labels are equal in length,
remove old labels and insert new ones before TokenOBrace.

To implement this, I also added the following methods.

- `*nodes.Insert(pos *node, c nodeContent) *node`
- `*nodes.InsertNode(pos *node, n *node) *node`

They are similar to the existing Append / AppendNode,
but insert a node before a given position.
2020-08-21 11:30:32 -07:00
Radek Simko a20a69ce16 Merge pull request #317 from minamijoyo/fix-typos-in-hclwrite-parser
Fix typos in hclwrite/parser.go
2020-05-27 10:35:22 +01:00
Alisdair McDiarmid b5f1f971b4 hclwrite: Add fuzz testing 2020-05-14 15:03:29 -04:00
Alisdair McDiarmid d58c873a08 hclwrite: Fix panic for dotted full splat (foo.*)
The following expression caused a panic in hclwrite:

  a = foo.*

This was due to the unusual dotted form of a full splat (where the splat
operator is at the end of the expression) being generated with an
invalid source range. In the full splat case, the end of the range was
uninitialized, which caused the token slice to be empty, and thus the
panic.

This commit fixes the bug, adds test coverage, and includes some bonus
tests for other splat expression cases.
2020-05-13 16:23:21 -04:00
Alisdair McDiarmid fff0a094cc
Merge pull request #369 from bendrucker/hclwriter-numeric-dot
hclwrite: handle legacy dot access of numeric indexes
2020-05-13 16:05:28 -04:00
Alisdair McDiarmid 926e53e338 hclwrite: Generate multi-line objects and maps
The previous syntax for object and map values was a single line of
key-value pairs. For example:

  object = { bar = 5, baz = true, foo = "foo" }

This is very compact, but in practice for many HCL values, less readable
and less common than a multi-line object format. This commit changes the
generated output from hclwrite to one line per attribute.

Examples of the new format:

  // Empty object/map is a single line
  a = {}

  // Single-value object/map has the attribute on a separate line
  b = {
    bar = 5
  }

  // Multi-value object/map has one line per attribute
  c = {
    bar = 5
    baz = true
  }
2020-05-06 09:45:13 -04:00
Ben Drucker 8e720e092f add for 'foo[bar.baz]' 2020-04-28 21:08:43 -07:00
Ben Drucker 8e04c38ebf hclwrite: handle legacy dot access of numeric indexes 2020-04-28 20:38:45 -07:00
Martin Atkins fee90926da Use Unicode 12.0.0 grapheme cluster segmentation rules
HCL uses grapheme cluster segmentation to produce accurate "column"
indications in diagnostic messages and other human-oriented source
location information. Each new major version of Unicode introduces new
codepoints, some of which are defined to combine with other codepoints to
produce a single visible character (grapheme cluster).

We were previously using the rules from Unicode 9.0.0. This change
switches to using the segmentation rules from Unicode 12.0.0, which is
the latest version at the time of this commit and is also the version of
Unicode used for other purposes by the Go 1.14 runtime.

HCL does not use text segmentation results for any purpose that would
affect the meaning of decoded data extracted from HCL files, so this
change will only affect the human-oriented source positions generated for
files containing characters that were newly-introduced in Unicode 10, 11,
or 12. (Machine-oriented uses of source location information are based on
byte offsets and not affected by text segmentation.)
2020-03-09 09:16:33 -07:00
Martin Atkins 63e2897c12 hclsyntax: Source range of IndexExpr must cover whole expression
Some HCL callers make the (reasonable) assumption that the overall source
range of an expression will be a superset of all of the ranges of its
child expressions, for purposes such as extraction of source code
snippets, parse tree annotation in hclwrite, text editor analysis
functions like "go to reference", etc.

The IndexExpr type was not previously honoring that assumption, since its
source range was placed around only the bracket portion. That is a good
region to use when reporting errors relating to the index operation, but
it is not a faithful representation of the full extent of the expression.

In order to meet both of these requirements at once, IndexExpr now has
both SrcRange covering the entire expression and BracketRange covering
the index part delimited by brackets. We can then use BracketRange in
our error messages but return SrcRange as the result of the general
Range method that is common to all expression types.
2019-12-06 09:09:18 -08:00
Martin Atkins 06985781df hclwrite: Allow constructing expressions from raw tokens
We currently have functions for constructing new expressions from either
constant values or from traversals, but some surgical updates require
producing a more complex expression.

In the long run perhaps we'll have some mechanism for constructing valid
expressions via a high-level AST-like API, similar to what we already have
for structural constructs, but as a simpler first step here we add a
mechanism to just write raw tokens directly into an expression, with the
caller being responsible for making sure those tokens represent valid
HCL expression syntax.

Since this new API treats the given tokens as unstructured, the resulting
expression can't fully support the whole of the expression API, but it's
good enough for writing in complex expressions without disturbing existing
content elsewhere in the input file.
2019-11-19 15:35:01 -08:00
Colin Hoglund b954e171a6 hclwrite: fix TokensForTraversal handling of index steps 2019-11-11 15:17:19 -08:00
Masayuki Morita cc6a2232cd Fix typos in hclwrite/parser.go 2019-10-31 11:58:29 +09:00
Masayuki Morita c21e319577 hclwrite: Fix unstable list returned by Body.Blocks()
Fixes #310

The type of `Body.items` is a `nodeSet` (That is, `map[*node]struct{}`),
so the order of the list returned by Body.Blocks () was unstable.
2019-10-17 11:24:26 -07:00
Martin Atkins b0134908b0 hclwrite: Allow removing attributes and blocks from bodies
Previously we allowed adding both attributes and blocks, and we allowed
updating attributes, but we had no mechanism to surgically remove
attributes and blocks altogether.
2019-10-01 15:59:10 -07:00
Masayuki Morita 9d1235a5b4 hclwrite: Allow selecting blocks for updating 2019-10-01 15:59:10 -07:00
Martin Atkins 6c4344623b Unfold the "hcl" directory up into the root
The main HCL package is more visible this way, and so it's easier than
having to pick it out from dozens of other package directories.
2019-09-09 16:08:19 -07:00
Martin Atkins 3327dee567 Change module path to github.com/hashicorp/hcl/v2
This is in preparation for the first v2 release from the main HCL
repository.
2019-09-09 15:46:40 -07:00
Masayuki Morita 388af45637 hclwrite: Unquoted label should be parsed as *identifier 2019-09-09 13:16:13 -07:00
Kazuma Watanabe 72d32879a5 hclwrite: Do not add spaces before template control sequences 2019-08-09 14:00:04 -07:00
nozaq 0c3fe388e4 hclwrite: Fix heredocs never close during format
Waiting for TokenCHeredoc never ends since scanTokens() does not
 produce
 TokenNewlines inside heredocs.

 Related Issue: hashicorp/terraform#21434
2019-07-24 18:06:14 -07:00
nozaq 984d1e8201 hclwrite: Fix improper indent calculation inside heredocs (#107) 2019-07-22 15:57:18 -04:00
Martin Atkins 4b22149b7c hclwrite: No spaces before ... marker in argument lists or for exprs 2019-05-15 15:32:18 -07:00
Kristin Laemmert 956e03eb6d
hclwrite: heredoc tokens are in line.lead, not line.assign (#95) 2019-03-15 13:19:41 -07:00
Austin Burdine 7b147fbae4 hclwrite: fix space being added between interpolated items
closes #65
- add missing edge case for two interpolated items next to each other
- add tests for both quote and heredoc cases
2019-01-16 12:05:48 -08:00
Martin Atkins dac4796ca1 hclwrite: Formatter to put spaces before colons
The symmetrical spaces around colons in conditionals are important for
familiarity with C-like languages, so we'll instead accept spaces around
colons in our HCL2-unique "for expression" construct.
2018-12-14 15:53:02 -08:00
Martin Atkins 4c4fdbdcc0 hclwrite: Suspend indentation adjustment inside heredocs
Leading whitespace is significant in heredocs, so we'll avoid making any
indentation adjustments for lines between OHeredoc and CHeredoc.

This fixes #31.
2018-12-14 14:46:44 -08:00
Martin Atkins 002296d7bb hclwrite: add space between "in" keyword and expr in for expression
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 #52.
2018-12-14 14:19:04 -08:00
Martin Atkins bafa0c5ace hcl/hclsyntax: Accept single-line block definitions
This relaxes our previous spec to include a special form from HCL 1:

    foo { bar = baz }

Although we normally require each argument to be on a line of its own, as
a special case we allow a block to be defined with a single nested
argument all on one line.

Only one nested argument definition is allowed, and a nested block
definition like "foo { bar {} }" is also disallowed in order to force the
more-readable split of bar {} onto a line of its own.

This is a pragmatic addition for broader compatibility with HCL 1-oriented
input. This single-line usage is not considered idiomatic HCL 2 and may
in future be undone by the formatter, though for now it is left as-is
aside from the spacing around the braces.

This also changes the behavior of the source code formatter to include
spaces on both sides of braces. This mimicks the formatting behavior of
HCL 1 for this situation, and (subjectively) reads better even for other
one-line braced expressions like object constructors and object for
expressions.
2018-12-14 13:45:03 -08:00
Paul Tyng 9aa28a1a0b
Merge pull request #53 from hashicorp/pt/edges_from_hcl1
Add additional comment test cases
2018-11-06 18:06:04 -05:00
Martin Atkins 917fbe66e7 hclwrite: Expression.RenameVariablePrefix
This function serves the use-case of surgically renaming references to
variables in an existing file without disturbing other tokens.
2018-11-04 04:17:03 +00:00
Martin Atkins 0c14f1e3f6 hclwrite: Implement Body.SetAttributeTraversal 2018-11-04 02:09:35 +00:00
Martin Atkins 8cca983bca hclwrite: Support for creating new blocks and clearing bodies 2018-11-04 01:13:13 +00:00
Martin Atkins 361186aad0 hclwrite: Recast TestRoundupCreate as an example
Since the goal of this is to be somewhat realistic of what a caller might
do, we might as well also include it in the godoc examples.
2018-11-03 21:20:10 +00:00
Martin Atkins 17ff23300f hclwrite: NewEmptyFile function
This completes the minimal functionality for creating a new file from
scratch, rather than modifying an existing one. This is illustrated by
a new test TestRoundupCreate that uses the API to create a new file in a
similar way to how a calling application might.
2018-11-03 20:45:25 +00:00
Martin Atkins a44a287724 hclwrite: Make File and Tokens implement io.WriterTo
There isn't any strong reason for this -- they don't implement io.Reader
and so can't be used in places where a Reader+WriterTo is expected, like
io.Copy -- but go lint thinks that anything called WriteTo with an
io.Writer argument is an attempt to implement WriterTo and so this just
shuts up the linter.
2018-11-03 20:19:56 +00:00
Martin Atkins fd915f557d hclwrite: Rename Body.AppendBlock to AppendNewBlock
Since this function implicitly creates a new body, this name is more
appropriate and leaves the name "AppendBlock" open for a later method to
append an _existing_ block, such as when moving a block from one file
to another.
2018-11-03 19:54:56 +00:00
Martin Atkins 57c6d75eb8 hclwrite: Body.AppendBlock
This method allows a caller to generate a nested block within a body.
Since a nested block has its own content body, this now allows for deep
structures to be generated.
2018-11-03 09:21:43 -07:00
Martin Atkins 04d1413613 hclwrite: Split Attribute and Block into their own source files
These will grow to have significant logic of their own, so hiding them at
the end of the Body file makes things harder to read.
2018-11-03 09:21:43 -07:00
Paul Tyng e8e85f5edd
Add additional comment test cases
Some of these fail in HCL1 fmt
2018-10-31 12:25:12 -04:00
Martin Atkins d754d5a269 hclwrite: Adjust token spacing automatically when writing
Although our underlying parse tree retains all of the token content, it
doesn't necessarily retain all of the spacing information under editing,
and so formatting on save ensures that we'll produce a canonical result
even if some edits have been applied that have changed the expected
alignment of objects, etc.
2018-08-25 12:15:40 -07:00
Martin Atkins 59bb5c2670 hclwrite: Fix NewExpressionAbsTraversal signature
It is still not implemented, but at least now it is declared with the
correct return type.
2018-08-09 08:47:37 -07:00
Martin Atkins c8c208e083 hclwrite: Body.SetAttributeValue
For now, this is the only way to set an attribute, and so attributes can
only be set to literal values.

Later this will be generalized so that this is just a helper wrapper
around a "SetAttribute" method that just uses a given expression, which
then helps by constructing the expression from the value first.
2018-08-09 08:44:48 -07:00
Martin Atkins 77c0b55a59 hclwrite: Simplify internal data structures
The original prototype of hclwrite tried to track both the tokens and
the AST as two parallel data structures. This quickly exploded in
complexity, leading to lots of messy code to manage keeping those two
structures in sync.

This new approach melds the two structures together, creating first a
physical token tree (made of "node" objects, and hidden from the caller)
and then attaching the AST nodes to that token tree as additional sidecar
data.

The result is much easier to work with, leading to less code in the parser
and considerably less complex data structures in the parser's tests.

This commit is enough to reach feature parity with the previous prototype,
but it remains a prototype. With a more usable foundation, we'll evolve
this into a more complete implementation in subsequent commits.
2018-08-01 08:46:31 -07:00