Skip to content

Commit 9a3d8b3

Browse files
authored
fix: support of deeper nesting of dynamic block (#167)
* decoder: Add failing tests for deeper dynamic in completion+hover+semtok * decoder: recognise deeper nesting of dynamic block
1 parent 2b1ae40 commit 9a3d8b3

File tree

4 files changed

+305
-1
lines changed

4 files changed

+305
-1
lines changed

decoder/body_extensions_test.go

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2553,7 +2553,6 @@ resource "aws_elastic_beanstalk_environment" "example" {
25532553
}),
25542554
"",
25552555
},
2556-
// never complete dynamic as a dynamic label
25572556
{
25582557
"never complete dynamic as a dynamic label",
25592558
&schema.BodySchema{
@@ -2620,6 +2619,98 @@ resource "aws_elastic_beanstalk_environment" "example" {
26202619
}),
26212620
"",
26222621
},
2622+
{
2623+
"deeper nesting support",
2624+
&schema.BodySchema{
2625+
Blocks: map[string]*schema.BlockSchema{
2626+
"resource": {
2627+
Labels: []*schema.LabelSchema{
2628+
{
2629+
Name: "type",
2630+
IsDepKey: true,
2631+
Completable: true,
2632+
},
2633+
{Name: "name"},
2634+
},
2635+
Body: &schema.BodySchema{
2636+
Extensions: &schema.BodyExtensions{
2637+
DynamicBlocks: true,
2638+
},
2639+
Blocks: make(map[string]*schema.BlockSchema, 0),
2640+
},
2641+
DependentBody: map[schema.SchemaKey]*schema.BodySchema{
2642+
schema.NewSchemaKey(schema.DependencyKeys{
2643+
Labels: []schema.LabelDependent{
2644+
{Index: 0, Value: "aws_instance"},
2645+
},
2646+
}): {
2647+
Blocks: map[string]*schema.BlockSchema{
2648+
"foo": {
2649+
Body: &schema.BodySchema{
2650+
Blocks: map[string]*schema.BlockSchema{
2651+
"bar": {
2652+
Body: &schema.BodySchema{
2653+
Blocks: map[string]*schema.BlockSchema{
2654+
"baz": {
2655+
Body: schema.NewBodySchema(),
2656+
},
2657+
},
2658+
},
2659+
},
2660+
},
2661+
},
2662+
},
2663+
},
2664+
},
2665+
},
2666+
},
2667+
},
2668+
},
2669+
`resource "aws_instance" "example" {
2670+
foo {
2671+
bar {
2672+
2673+
}
2674+
}
2675+
}`,
2676+
hcl.Pos{Line: 4, Column: 7, Byte: 60},
2677+
lang.CompleteCandidates([]lang.Candidate{
2678+
{
2679+
Label: "baz",
2680+
Detail: "Block",
2681+
Kind: lang.BlockCandidateKind,
2682+
TextEdit: lang.TextEdit{
2683+
Range: hcl.Range{
2684+
Filename: "test.tf",
2685+
Start: hcl.Pos{Line: 4, Column: 7, Byte: 60},
2686+
End: hcl.Pos{Line: 4, Column: 7, Byte: 60},
2687+
},
2688+
NewText: "baz",
2689+
Snippet: "baz {\n ${1}\n}",
2690+
},
2691+
},
2692+
{
2693+
Label: "dynamic",
2694+
Description: lang.MarkupContent{
2695+
Value: "A dynamic block to produce blocks dynamically by iterating over a given complex value",
2696+
Kind: lang.MarkdownKind,
2697+
},
2698+
Detail: "Block, map",
2699+
Kind: lang.BlockCandidateKind,
2700+
TriggerSuggest: true,
2701+
TextEdit: lang.TextEdit{
2702+
Range: hcl.Range{
2703+
Filename: "test.tf",
2704+
Start: hcl.Pos{Line: 4, Column: 7, Byte: 60},
2705+
End: hcl.Pos{Line: 4, Column: 7, Byte: 60},
2706+
},
2707+
NewText: "dynamic",
2708+
Snippet: "dynamic \"${1}\" {\n ${2}\n}",
2709+
},
2710+
},
2711+
}),
2712+
"",
2713+
},
26232714
}
26242715

26252716
for i, tc := range testCases {

decoder/decoder.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,14 @@ func mergeBlockBodySchemas(block *hcl.Block, blockSchema *schema.BlockSchema) (*
6262
}
6363
for bType, block := range depSchema.Blocks {
6464
if _, exists := mergedSchema.Blocks[bType]; !exists {
65+
// propagate DynamicBlocks extension to any nested blocks
6566
if mergedSchema.Extensions != nil && mergedSchema.Extensions.DynamicBlocks {
6667
if block.Body.Extensions == nil {
6768
block.Body.Extensions = &schema.BodyExtensions{}
6869
}
6970
block.Body.Extensions.DynamicBlocks = true
7071
}
72+
7173
mergedSchema.Blocks[bType] = block
7274
} else {
7375
// Skip duplicate block type
@@ -94,6 +96,21 @@ func mergeBlockBodySchemas(block *hcl.Block, blockSchema *schema.BlockSchema) (*
9496
mergedSchema.Extensions = depSchema.Extensions.Copy()
9597
}
9698
} else if !ok && mergedSchema.Extensions != nil && mergedSchema.Extensions.DynamicBlocks && len(mergedSchema.Blocks) > 0 {
99+
// dynamic blocks are only relevant for dependent schemas,
100+
// but we may end up here because the schema is a result
101+
// of merged static + dependent schema from previous iteration
102+
103+
// propagate DynamicBlocks extension to any nested blocks
104+
if mergedSchema.Extensions != nil && mergedSchema.Extensions.DynamicBlocks {
105+
for bType, block := range mergedSchema.Blocks {
106+
if block.Body.Extensions == nil {
107+
block.Body.Extensions = &schema.BodyExtensions{}
108+
}
109+
block.Body.Extensions.DynamicBlocks = true
110+
mergedSchema.Blocks[bType] = block
111+
}
112+
}
113+
97114
mergedSchema.Blocks["dynamic"] = buildDynamicBlockSchema(mergedSchema)
98115
}
99116

decoder/hover_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,77 @@ func TestDecoder_HoverAtPos_extensions_dynamic(t *testing.T) {
15121512
},
15131513
},
15141514
},
1515+
1516+
{
1517+
"deeper nesting support",
1518+
&schema.BodySchema{
1519+
Blocks: map[string]*schema.BlockSchema{
1520+
"resource": {
1521+
Labels: []*schema.LabelSchema{
1522+
{
1523+
Name: "type",
1524+
IsDepKey: true,
1525+
Completable: true,
1526+
},
1527+
{Name: "name"},
1528+
},
1529+
Body: &schema.BodySchema{
1530+
Extensions: &schema.BodyExtensions{
1531+
DynamicBlocks: true,
1532+
},
1533+
Blocks: make(map[string]*schema.BlockSchema, 0),
1534+
},
1535+
DependentBody: map[schema.SchemaKey]*schema.BodySchema{
1536+
schema.NewSchemaKey(schema.DependencyKeys{
1537+
Labels: []schema.LabelDependent{
1538+
{Index: 0, Value: "aws_instance"},
1539+
},
1540+
}): {
1541+
Blocks: map[string]*schema.BlockSchema{
1542+
"foo": {
1543+
Body: &schema.BodySchema{
1544+
Blocks: map[string]*schema.BlockSchema{
1545+
"bar": {
1546+
Body: &schema.BodySchema{
1547+
Blocks: map[string]*schema.BlockSchema{
1548+
"baz": {
1549+
Body: schema.NewBodySchema(),
1550+
},
1551+
},
1552+
},
1553+
},
1554+
},
1555+
},
1556+
},
1557+
},
1558+
},
1559+
},
1560+
},
1561+
},
1562+
},
1563+
`resource "aws_instance" "example" {
1564+
foo {
1565+
bar {
1566+
dynamic "baz" {
1567+
1568+
}
1569+
}
1570+
}
1571+
}`,
1572+
hcl.Pos{Line: 4, Column: 10, Byte: 63},
1573+
&lang.HoverData{
1574+
Content: lang.MarkupContent{
1575+
Value: "**dynamic** _Block, map_\n\n" +
1576+
"A dynamic block to produce blocks dynamically by iterating over a given complex value",
1577+
Kind: lang.MarkdownKind,
1578+
},
1579+
Range: hcl.Range{
1580+
Filename: "test.tf",
1581+
Start: hcl.Pos{Line: 4, Column: 7, Byte: 60},
1582+
End: hcl.Pos{Line: 4, Column: 14, Byte: 67},
1583+
},
1584+
},
1585+
},
15151586
}
15161587

15171588
for i, tc := range testCases {

decoder/semantic_tokens_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2156,6 +2156,131 @@ func TestDecoder_SemanticTokensInFile_extensions_dynamic(t *testing.T) {
21562156
},
21572157
},
21582158
},
2159+
{
2160+
"deeper nested dynamic blocks",
2161+
&schema.BodySchema{
2162+
Blocks: map[string]*schema.BlockSchema{
2163+
"myblock": {
2164+
Labels: []*schema.LabelSchema{
2165+
{
2166+
Name: "type",
2167+
IsDepKey: true,
2168+
Completable: true,
2169+
SemanticTokenModifiers: lang.SemanticTokenModifiers{lang.TokenModifierDependent},
2170+
},
2171+
{Name: "name"},
2172+
},
2173+
Body: &schema.BodySchema{
2174+
Extensions: &schema.BodyExtensions{
2175+
DynamicBlocks: true,
2176+
},
2177+
Blocks: make(map[string]*schema.BlockSchema, 0),
2178+
},
2179+
DependentBody: map[schema.SchemaKey]*schema.BodySchema{
2180+
schema.NewSchemaKey(schema.DependencyKeys{
2181+
Labels: []schema.LabelDependent{
2182+
{Index: 0, Value: "foo"},
2183+
},
2184+
}): {
2185+
Blocks: map[string]*schema.BlockSchema{
2186+
"setting": {
2187+
Body: &schema.BodySchema{
2188+
Blocks: map[string]*schema.BlockSchema{
2189+
"foo": {
2190+
Body: &schema.BodySchema{
2191+
Blocks: map[string]*schema.BlockSchema{
2192+
"bar": {
2193+
Body: schema.NewBodySchema(),
2194+
},
2195+
},
2196+
},
2197+
},
2198+
},
2199+
},
2200+
},
2201+
},
2202+
},
2203+
},
2204+
},
2205+
},
2206+
},
2207+
`myblock "foo" "bar" {
2208+
setting {
2209+
foo {
2210+
dynamic "bar" {
2211+
2212+
}
2213+
}
2214+
}
2215+
}`,
2216+
[]lang.SemanticToken{
2217+
{ // myblock
2218+
Type: lang.TokenBlockType,
2219+
Modifiers: []lang.SemanticTokenModifier{},
2220+
Range: hcl.Range{
2221+
Filename: "test.tf",
2222+
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
2223+
End: hcl.Pos{Line: 1, Column: 8, Byte: 7},
2224+
},
2225+
},
2226+
{ // "foo"
2227+
Type: lang.TokenBlockLabel,
2228+
Modifiers: []lang.SemanticTokenModifier{
2229+
lang.TokenModifierDependent,
2230+
},
2231+
Range: hcl.Range{
2232+
Filename: "test.tf",
2233+
Start: hcl.Pos{Line: 1, Column: 9, Byte: 8},
2234+
End: hcl.Pos{Line: 1, Column: 14, Byte: 13},
2235+
},
2236+
},
2237+
{ // "bar"
2238+
Type: lang.TokenBlockLabel,
2239+
Modifiers: []lang.SemanticTokenModifier{},
2240+
Range: hcl.Range{
2241+
Filename: "test.tf",
2242+
Start: hcl.Pos{Line: 1, Column: 15, Byte: 14},
2243+
End: hcl.Pos{Line: 1, Column: 20, Byte: 19},
2244+
},
2245+
},
2246+
{ // setting
2247+
Type: lang.TokenBlockType,
2248+
Modifiers: lang.SemanticTokenModifiers{},
2249+
Range: hcl.Range{
2250+
Filename: "test.tf",
2251+
Start: hcl.Pos{Line: 2, Column: 3, Byte: 24},
2252+
End: hcl.Pos{Line: 2, Column: 10, Byte: 31},
2253+
},
2254+
},
2255+
{ // foo
2256+
Type: lang.TokenBlockType,
2257+
Modifiers: lang.SemanticTokenModifiers{},
2258+
Range: hcl.Range{
2259+
Filename: "test.tf",
2260+
Start: hcl.Pos{Line: 3, Column: 5, Byte: 38},
2261+
End: hcl.Pos{Line: 3, Column: 8, Byte: 41},
2262+
},
2263+
},
2264+
{ // dynamic
2265+
Type: lang.TokenBlockType,
2266+
Modifiers: lang.SemanticTokenModifiers{},
2267+
Range: hcl.Range{
2268+
Filename: "test.tf",
2269+
Start: hcl.Pos{Line: 4, Column: 7, Byte: 50},
2270+
End: hcl.Pos{Line: 4, Column: 14, Byte: 57},
2271+
},
2272+
},
2273+
{ // "bar"
2274+
Type: lang.TokenBlockLabel,
2275+
Modifiers: lang.SemanticTokenModifiers{},
2276+
Range: hcl.Range{
2277+
Filename: "test.tf",
2278+
Start: hcl.Pos{Line: 4, Column: 15, Byte: 58},
2279+
End: hcl.Pos{Line: 4, Column: 20, Byte: 63},
2280+
},
2281+
},
2282+
},
2283+
},
21592284
}
21602285

21612286
for i, tc := range testCases {

0 commit comments

Comments
 (0)