-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
decoder: Implement completion for LiteralType
- Loading branch information
1 parent
6de5fd7
commit cfdd98f
Showing
4 changed files
with
1,309 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
package decoder | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
|
||
"github.com/hashicorp/hcl-lang/lang" | ||
"github.com/hashicorp/hcl-lang/schema" | ||
"github.com/hashicorp/hcl/v2" | ||
"github.com/hashicorp/hcl/v2/hclsyntax" | ||
"github.com/zclconf/go-cty/cty" | ||
) | ||
|
||
func (lt LiteralType) CompletionAtPos(ctx context.Context, pos hcl.Pos) []lang.Candidate { | ||
typ := lt.cons.Type | ||
|
||
if isEmptyExpression(lt.expr) { | ||
editRange := hcl.Range{ | ||
Filename: lt.expr.Range().Filename, | ||
Start: pos, | ||
End: pos, | ||
} | ||
|
||
if typ.IsPrimitiveType() { | ||
if typ == cty.Bool { | ||
return boolLiteralCandidates("", editRange) | ||
} | ||
return []lang.Candidate{} | ||
} | ||
|
||
if typ == cty.DynamicPseudoType { | ||
return []lang.Candidate{} | ||
} | ||
|
||
return []lang.Candidate{ | ||
{ | ||
Label: labelForLiteralType(typ), | ||
Detail: typ.FriendlyName(), | ||
Kind: candidateKindForType(typ), | ||
TextEdit: lang.TextEdit{ | ||
Range: editRange, | ||
NewText: newTextForLiteralType(typ), | ||
Snippet: snippetForLiteralType(1, typ), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
if typ == cty.Bool { | ||
return lt.completeBoolAtPos(ctx, typ, pos) | ||
} | ||
|
||
if typ.IsListType() { | ||
expr, ok := lt.expr.(*hclsyntax.TupleConsExpr) | ||
if !ok { | ||
return []lang.Candidate{} | ||
} | ||
|
||
cons := schema.List{ | ||
Elem: schema.LiteralType{ | ||
Type: typ.ElementType(), | ||
}, | ||
} | ||
|
||
return newExpression(lt.pathCtx, expr, cons).CompletionAtPos(ctx, pos) | ||
} | ||
|
||
if typ.IsSetType() { | ||
expr, ok := lt.expr.(*hclsyntax.TupleConsExpr) | ||
if !ok { | ||
return []lang.Candidate{} | ||
} | ||
|
||
cons := schema.Set{ | ||
Elem: schema.LiteralType{ | ||
Type: typ.ElementType(), | ||
}, | ||
} | ||
|
||
return newExpression(lt.pathCtx, expr, cons).CompletionAtPos(ctx, pos) | ||
} | ||
|
||
if typ.IsTupleType() { | ||
expr, ok := lt.expr.(*hclsyntax.TupleConsExpr) | ||
if !ok { | ||
return []lang.Candidate{} | ||
} | ||
|
||
elemTypes := typ.TupleElementTypes() | ||
cons := schema.Tuple{ | ||
Elems: make([]schema.Constraint, len(elemTypes)), | ||
} | ||
for i, elemType := range elemTypes { | ||
cons.Elems[i] = schema.LiteralType{ | ||
Type: elemType, | ||
} | ||
} | ||
|
||
return newExpression(lt.pathCtx, expr, cons).CompletionAtPos(ctx, pos) | ||
} | ||
|
||
if typ.IsMapType() { | ||
expr, ok := lt.expr.(*hclsyntax.ObjectConsExpr) | ||
if !ok { | ||
return []lang.Candidate{} | ||
} | ||
|
||
cons := schema.Map{ | ||
Elem: schema.LiteralType{ | ||
Type: typ.ElementType(), | ||
}, | ||
} | ||
return newExpression(lt.pathCtx, expr, cons).CompletionAtPos(ctx, pos) | ||
} | ||
|
||
if typ.IsObjectType() { | ||
expr, ok := lt.expr.(*hclsyntax.ObjectConsExpr) | ||
if !ok { | ||
return []lang.Candidate{} | ||
} | ||
|
||
cons := schema.Object{ | ||
Attributes: ctyObjectToObjectAttributes(typ), | ||
} | ||
return newExpression(lt.pathCtx, expr, cons).CompletionAtPos(ctx, pos) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (lt LiteralType) completeBoolAtPos(ctx context.Context, typ cty.Type, pos hcl.Pos) []lang.Candidate { | ||
switch eType := lt.expr.(type) { | ||
|
||
case *hclsyntax.ScopeTraversalExpr: | ||
prefixLen := pos.Byte - eType.Range().Start.Byte | ||
prefix := eType.Traversal.RootName()[0:prefixLen] | ||
return boolLiteralCandidates(prefix, eType.Range()) | ||
|
||
case *hclsyntax.LiteralValueExpr: | ||
if eType.Val.Type() == cty.Bool { | ||
value := "false" | ||
if eType.Val.True() { | ||
value = "true" | ||
} | ||
prefixLen := pos.Byte - eType.Range().Start.Byte | ||
prefix := value[0:prefixLen] | ||
return boolLiteralCandidates(prefix, eType.Range()) | ||
} | ||
} | ||
|
||
return []lang.Candidate{} | ||
} | ||
|
||
func boolLiteralCandidates(prefix string, editRange hcl.Range) []lang.Candidate { | ||
candidates := make([]lang.Candidate, 0) | ||
|
||
if strings.HasPrefix("false", prefix) { | ||
candidates = append(candidates, lang.Candidate{ | ||
Label: "false", | ||
Detail: cty.Bool.FriendlyNameForConstraint(), | ||
Kind: lang.BoolCandidateKind, | ||
TextEdit: lang.TextEdit{ | ||
NewText: "false", | ||
Snippet: "false", | ||
Range: editRange, | ||
}, | ||
}) | ||
} | ||
if strings.HasPrefix("true", prefix) { | ||
candidates = append(candidates, lang.Candidate{ | ||
Label: "true", | ||
Detail: cty.Bool.FriendlyNameForConstraint(), | ||
Kind: lang.BoolCandidateKind, | ||
TextEdit: lang.TextEdit{ | ||
NewText: "true", | ||
Snippet: "true", | ||
Range: editRange, | ||
}, | ||
}) | ||
} | ||
|
||
return candidates | ||
} | ||
|
||
func ctyObjectToObjectAttributes(objType cty.Type) schema.ObjectAttributes { | ||
attrTypes := objType.AttributeTypes() | ||
objAttributes := make(schema.ObjectAttributes, len(attrTypes)) | ||
|
||
for name, attrType := range attrTypes { | ||
aSchema := &schema.AttributeSchema{ | ||
Constraint: schema.LiteralType{ | ||
Type: attrType, | ||
}, | ||
} | ||
if objType.AttributeOptional(name) { | ||
aSchema.IsOptional = true | ||
} else { | ||
aSchema.IsRequired = true | ||
} | ||
objAttributes[name] = aSchema | ||
} | ||
|
||
return objAttributes | ||
} |
Oops, something went wrong.