diff --git a/decoder/expr_keyword.go b/decoder/expr_keyword.go index de65b7c6..72686333 100644 --- a/decoder/expr_keyword.go +++ b/decoder/expr_keyword.go @@ -95,8 +95,26 @@ func (kw Keyword) HoverAtPos(ctx context.Context, pos hcl.Pos) *lang.HoverData { } func (kw Keyword) SemanticTokens(ctx context.Context) []lang.SemanticToken { - // TODO - return nil + eType, ok := kw.expr.(*hclsyntax.ScopeTraversalExpr) + if !ok { + return []lang.SemanticToken{} + } + + if len(eType.Traversal) != 1 { + return []lang.SemanticToken{} + } + + if eType.Traversal.RootName() == kw.cons.Keyword { + return []lang.SemanticToken{ + { + Type: lang.TokenKeyword, + Modifiers: []lang.SemanticTokenModifier{}, + Range: eType.Range(), + }, + } + } + + return []lang.SemanticToken{} } func (kw Keyword) ReferenceOrigins(ctx context.Context, allowSelfRefs bool) reference.Origins { diff --git a/decoder/expr_keyword_test.go b/decoder/expr_keyword_test.go index 38018406..baa9e29d 100644 --- a/decoder/expr_keyword_test.go +++ b/decoder/expr_keyword_test.go @@ -240,3 +240,113 @@ func TestHoverAtPos_exprKeyword(t *testing.T) { }) } } + +func TestSemanticTokens_exprKeyword(t *testing.T) { + testCases := []struct { + testName string + attrSchema map[string]*schema.AttributeSchema + cfg string + expectedSemanticTokens []lang.SemanticToken + }{ + { + "mismatching expression type", + map[string]*schema.AttributeSchema{ + "attr": { + Constraint: schema.Keyword{ + Keyword: "foobar", + }, + }, + }, + `attr = "foobar"`, + []lang.SemanticToken{ + { + Type: "hcl-attrName", + Modifiers: lang.SemanticTokenModifiers{}, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + }, + }, + }, + }, + { + "mismatching keyword", + map[string]*schema.AttributeSchema{ + "attr": { + Constraint: schema.Keyword{ + Keyword: "foobar", + }, + }, + }, + `attr = barfoo`, + []lang.SemanticToken{ + { + Type: "hcl-attrName", + Modifiers: lang.SemanticTokenModifiers{}, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + }, + }, + }, + }, + { + "matching keyword", + map[string]*schema.AttributeSchema{ + "attr": { + Constraint: schema.Keyword{ + Keyword: "foobar", + }, + }, + }, + `attr = foobar`, + []lang.SemanticToken{ + { + Type: "hcl-attrName", + Modifiers: lang.SemanticTokenModifiers{}, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 5, Byte: 4}, + }, + }, + { + Type: "hcl-keyword", + Modifiers: lang.SemanticTokenModifiers{}, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 8, Byte: 7}, + End: hcl.Pos{Line: 1, Column: 14, Byte: 13}, + }, + }, + }, + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d-%s", i, tc.testName), func(t *testing.T) { + bodySchema := &schema.BodySchema{ + Attributes: tc.attrSchema, + } + + f, _ := hclsyntax.ParseConfig([]byte(tc.cfg), "test.tf", hcl.InitialPos) + d := testPathDecoder(t, &PathContext{ + Schema: bodySchema, + Files: map[string]*hcl.File{ + "test.tf": f, + }, + }) + + ctx := context.Background() + tokens, err := d.SemanticTokensInFile(ctx, "test.tf") + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(tc.expectedSemanticTokens, tokens); diff != "" { + t.Fatalf("unexpected tokens: %s", diff) + } + }) + } +}