Skip to content

Commit

Permalink
decoder: Add support for Reference as Constraint (#185)
Browse files Browse the repository at this point in the history
* schema: Implement EmptyCompletionData for Reference

* decoder: Implement completion for Reference

* decoder: Implement hover for Reference

* decoder: Implement semantic tokens for Reference

* decoder: Implement reference origins for Reference

* decoder: Implement reference targets for Reference
  • Loading branch information
radeksimko authored Mar 24, 2023
1 parent ba16692 commit d469d16
Show file tree
Hide file tree
Showing 16 changed files with 2,185 additions and 35 deletions.
34 changes: 3 additions & 31 deletions decoder/expr_reference.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,12 @@
package decoder

import (
"context"

"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/hcl-lang/reference"
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/hcl/v2"
)

type Reference struct {
expr hcl.Expression
cons schema.Reference
}

func (ref Reference) CompletionAtPos(ctx context.Context, pos hcl.Pos) []lang.Candidate {
// TODO
return nil
}

func (ref Reference) HoverAtPos(ctx context.Context, pos hcl.Pos) *lang.HoverData {
// TODO
return nil
}

func (ref Reference) SemanticTokens(ctx context.Context) []lang.SemanticToken {
// TODO
return nil
}

func (ref Reference) ReferenceOrigins(ctx context.Context, allowSelfRefs bool) reference.Origins {
// TODO
return nil
}

func (ref Reference) ReferenceTargets(ctx context.Context, targetCtx *TargetContext) reference.Targets {
// TODO
return nil
expr hcl.Expression
cons schema.Reference
pathCtx *PathContext
}
99 changes: 99 additions & 0 deletions decoder/expr_reference_completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package decoder

import (
"context"

"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/hcl-lang/reference"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
)

func (ref Reference) CompletionAtPos(ctx context.Context, pos hcl.Pos) []lang.Candidate {
if ref.cons.Address != nil {
// no candidates if traversal itself is addressable
return []lang.Candidate{}
}

if ref.pathCtx.ReferenceTargets == nil {
return []lang.Candidate{}
}

file := ref.pathCtx.Files[ref.expr.Range().Filename]
rootBody, ok := file.Body.(*hclsyntax.Body)
if !ok {
return []lang.Candidate{}
}

outerBodyRng := rootBody.Range()
// Find outer block body range to allow filtering
// of references pointing back to the same block
outerBlock := rootBody.OutermostBlockAtPos(pos)
if outerBlock != nil {
ob := outerBlock.Body.(*hclsyntax.Body)
outerBodyRng = ob.Range()
}

if isEmptyExpression(ref.expr) {
editRng := hcl.Range{
Filename: ref.expr.Range().Filename,
Start: pos,
End: pos,
}
candidates := make([]lang.Candidate, 0)
ref.pathCtx.ReferenceTargets.MatchWalk(ctx, ref.cons, "", outerBodyRng, editRng, func(target reference.Target) error {
address := target.Address(ctx, editRng.Start).String()

candidates = append(candidates, lang.Candidate{
Label: address,
Detail: target.FriendlyName(),
Description: target.Description,
Kind: lang.TraversalCandidateKind,
TextEdit: lang.TextEdit{
NewText: address,
Snippet: address,
Range: editRng,
},
})
return nil
})
return candidates
}

eType, ok := ref.expr.(*hclsyntax.ScopeTraversalExpr)
if !ok {
return []lang.Candidate{}
}

editRng := eType.Range()
if !editRng.ContainsPos(pos) {
// account for trailing character(s) which doesn't appear in AST
// such as dot, opening bracket etc.
editRng.End = pos
}
prefixRng := hcl.Range{
Filename: eType.Range().Filename,
Start: eType.Range().Start,
End: pos,
}
prefix := string(prefixRng.SliceBytes(file.Bytes))

candidates := make([]lang.Candidate, 0)
ref.pathCtx.ReferenceTargets.MatchWalk(ctx, ref.cons, prefix, outerBodyRng, editRng, func(target reference.Target) error {
address := target.Address(ctx, editRng.Start).String()

candidates = append(candidates, lang.Candidate{
Label: address,
Detail: target.FriendlyName(),
Description: target.Description,
Kind: lang.TraversalCandidateKind,
TextEdit: lang.TextEdit{
NewText: address,
Snippet: address,
Range: editRng,
},
})
return nil
})
return candidates
}
Loading

0 comments on commit d469d16

Please sign in to comment.