From 5d2496e37c4a38be885b1e05287e86c99ea88775 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 16 Jun 2021 12:35:28 +0100 Subject: [PATCH] schema/decoder: Make label completion more flexible --- decoder/candidates.go | 6 ++++ decoder/candidates_test.go | 65 +++++++++++++++++++++++++++++++++----- schema/label_schema.go | 6 ++++ 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/decoder/candidates.go b/decoder/candidates.go index a124ecc7..1059c74f 100644 --- a/decoder/candidates.go +++ b/decoder/candidates.go @@ -111,6 +111,12 @@ func (d *Decoder) candidatesAtPos(body *hclsyntax.Body, outerBodyRng hcl.Range, } prefixRng.End = pos + labelSchema := bSchema.Labels[i] + + if !labelSchema.Completable { + return lang.ZeroCandidates(), nil + } + return d.labelCandidatesFromDependentSchema(i, bSchema.DependentBody, prefixRng, rng) } } diff --git a/decoder/candidates_test.go b/decoder/candidates_test.go index 22b6c47c..b15cfb9b 100644 --- a/decoder/candidates_test.go +++ b/decoder/candidates_test.go @@ -135,7 +135,7 @@ func TestDecoder_CandidatesAtPos_nilBodySchema(t *testing.T) { Blocks: map[string]*schema.BlockSchema{ "resource": { Labels: []*schema.LabelSchema{ - {Name: "type", IsDepKey: true}, + {Name: "type", IsDepKey: true, Completable: true}, {Name: "name"}, }, Body: nil, @@ -495,7 +495,7 @@ func TestDecoder_CandidatesAtPos_rightHandSideInString(t *testing.T) { func TestDecoder_CandidatesAtPos_endOfLabel(t *testing.T) { blockSchema := &schema.BlockSchema{ Labels: []*schema.LabelSchema{ - {Name: "type"}, + {Name: "type", Completable: true}, }, DependentBody: map[schema.SchemaKey]*schema.BodySchema{ schema.NewSchemaKey(schema.DependencyKeys{ @@ -595,9 +595,58 @@ func TestDecoder_CandidatesAtPos_endOfLabel(t *testing.T) { } } +func TestDecoder_CandidatesAtPos_nonCompletableLabel(t *testing.T) { + blockSchema := &schema.BlockSchema{ + Labels: []*schema.LabelSchema{ + {Name: "type", IsDepKey: true}, + }, + DependentBody: map[schema.SchemaKey]*schema.BodySchema{ + schema.NewSchemaKey(schema.DependencyKeys{ + Labels: []schema.LabelDependent{ + {Index: 0, Value: "myfirst"}, + }, + }): {}, + schema.NewSchemaKey(schema.DependencyKeys{ + Labels: []schema.LabelDependent{ + {Index: 0, Value: "mysecond"}, + }, + }): {}, + }, + } + bodySchema := &schema.BodySchema{ + Blocks: map[string]*schema.BlockSchema{ + "myblock": blockSchema, + }, + } + testConfig := []byte(`myblock "" { +} +`) + + d := NewDecoder() + d.SetSchema(bodySchema) + f, _ := hclsyntax.ParseConfig(testConfig, "test.tf", hcl.InitialPos) + err := d.LoadFile("test.tf", f) + if err != nil { + t.Fatal(err) + } + + candidates, err := d.CandidatesAtPos("test.tf", hcl.Pos{ + Line: 1, + Column: 10, + Byte: 9, + }) + if err != nil { + t.Fatal(err) + } + expectedCandidates := lang.ZeroCandidates() + if diff := cmp.Diff(expectedCandidates, candidates); diff != "" { + t.Fatalf("unexpected candidates: %s", diff) + } +} + func TestDecoder_CandidatesAtPos_zeroByteContent(t *testing.T) { resourceLabelSchema := []*schema.LabelSchema{ - {Name: "type", IsDepKey: true}, + {Name: "type", IsDepKey: true, Completable: true}, {Name: "name"}, } resourceSchema := &schema.BlockSchema{ @@ -653,7 +702,7 @@ func TestDecoder_CandidatesAtPos_zeroByteContent(t *testing.T) { func TestDecoder_CandidatesAtPos_endOfFilePos(t *testing.T) { resourceLabelSchema := []*schema.LabelSchema{ - {Name: "type", IsDepKey: true}, + {Name: "type", IsDepKey: true, Completable: true}, {Name: "name"}, } resourceSchema := &schema.BlockSchema{ @@ -717,7 +766,7 @@ func TestDecoder_CandidatesAtPos_endOfFilePos(t *testing.T) { func TestDecoder_CandidatesAtPos_emptyLabel(t *testing.T) { resourceLabelSchema := []*schema.LabelSchema{ - {Name: "type", IsDepKey: true}, + {Name: "type", IsDepKey: true, Completable: true}, {Name: "name"}, } resourceSchema := &schema.BlockSchema{ @@ -811,7 +860,7 @@ func TestDecoder_CandidatesAtPos_emptyLabel(t *testing.T) { func TestDecoder_CandidatesAtPos_emptyLabel_duplicateDepKeys(t *testing.T) { resourceLabelSchema := []*schema.LabelSchema{ - {Name: "type", IsDepKey: true}, + {Name: "type", IsDepKey: true, Completable: true}, {Name: "name"}, } resourceSchema := &schema.BlockSchema{ @@ -903,7 +952,7 @@ func TestDecoder_CandidatesAtPos_emptyLabel_duplicateDepKeys(t *testing.T) { func TestDecoder_CandidatesAtPos_basic(t *testing.T) { resourceLabelSchema := []*schema.LabelSchema{ - {Name: "type", IsDepKey: true}, + {Name: "type", IsDepKey: true, Completable: true}, {Name: "name"}, } @@ -1287,7 +1336,7 @@ func TestDecoder_CandidatesAtPos_AnyAttribute(t *testing.T) { func TestDecoder_CandidatesAtPos_multipleTypes(t *testing.T) { resourceLabelSchema := []*schema.LabelSchema{ - {Name: "type", IsDepKey: true}, + {Name: "type", IsDepKey: true, Completable: true}, {Name: "name"}, } diff --git a/schema/label_schema.go b/schema/label_schema.go index 3cdf605e..5f83c16f 100644 --- a/schema/label_schema.go +++ b/schema/label_schema.go @@ -12,6 +12,11 @@ type LabelSchema struct { // IsDepKey describes whether to use this label as key // when looking up dependent schema IsDepKey bool + + // In cases where label's IsDepKey=true any DependentKey label values + // within Blocks's DependentBody can be used for completion + // This enables such behaviour. + Completable bool } func (*LabelSchema) isSchemaImpl() schemaImplSigil { @@ -25,6 +30,7 @@ func (ls *LabelSchema) Copy() *LabelSchema { return &LabelSchema{ Name: ls.Name, + Completable: ls.Completable, Description: ls.Description, IsDepKey: ls.IsDepKey, }