Skip to content

Commit

Permalink
schema: Implement EmptyCompletionData for LiteralType
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko committed Jan 20, 2023
1 parent 0a9e3b0 commit 3fdd7bf
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 15 deletions.
24 changes: 12 additions & 12 deletions decoder/attribute_candidates_legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"primitive type",
"primitive",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.String),
Constraint: schema.LiteralType{Type: cty.String},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand All @@ -47,7 +47,7 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"map of strings",
"mymap",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.Map(cty.String)),
Constraint: schema.LiteralType{Type: cty.Map(cty.String)},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand All @@ -72,7 +72,7 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"map of numbers",
"mymap",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.Map(cty.Number)),
Constraint: schema.LiteralType{Type: cty.Map(cty.Number)},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand All @@ -97,7 +97,7 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"list of numbers",
"mylist",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.List(cty.Number)),
Constraint: schema.LiteralType{Type: cty.List(cty.Number)},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand All @@ -120,10 +120,10 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"list of objects",
"mylistobj",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.List(cty.Object(map[string]cty.Type{
Constraint: schema.LiteralType{Type: cty.List(cty.Object(map[string]cty.Type{
"first": cty.String,
"second": cty.Number,
}))),
}))},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand All @@ -149,7 +149,7 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"set of numbers",
"myset",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.Set(cty.Number)),
Constraint: schema.LiteralType{Type: cty.Set(cty.Number)},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand All @@ -172,11 +172,11 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"object",
"myobj",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.Object(map[string]cty.Type{
Constraint: schema.LiteralType{Type: cty.Object(map[string]cty.Type{
"keystr": cty.String,
"keynum": cty.Number,
"keybool": cty.Bool,
})),
})},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand All @@ -203,7 +203,7 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"unknown type",
"mynil",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.DynamicPseudoType),
Constraint: schema.LiteralType{Type: cty.DynamicPseudoType},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand All @@ -226,13 +226,13 @@ func TestLegacyDecoder_CompletionAtPos_EmptyCompletionData(t *testing.T) {
"nested object",
"myobj",
&schema.AttributeSchema{
Expr: schema.LiteralTypeOnly(cty.Object(map[string]cty.Type{
Constraint: schema.LiteralType{Type: cty.Object(map[string]cty.Type{
"keystr": cty.String,
"another": cty.Object(map[string]cty.Type{
"nestedstr": cty.String,
"nested_number": cty.Number,
}),
})),
})},
},
lang.CompleteCandidates([]lang.Candidate{
{
Expand Down
161 changes: 158 additions & 3 deletions schema/constraint_literal_type.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package schema

import (
"fmt"
"sort"
"strings"

"github.com/zclconf/go-cty/cty"
)

Expand All @@ -16,6 +20,10 @@ import (
// e.g. LiteralType{Type: cty.List(...)}.
type LiteralType struct {
Type cty.Type

// nestingLvl represents current level of nesting
// used for correctly indenting complex types.
nestingLvl int
}

func (LiteralType) isConstraintImpl() constraintSigil {
Expand All @@ -28,11 +36,158 @@ func (lt LiteralType) FriendlyName() string {

func (lt LiteralType) Copy() Constraint {
return LiteralType{
Type: lt.Type,
Type: lt.Type,
nestingLvl: lt.nestingLvl,
}
}

func (lt LiteralType) EmptyCompletionData(nextPlaceholder int) CompletionData {
// TODO
return CompletionData{}
// primitive types
switch lt.Type {
case cty.String:
return CompletionData{
NewText: `""`,
Snippet: fmt.Sprintf(`"${%d:value}"`, nextPlaceholder),
LastPlaceholder: nextPlaceholder,
}
case cty.Bool:
return CompletionData{
NewText: "false",
Snippet: fmt.Sprintf(`${%d:false}`, nextPlaceholder),
LastPlaceholder: nextPlaceholder,
}
case cty.Number:
return CompletionData{
NewText: "1",
Snippet: fmt.Sprintf(`${%d:1}`, nextPlaceholder),
LastPlaceholder: nextPlaceholder,
}
case cty.DynamicPseudoType:
return CompletionData{
NewText: "",
Snippet: fmt.Sprintf(`${%d}`, nextPlaceholder),
LastPlaceholder: nextPlaceholder,
}
}

// complex types typically span multiple lines, so indenting matters
indent := strings.Repeat(" ", lt.nestingLvl+1)
endBraceIndent := strings.Repeat(" ", lt.nestingLvl)

if lt.Type.IsMapType() {
elCons := LiteralType{
Type: *lt.Type.MapElementType(),
nestingLvl: lt.nestingLvl + 1,
}
elCd := elCons.EmptyCompletionData(nextPlaceholder + 1)

return CompletionData{
NewText: fmt.Sprintf("{\n"+`%s"key" = %s`+"%s\n}",
indent, elCd.NewText, endBraceIndent),
Snippet: fmt.Sprintf("{\n"+`%s"${%d:key}" = %s`+"%s\n}",
indent, nextPlaceholder, elCd.Snippet, endBraceIndent),
LastPlaceholder: elCd.LastPlaceholder,
}
}

if lt.Type.IsObjectType() {
newText := ""
snippet := ""
attrNames := sortedObjectAttrNames(lt.Type)

lastPlaceholder := nextPlaceholder
for i, name := range attrNames {
elType := lt.Type.AttributeType(name)
attrCons := LiteralType{
Type: elType,
nestingLvl: lt.nestingLvl + 1,
}
if i > 0 {
lastPlaceholder++
}
attrCd := attrCons.EmptyCompletionData(lastPlaceholder)

newText += fmt.Sprintf("%s%s = %s\n", indent, name, attrCd.NewText)
snippet += fmt.Sprintf("%s%s = %s\n", indent, name, attrCd.Snippet)
lastPlaceholder = attrCd.LastPlaceholder
}

return CompletionData{
NewText: fmt.Sprintf("{\n%s%s}", newText, endBraceIndent),
Snippet: fmt.Sprintf("{\n%s%s}", snippet, endBraceIndent),
LastPlaceholder: lastPlaceholder,
}
}

if lt.Type.IsListType() || lt.Type.IsSetType() {
elCons := LiteralType{
Type: lt.Type.ElementType(),
}
elCd := elCons.EmptyCompletionData(nextPlaceholder)

return CompletionData{
NewText: fmt.Sprintf("[ %s ]", elCd.NewText),
Snippet: fmt.Sprintf(`[ %s ]`, elCd.Snippet),
LastPlaceholder: elCd.LastPlaceholder,
}
}

if lt.Type.IsTupleType() {
elTypes := lt.Type.TupleElementTypes()
if len(elTypes) == 1 {
elCons := LiteralType{
Type: elTypes[0],
}
elCd := elCons.EmptyCompletionData(nextPlaceholder)

return CompletionData{
NewText: fmt.Sprintf("[ %s ]", elCd.NewText),
Snippet: fmt.Sprintf(`[ %s ]`, elCd.Snippet),
LastPlaceholder: elCd.LastPlaceholder,
}
}

newText := ""
snippet := ""
lastPlaceholder := nextPlaceholder
for i, elType := range elTypes {
elCons := LiteralType{
Type: elType,
nestingLvl: lt.nestingLvl + 1,
}
elCd := elCons.EmptyCompletionData(lastPlaceholder + i)

newText += elCd.NewText + ",\n"
snippet += elCd.Snippet + ",\n"
lastPlaceholder = elCd.LastPlaceholder
}
return CompletionData{
NewText: fmt.Sprintf("[\n%s%s]", newText, endBraceIndent),
Snippet: fmt.Sprintf("[\n%s%s]", snippet, endBraceIndent),
LastPlaceholder: lastPlaceholder,
}
}

return CompletionData{
NewText: "",
Snippet: "",
LastPlaceholder: nextPlaceholder,
}
}

func sortedObjectAttrNames(obj cty.Type) []string {
if !obj.IsObjectType() {
return []string{}
}

types := obj.AttributeTypes()
names := make([]string, len(types))
i := 0
for name := range types {
names[i] = name
i++
}

sort.Strings(names)
return names
}

0 comments on commit 3fdd7bf

Please sign in to comment.