Skip to content

Commit

Permalink
Use local ref. targets for each.* references
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed Nov 22, 2022
1 parent f48e63d commit d386af9
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 64 deletions.
118 changes: 112 additions & 6 deletions decoder/body_extensions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,16 +693,69 @@ variable "test" {
},
},
},
reference.Targets{},
reference.Targets{
{
LocalAddr: lang.Address{
lang.RootStep{Name: "each"},
},
Type: cty.Object(map[string]cty.Type{
"key": cty.String,
"value": cty.DynamicPseudoType,
}),
Description: lang.Markdown("Each element the given map or set"),
RangePtr: nil,
DefRangePtr: nil,
NestedTargets: reference.Targets{
{
LocalAddr: lang.Address{
lang.RootStep{Name: "each"},
lang.AttrStep{Name: "key"},
},
Type: cty.String,
Description: lang.Markdown("The map key (or set member) corresponding to this instance"),
RangePtr: nil,
DefRangePtr: nil,
},
{
LocalAddr: lang.Address{
lang.RootStep{Name: "each"},
lang.AttrStep{Name: "value"},
},
Type: cty.DynamicPseudoType,
Description: lang.Markdown("The map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)"),
RangePtr: nil,
DefRangePtr: nil,
},
},
},
},
`resource "aws_instance" "foo" {
for_each = {
a_group = "eastus"
another_group = "westus2"
}
thing =
}`,
hcl.Pos{Line: 6, Column: 8, Byte: 101},
hcl.Pos{Line: 6, Column: 9, Byte: 102},
lang.CompleteCandidates([]lang.Candidate{
{
Label: "each",
Detail: "object",
Kind: lang.TraversalCandidateKind,
Description: lang.MarkupContent{
Value: "Each element the given map or set",
Kind: lang.MarkdownKind,
},
TextEdit: lang.TextEdit{
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 6, Column: 9, Byte: 102},
End: hcl.Pos{Line: 6, Column: 9, Byte: 102},
},
NewText: "each",
Snippet: "each",
},
},
{
Label: "each.key",
Detail: "string",
Expand All @@ -723,7 +776,7 @@ thing =
},
{
Label: "each.value",
Detail: "any type",
Detail: "dynamic",
Kind: lang.TraversalCandidateKind,
Description: lang.MarkupContent{
Value: "The map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)",
Expand Down Expand Up @@ -880,7 +933,7 @@ for_each = {
IsOptional: true,
Expr: schema.ExprConstraints{
schema.TraversalExpr{
OfType: cty.Number,
OfType: cty.DynamicPseudoType,
},
},
},
Expand All @@ -892,7 +945,42 @@ for_each = {
},
},
},
reference.Targets{},
reference.Targets{
{
LocalAddr: lang.Address{
lang.RootStep{Name: "each"},
},
Type: cty.Object(map[string]cty.Type{
"key": cty.String,
"value": cty.DynamicPseudoType,
}),
Description: lang.Markdown("Each element the given map or set"),
RangePtr: nil,
DefRangePtr: nil,
NestedTargets: reference.Targets{
{
LocalAddr: lang.Address{
lang.RootStep{Name: "each"},
lang.AttrStep{Name: "key"},
},
Type: cty.String,
Description: lang.Markdown("The map key (or set member) corresponding to this instance"),
RangePtr: nil,
DefRangePtr: nil,
},
{
LocalAddr: lang.Address{
lang.RootStep{Name: "each"},
lang.AttrStep{Name: "value"},
},
Type: cty.DynamicPseudoType,
Description: lang.Markdown("The map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)"),
RangePtr: nil,
DefRangePtr: nil,
},
},
},
},
`resource "aws_instance" "foo" {
for_each = {
a_group = "eastus"
Expand All @@ -904,6 +992,24 @@ foo {
}`,
hcl.Pos{Line: 7, Column: 11, Byte: 109},
lang.CompleteCandidates([]lang.Candidate{
{
Label: "each",
Detail: "object",
Kind: lang.TraversalCandidateKind,
Description: lang.MarkupContent{
Value: "Each element the given map or set",
Kind: lang.MarkdownKind,
},
TextEdit: lang.TextEdit{
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 7, Column: 10, Byte: 109},
End: hcl.Pos{Line: 7, Column: 10, Byte: 109},
},
NewText: "each",
Snippet: "each",
},
},
{
Label: "each.key",
Detail: "string",
Expand All @@ -924,7 +1030,7 @@ foo {
},
{
Label: "each.value",
Detail: "any type",
Detail: "dynamic",
Kind: lang.TraversalCandidateKind,
Description: lang.MarkupContent{
Value: "The map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)",
Expand Down
56 changes: 18 additions & 38 deletions decoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,49 +264,29 @@ func countIndexReferenceTarget(attr *hcl.Attribute, bodyRange hcl.Range) referen
}
}

func foreachEachCandidate(editRng hcl.Range) []lang.Candidate {
return []lang.Candidate{
func forEachReferenceTargets(attr *hcl.Attribute, bodyRange hcl.Range) reference.Targets {
return reference.Targets{
{
Label: "each.key",
Detail: "string",
Description: lang.MarkupContent{
Value: "The map key (or set member) corresponding to this instance",
Kind: lang.MarkdownKind,
},
Kind: lang.TraversalCandidateKind,
TextEdit: lang.TextEdit{
NewText: "each.key",
Snippet: "each.key",
Range: editRng,
LocalAddr: lang.Address{
lang.RootStep{Name: "each"},
lang.AttrStep{Name: "key"},
},
TargetableFromRangePtr: bodyRange.Ptr(),
Type: cty.String,
Description: lang.Markdown("The map key (or set member) corresponding to this instance"),
RangePtr: attr.Range.Ptr(),
DefRangePtr: attr.NameRange.Ptr(),
},
{
Label: "each.value",
Detail: "any type",
Description: lang.MarkupContent{
Value: "The map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)",
Kind: lang.MarkdownKind,
},
Kind: lang.TraversalCandidateKind,
TextEdit: lang.TextEdit{
NewText: "each.value",
Snippet: "each.value",
Range: editRng,
LocalAddr: lang.Address{
lang.RootStep{Name: "each"},
lang.AttrStep{Name: "value"},
},
TargetableFromRangePtr: bodyRange.Ptr(),
Type: cty.DynamicPseudoType,
Description: lang.Markdown("The map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)"),
RangePtr: attr.Range.Ptr(),
DefRangePtr: attr.NameRange.Ptr(),
},
}
}

func eachKeyHoverData(rng hcl.Range) *lang.HoverData {
return &lang.HoverData{
Content: lang.Markdown("`each.key` _string_\n\nThe map key (or set member) corresponding to this instance"),
Range: rng,
}
}

func eachValueHoverData(rng hcl.Range) *lang.HoverData {
return &lang.HoverData{
Content: lang.Markdown("`each.value` _any type_\n\nThe map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)"),
Range: rng,
}
}
4 changes: 0 additions & 4 deletions decoder/expression_candidates.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,6 @@ func (d *PathDecoder) constraintToCandidates(ctx context.Context, constraint sch
},
})
case schema.TraversalExpr:
if schema.ActiveForEachFromContext(ctx) && attr.Name != "for_each" {
candidates = append(candidates, foreachEachCandidate(editRng)...)
}

candidates = append(candidates, d.candidatesForTraversalConstraint(ctx, c, outerBodyRng, prefixRng, editRng)...)
case schema.TupleConsExpr:
candidates = append(candidates, lang.Candidate{
Expand Down
14 changes: 0 additions & 14 deletions decoder/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,20 +264,6 @@ func (d *PathDecoder) hoverDataForExpr(ctx context.Context, expr hcl.Expression,
}, nil
}

address, err := lang.TraversalToAddress(e.AsTraversal())
if err != nil {
return nil, err
}

eachKeyAddr := lang.Address{lang.RootStep{Name: "each"}, lang.AttrStep{Name: "key"}}
eachValueAddr := lang.Address{lang.RootStep{Name: "each"}, lang.AttrStep{Name: "value"}}

if address.Equals(eachKeyAddr) && schema.ActiveForEachFromContext(ctx) {
return eachKeyHoverData(expr.Range()), nil
} else if address.Equals(eachValueAddr) && schema.ActiveForEachFromContext(ctx) {
return eachValueHoverData(expr.Range()), nil
}

tes, ok := constraints.TraversalExprs()
if ok {
content, err := d.hoverContentForTraversalExpr(ctx, e.AsTraversal(), tes, pos)
Expand Down
4 changes: 2 additions & 2 deletions decoder/hover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,7 @@ func TestDecoder_HoverAtPos_foreach_extension(t *testing.T) {
`,
hcl.Pos{Line: 2, Column: 8, Byte: 30},
&lang.HoverData{
Content: lang.Markdown("`each.key` _string_\n\nThe map key (or set member) corresponding to this instance"),
Content: lang.Markdown("`each.key`\n_string_\n\nThe map key (or set member) corresponding to this instance"),
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 2, Column: 8, Byte: 29},
Expand Down Expand Up @@ -1356,7 +1356,7 @@ func TestDecoder_HoverAtPos_foreach_extension(t *testing.T) {
`,
hcl.Pos{Line: 2, Column: 8, Byte: 30},
&lang.HoverData{
Content: lang.Markdown("`each.value` _any type_\n\nThe map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)"),
Content: lang.Markdown("`each.value`\n_dynamic_\n\nThe map value corresponding to this instance. (If a set was provided, this is the same as `each.key`.)"),
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 2, Column: 8, Byte: 29},
Expand Down
4 changes: 4 additions & 0 deletions decoder/reference_targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock *
refs = append(refs, countIndexReferenceTarget(attr, *content.RangePtr))
continue
}
if bodySchema.Extensions.ForEach && attr.Name == "for_each" && content.RangePtr != nil {
refs = append(refs, forEachReferenceTargets(attr, *content.RangePtr)...)
continue
}
}
attrSchema, ok := bodySchema.Attributes[attr.Name]
if !ok {
Expand Down

0 comments on commit d386af9

Please sign in to comment.