Skip to content

Commit

Permalink
Fix missing hover for count and for_each expression (#166)
Browse files Browse the repository at this point in the history
* Add test for missing hover on for_each expression

* Add test for missing hover on count expression

* Enable reference collection for count and for_each
  • Loading branch information
dbanck committed Nov 30, 2022
1 parent e6f0252 commit 2b1ae40
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 9 deletions.
199 changes: 196 additions & 3 deletions decoder/hover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ func TestDecoder_HoverAtPos_typeDeclaration(t *testing.T) {
}
}

func TestDecoder_HoverAtPos_extension(t *testing.T) {
func TestDecoder_HoverAtPos_extensions_count(t *testing.T) {
testCases := []struct {
name string
bodySchema *schema.BodySchema
Expand Down Expand Up @@ -1106,7 +1106,7 @@ func TestDecoder_HoverAtPos_extension(t *testing.T) {
}
}

func TestDecoder_HoverAtPos_foreach_extension(t *testing.T) {
func TestDecoder_HoverAtPos_extension_for_each(t *testing.T) {
testCases := []struct {
name string
bodySchema *schema.BodySchema
Expand Down Expand Up @@ -1395,7 +1395,7 @@ func TestDecoder_HoverAtPos_foreach_extension(t *testing.T) {
}
}

func TestDecoder_HoverAtPos_dynamic_extension(t *testing.T) {
func TestDecoder_HoverAtPos_extensions_dynamic(t *testing.T) {
testCases := []struct {
name string
bodySchema *schema.BodySchema
Expand Down Expand Up @@ -1540,3 +1540,196 @@ func TestDecoder_HoverAtPos_dynamic_extension(t *testing.T) {
})
}
}

func TestDecoder_HoverAtPos_extensions_references(t *testing.T) {
testCases := []struct {
name string
bodySchema *schema.BodySchema
config string
pos hcl.Pos
expectedData *lang.HoverData
}{
{
"for_each var reference",
&schema.BodySchema{
Blocks: map[string]*schema.BlockSchema{
"myblock": {
Labels: []*schema.LabelSchema{
{Name: "type", IsDepKey: true},
{Name: "name"},
},
Body: &schema.BodySchema{
Extensions: &schema.BodyExtensions{
ForEach: true,
},
Attributes: map[string]*schema.AttributeSchema{
"foo": {
IsOptional: true,
Expr: schema.ExprConstraints{
schema.TraversalExpr{
OfType: cty.String,
},
},
},
},
},
},
"variable": {
Address: &schema.BlockAddrSchema{
Steps: []schema.AddrStep{
schema.StaticStep{Name: "var"},
schema.LabelStep{Index: 0},
},
ScopeId: lang.ScopeId("variable"),
AsReference: true,
AsTypeOf: &schema.BlockAsTypeOf{
AttributeExpr: "type",
AttributeValue: "default",
},
},
Labels: []*schema.LabelSchema{
{Name: "name"},
},
Body: &schema.BodySchema{
Attributes: map[string]*schema.AttributeSchema{
"type": {
Expr: schema.ExprConstraints{schema.TypeDeclarationExpr{}},
IsOptional: true,
},
},
},
},
},
},
`myblock "foo" "bar" {
foo = each.value
for_each = var.name
}
variable "name" {
value = { key = "value" }
}
`,
hcl.Pos{Line: 3, Column: 19, Byte: 59},
&lang.HoverData{
Content: lang.Markdown("`var.name`\n_dynamic_"),
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 3, Column: 14, Byte: 54},
End: hcl.Pos{Line: 3, Column: 22, Byte: 62},
},
},
},
{
"count var reference",
&schema.BodySchema{
Blocks: map[string]*schema.BlockSchema{
"myblock": {
Labels: []*schema.LabelSchema{
{Name: "type", IsDepKey: true},
{Name: "name"},
},
Body: &schema.BodySchema{
Extensions: &schema.BodyExtensions{
Count: true,
},
Attributes: map[string]*schema.AttributeSchema{
"foo": {
IsOptional: true,
Expr: schema.ExprConstraints{
schema.TraversalExpr{
OfType: cty.Number,
},
},
},
},
},
},
"variable": {
Address: &schema.BlockAddrSchema{
Steps: []schema.AddrStep{
schema.StaticStep{Name: "var"},
schema.LabelStep{Index: 0},
},
ScopeId: lang.ScopeId("variable"),
AsReference: true,
AsTypeOf: &schema.BlockAsTypeOf{
AttributeExpr: "type",
AttributeValue: "default",
},
},
Labels: []*schema.LabelSchema{
{Name: "name"},
},
Body: &schema.BodySchema{
Attributes: map[string]*schema.AttributeSchema{
"type": {
Expr: schema.ExprConstraints{schema.TypeDeclarationExpr{}},
IsOptional: true,
},
},
},
},
},
},
`myblock "foo" "bar" {
foo = count.index
count = var.name
}
variable "name" {
value = 4
}
`,
hcl.Pos{Line: 3, Column: 16, Byte: 57},
&lang.HoverData{
Content: lang.Markdown("`var.name`\n_dynamic_"),
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 3, Column: 11, Byte: 52},
End: hcl.Pos{Line: 3, Column: 19, Byte: 60},
},
},
},
}

for i, tc := range testCases {
t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) {
ctx := context.Background()

f, diags := hclsyntax.ParseConfig([]byte(tc.config), "test.tf", hcl.InitialPos)
if diags != nil {
t.Fatal(diags)
}

d := testPathDecoder(t, &PathContext{
Schema: tc.bodySchema,
Files: map[string]*hcl.File{
"test.tf": f,
},
})
targets, err := d.CollectReferenceTargets()
if err != nil {
t.Fatal(err)
}
origins, err := d.CollectReferenceOrigins()
if err != nil {
t.Fatal(err)
}
d = testPathDecoder(t, &PathContext{
Schema: tc.bodySchema,
Files: map[string]*hcl.File{
"test.tf": f,
},
ReferenceTargets: targets,
ReferenceOrigins: origins,
})

data, err := d.HoverAtPos(ctx, "test.tf", tc.pos)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(tc.expectedData, data, ctydebug.CmpOptions); diff != "" {
t.Fatalf("hover data mismatch: %s", diff)
}
})
}
}
20 changes: 14 additions & 6 deletions decoder/reference_origins.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,21 @@ func (d *PathDecoder) referenceOriginsInBody(body hcl.Body, bodySchema *schema.B
content := decodeBody(body, bodySchema)

for _, attr := range content.Attributes {
aSchema, ok := bodySchema.Attributes[attr.Name]
if !ok {
if bodySchema.AnyAttribute == nil {
// skip unknown attribute
continue
var aSchema *schema.AttributeSchema
if bodySchema.Extensions != nil && bodySchema.Extensions.Count && attr.Name == "count" {
aSchema = countAttributeSchema()
} else if bodySchema.Extensions != nil && bodySchema.Extensions.ForEach && attr.Name == "for_each" {
aSchema = forEachAttributeSchema()
} else {
var ok bool
aSchema, ok = bodySchema.Attributes[attr.Name]
if !ok {
if bodySchema.AnyAttribute == nil {
// skip unknown attribute
continue
}
aSchema = bodySchema.AnyAttribute
}
aSchema = bodySchema.AnyAttribute
}

if aSchema.OriginForTarget != nil {
Expand Down

0 comments on commit 2b1ae40

Please sign in to comment.