Skip to content

Commit

Permalink
Be more permissive in allowed characters in a block label (#94)
Browse files Browse the repository at this point in the history
* Be more permissive in allowed characters in a block label

Previously a block label would only be allowed to contain the set of
characters in an Indentifier, however a block label string literal can
contain pretty much any character. This commit updates the base HCL grammar
to support both indentifiers and string literals in block labels and adds tests
to ensure this behaviour works as expected.

* Add additional tests for edgecases in block labels
  • Loading branch information
glennsarti authored Nov 17, 2023
1 parent ad29c91 commit ac3ca8c
Show file tree
Hide file tree
Showing 6 changed files with 449 additions and 9 deletions.
12 changes: 8 additions & 4 deletions src/_main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ repository:
block:
name: meta.block.hcl
comment: This will match HCL blocks like `thing1 "one" "two" {` or `thing2 {`
begin: ([\w][\-\w]*)([\s\"\-\w]*)(\{)
begin: ([\w][\-\w]*)([^{\r\n]*)(\{)
beginCaptures:
"1":
patterns:
Expand All @@ -86,8 +86,11 @@ repository:
"2":
patterns:
- name: variable.other.enummember.hcl
comment: Block label
match: '[\"\-\w]+'
comment: Block label (String Literal)
match: '\"[^\"\r\n]*\"'
- name: variable.other.enummember.hcl
comment: Block label (Indentifier)
match: '[[:alpha:]][[:alnum:]_-]*'
"3":
name: punctuation.section.block.begin.hcl
end: \}
Expand All @@ -97,8 +100,9 @@ repository:
patterns:
- include: "#comments"
- include: "#attribute_definition"
- include: "#block"
- include: "#expressions"
# Order is important and blocks should be last
- include: "#block"
expressions:
patterns:
- include: "#literal_values"
Expand Down
15 changes: 10 additions & 5 deletions syntaxes/hcl.tmGrammar.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
},
"block": {
"name": "meta.block.hcl",
"begin": "([\\w][\\-\\w]*)([\\s\\\"\\-\\w]*)(\\{)",
"begin": "([\\w][\\-\\w]*)([^{\\r\\n]*)(\\{)",
"end": "\\}",
"comment": "This will match HCL blocks like `thing1 \"one\" \"two\" {` or `thing2 {`",
"beginCaptures": {
Expand All @@ -98,8 +98,13 @@
"2": {
"patterns": [
{
"match": "[\\\"\\-\\w]+",
"comment": "Block label",
"match": "\\\"[^\\\"\\r\\n]*\\\"",
"comment": "Block label (String Literal)",
"name": "variable.other.enummember.hcl"
},
{
"match": "[[:alpha:]][[:alnum:]_-]*",
"comment": "Block label (Indentifier)",
"name": "variable.other.enummember.hcl"
}
]
Expand All @@ -121,10 +126,10 @@
"include": "#attribute_definition"
},
{
"include": "#block"
"include": "#expressions"
},
{
"include": "#expressions"
"include": "#block"
}
]
},
Expand Down
47 changes: 47 additions & 0 deletions tests/snapshot/hcl/block_labels.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// generic blocks with multiple labels as strings
block0 {}
block0-newline {
}

block1 "foo" {}
block1-newline "foo" {
}

block2 "foo" "bar" {}
block2-newline "foo" "bar" {
}

block3 "foo" "bar" "baz" {}
block3-newline "foo" "bar" "baz" {
}

block4 "foo" "bar" "baz" "lorum" {}
block4-newline "foo" "bar" "baz" "lorum" {
}

block5 "foo" "bar" "baz" "lorum" "ipsum" {}
block5-newline "foo" "bar" "baz" "lorum" "ipsum" {
}

// generic blocks with indentifier labels
blocklabel1 foo bar lorum-ipsum {}
blocklabel1-newline foo bar lorum-ipsum {
}

blocklabelmixed "foo" bar "loru" m-ipsum {}
blocklabelmixed-newline "foo" bar "loru" m-ipsum {
}

// utf8 labels
blockutf8 "foož:/ᚠᚢ" {}
blockutf8-newline "foož:/ᚠᚢ" {
}

// edgecases
blockempty "" {}
blockempty-newline "" {
}

block-single-char-indentifier a {}
block-single-char-indentifier-newline a {
}
244 changes: 244 additions & 0 deletions tests/snapshot/hcl/block_labels.hcl.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
>// generic blocks with multiple labels as strings
#^^ source.hcl comment.line.double-slash.hcl punctuation.definition.comment.hcl
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.hcl comment.line.double-slash.hcl
>block0 {}
#^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>block0-newline {
#^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>block1 "foo" {}
#^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>block1-newline "foo" {
#^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>block2 "foo" "bar" {}
#^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>block2-newline "foo" "bar" {
#^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>block3 "foo" "bar" "baz" {}
#^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>block3-newline "foo" "bar" "baz" {
#^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>block4 "foo" "bar" "baz" "lorum" {}
#^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>block4-newline "foo" "bar" "baz" "lorum" {
#^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>block5 "foo" "bar" "baz" "lorum" "ipsum" {}
#^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>block5-newline "foo" "bar" "baz" "lorum" "ipsum" {
#^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>// generic blocks with indentifier labels
#^^ source.hcl comment.line.double-slash.hcl punctuation.definition.comment.hcl
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.hcl comment.line.double-slash.hcl
>blocklabel1 foo bar lorum-ipsum {}
#^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>blocklabel1-newline foo bar lorum-ipsum {
#^^^^^^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>blocklabelmixed "foo" bar "loru" m-ipsum {}
#^^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>blocklabelmixed-newline "foo" bar "loru" m-ipsum {
#^^^^^^^^^^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>// utf8 labels
#^^ source.hcl comment.line.double-slash.hcl punctuation.definition.comment.hcl
# ^^^^^^^^^^^^ source.hcl comment.line.double-slash.hcl
>blockutf8 "foož:/ᚠᚢ" {}
#^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>blockutf8-newline "foož:/ᚠᚢ" {
#^^^^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^^^^^^^^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>// edgecases
#^^ source.hcl comment.line.double-slash.hcl punctuation.definition.comment.hcl
# ^^^^^^^^^^ source.hcl comment.line.double-slash.hcl
>blockempty "" {}
#^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>blockempty-newline "" {
#^^^^^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
>block-single-char-indentifier a {}
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>block-single-char-indentifier-newline a {
#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.hcl meta.block.hcl entity.name.type.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl variable.other.enummember.hcl
# ^ source.hcl meta.block.hcl
# ^ source.hcl meta.block.hcl punctuation.section.block.begin.hcl
>}
#^ source.hcl meta.block.hcl punctuation.section.block.end.hcl
>
21 changes: 21 additions & 0 deletions tests/snapshot/hcl/block_nested_complex.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
resource "aws_instance" "web" {
// The trailing { can confuse the regex into thinking it's a block
type = list(object({
internal = number
external = number
protocol = string
}))

// The trailing { can confuse the regex into thinking it's a block
default = [{
internal = 8300
external = 8300
protocol = "tcp"
}
]

# This is a nested block and should highlight correctly
nested-resource "aws_instance" "foo" {
type = "nested"
}
}
Loading

0 comments on commit ac3ca8c

Please sign in to comment.