Skip to content

Commit 058bfb1

Browse files
dbanckradeksimko
andauthored
Add SortText and respect maxCandidates for hook completion (#126)
* Add `SortText` to candidates * Only allow `maxCandidates` when building completion candidates * Update decoder/expression_candidates.go Co-authored-by: Radek Simko <[email protected]> Co-authored-by: Radek Simko <[email protected]>
1 parent 118ac45 commit 058bfb1

File tree

4 files changed

+91
-2
lines changed

4 files changed

+91
-2
lines changed

decoder/context.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ func (d *Decoder) SetContext(ctx DecoderContext) {
5151

5252
// CompletionFunc is the function signature for completion hooks.
5353
//
54-
// The completion func has access to path, filename and pos via context:
54+
// The completion func has access to path, filename, pos and maximum
55+
// candidate count via context:
5556
// path, ok := decoder.PathFromContext(ctx)
5657
// filename, ok := decoder.FilenameFromContext(ctx)
5758
// pos, ok := decoder.PosFromContext(ctx)
59+
// maxCandidates, ok := decoder.MaxCandidatesFromContext(ctx)
5860
type CompletionFunc func(ctx context.Context, value cty.Value) ([]Candidate, error)
5961
type CompletionFuncMap map[string]CompletionFunc
6062

@@ -90,6 +92,10 @@ type Candidate struct {
9092
// ResolveHook represents a resolve hook to call
9193
// and any arguments to pass to it
9294
ResolveHook *lang.ResolveHook
95+
96+
// SortText is an optional string that will be used when comparing this
97+
// candidate with other candidates
98+
SortText string
9399
}
94100

95101
// ExpressionCandidate is a simplified version of Candidate and the preferred

decoder/expression_candidates.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@ func (d *PathDecoder) attrValueCandidatesAtPos(ctx context.Context, attr *hclsyn
2525
candidates.List = append(candidates.List, d.candidatesFromHooks(ctx, attr, schema, outerBodyRng, pos)...)
2626
}
2727

28-
if len(constraints) > 0 {
28+
count := len(candidates.List)
29+
if len(constraints) > 0 && uint(count) < d.maxCandidates {
2930
prefixRng := editRng
3031
prefixRng.End = pos
3132

3233
for _, c := range constraints {
34+
if uint(count) >= d.maxCandidates {
35+
return candidates, nil
36+
}
37+
3338
candidates.List = append(candidates.List, d.constraintToCandidates(c, outerBodyRng, prefixRng, editRng)...)
39+
count++
3440
}
3541
}
3642

@@ -186,6 +192,13 @@ func FilenameFromContext(ctx context.Context) (string, bool) {
186192
return f, ok
187193
}
188194

195+
type maxCandidatesKey struct{}
196+
197+
func MaxCandidatesFromContext(ctx context.Context) (uint, bool) {
198+
mc, ok := ctx.Value(maxCandidatesKey{}).(uint)
199+
return mc, ok
200+
}
201+
189202
func isEmptyExpr(expr hclsyntax.Expression) bool {
190203
l, ok := expr.(*hclsyntax.LiteralValueExpr)
191204
if !ok {
@@ -233,12 +246,18 @@ func (d *PathDecoder) candidatesFromHooks(ctx context.Context, attr *hclsyntax.A
233246
ctx = context.WithValue(ctx, pathKey{}, d.path)
234247
ctx = context.WithValue(ctx, filenameKey{}, attr.Expr.Range().Filename)
235248
ctx = context.WithValue(ctx, posKey{}, pos)
249+
ctx = context.WithValue(ctx, maxCandidatesKey{}, d.maxCandidates)
236250

251+
count := 0
237252
for _, hook := range schema.CompletionHooks {
238253
if completionFunc, ok := d.decoderCtx.CompletionHooks[hook.Name]; ok {
239254
res, _ := completionFunc(ctx, cty.StringVal(prefix))
240255

241256
for _, c := range res {
257+
if uint(count) >= d.maxCandidates {
258+
return candidates
259+
}
260+
242261
candidates = append(candidates, lang.Candidate{
243262
Label: c.Label,
244263
Detail: c.Detail,
@@ -251,7 +270,9 @@ func (d *PathDecoder) candidatesFromHooks(ctx context.Context, attr *hclsyntax.A
251270
Range: editRng,
252271
},
253272
ResolveHook: c.ResolveHook,
273+
SortText: c.SortText,
254274
})
275+
count++
255276
}
256277

257278
}

decoder/expression_candidates_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2925,3 +2925,61 @@ func TestDecoder_CandidateAtPos_expressions_Hooks(t *testing.T) {
29252925
})
29262926
}
29272927
}
2928+
2929+
func TestDecoder_CandidateAtPos_maxCandidates(t *testing.T) {
2930+
ctx := context.Background()
2931+
bodySchema := &schema.BodySchema{
2932+
Attributes: map[string]*schema.AttributeSchema{
2933+
"attr": {
2934+
Expr: schema.LiteralTypeOnly(cty.String),
2935+
CompletionHooks: lang.CompletionHooks{
2936+
{
2937+
Name: "TestCompletionHook50",
2938+
},
2939+
{
2940+
Name: "TestCompletionHook70",
2941+
},
2942+
},
2943+
},
2944+
},
2945+
}
2946+
2947+
// We're ignoring diagnostics here, since our config contains invalid HCL
2948+
f, _ := hclsyntax.ParseConfig([]byte(`attr = `), "test.tf", hcl.InitialPos)
2949+
d := testPathDecoder(t, &PathContext{
2950+
Schema: bodySchema,
2951+
Files: map[string]*hcl.File{
2952+
"test.tf": f,
2953+
},
2954+
})
2955+
d.decoderCtx.CompletionHooks["TestCompletionHook50"] = func(ctx context.Context, value cty.Value) ([]Candidate, error) {
2956+
candidates := make([]Candidate, 0)
2957+
for i := 0; i < 50; i++ {
2958+
candidates = append(candidates, Candidate{
2959+
Label: fmt.Sprintf("\"Label %d\"", i),
2960+
Kind: lang.StringCandidateKind,
2961+
})
2962+
}
2963+
return candidates, nil
2964+
}
2965+
d.decoderCtx.CompletionHooks["TestCompletionHook70"] = func(ctx context.Context, value cty.Value) ([]Candidate, error) {
2966+
candidates := make([]Candidate, 0)
2967+
for i := 0; i < 70; i++ {
2968+
candidates = append(candidates, Candidate{
2969+
Label: fmt.Sprintf("\"Label %d\"", i),
2970+
Kind: lang.StringCandidateKind,
2971+
})
2972+
}
2973+
return candidates, nil
2974+
}
2975+
2976+
candidates, err := d.CandidatesAtPos(ctx, "test.tf", hcl.Pos{Line: 1, Column: 8, Byte: 7})
2977+
if err != nil {
2978+
t.Fatal(err)
2979+
}
2980+
count := len(candidates.List)
2981+
2982+
if uint(count) != d.maxCandidates {
2983+
t.Fatalf("unexpected candidates count: %d", count)
2984+
}
2985+
}

lang/candidate.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ type Candidate struct {
4646
// ResolveHook allows resolution of additional information
4747
// for a completion candidate via ResolveCandidate.
4848
ResolveHook *ResolveHook
49+
50+
// SortText is an optional string that will be used when comparing this
51+
// candidate with other candidates
52+
SortText string
4953
}
5054

5155
// TextEdit represents a change (edit) of an HCL config file

0 commit comments

Comments
 (0)