| package hclsyntax |
| |
| import ( |
| "github.com/hashicorp/hcl/v2" |
| ) |
| |
| // ----------------------------------------------------------------------------- |
| // The methods in this file are all optional extension methods that serve to |
| // implement the methods of the same name on *hcl.File when its root body |
| // is provided by this package. |
| // ----------------------------------------------------------------------------- |
| |
| // BlocksAtPos implements the method of the same name for an *hcl.File that |
| // is backed by a *Body. |
| func (b *Body) BlocksAtPos(pos hcl.Pos) []*hcl.Block { |
| list, _ := b.blocksAtPos(pos, true) |
| return list |
| } |
| |
| // InnermostBlockAtPos implements the method of the same name for an *hcl.File |
| // that is backed by a *Body. |
| func (b *Body) InnermostBlockAtPos(pos hcl.Pos) *hcl.Block { |
| _, innermost := b.blocksAtPos(pos, false) |
| return innermost.AsHCLBlock() |
| } |
| |
| // OutermostBlockAtPos implements the method of the same name for an *hcl.File |
| // that is backed by a *Body. |
| func (b *Body) OutermostBlockAtPos(pos hcl.Pos) *hcl.Block { |
| return b.outermostBlockAtPos(pos).AsHCLBlock() |
| } |
| |
| // blocksAtPos is the internal engine of both BlocksAtPos and |
| // InnermostBlockAtPos, which both need to do the same logic but return a |
| // differently-shaped result. |
| // |
| // list is nil if makeList is false, avoiding an allocation. Innermost is |
| // always set, and if the returned list is non-nil it will always match the |
| // final element from that list. |
| func (b *Body) blocksAtPos(pos hcl.Pos, makeList bool) (list []*hcl.Block, innermost *Block) { |
| current := b |
| |
| Blocks: |
| for current != nil { |
| for _, block := range current.Blocks { |
| wholeRange := hcl.RangeBetween(block.TypeRange, block.CloseBraceRange) |
| if wholeRange.ContainsPos(pos) { |
| innermost = block |
| if makeList { |
| list = append(list, innermost.AsHCLBlock()) |
| } |
| current = block.Body |
| continue Blocks |
| } |
| } |
| |
| // If we fall out here then none of the current body's nested blocks |
| // contain the position we are looking for, and so we're done. |
| break |
| } |
| |
| return |
| } |
| |
| // outermostBlockAtPos is the internal version of OutermostBlockAtPos that |
| // returns a hclsyntax.Block rather than an hcl.Block, allowing for further |
| // analysis if necessary. |
| func (b *Body) outermostBlockAtPos(pos hcl.Pos) *Block { |
| // This is similar to blocksAtPos, but simpler because we know it only |
| // ever needs to search the first level of nested blocks. |
| |
| for _, block := range b.Blocks { |
| wholeRange := hcl.RangeBetween(block.TypeRange, block.CloseBraceRange) |
| if wholeRange.ContainsPos(pos) { |
| return block |
| } |
| } |
| |
| return nil |
| } |
| |
| // AttributeAtPos implements the method of the same name for an *hcl.File |
| // that is backed by a *Body. |
| func (b *Body) AttributeAtPos(pos hcl.Pos) *hcl.Attribute { |
| return b.attributeAtPos(pos).AsHCLAttribute() |
| } |
| |
| // attributeAtPos is the internal version of AttributeAtPos that returns a |
| // hclsyntax.Block rather than an hcl.Block, allowing for further analysis if |
| // necessary. |
| func (b *Body) attributeAtPos(pos hcl.Pos) *Attribute { |
| searchBody := b |
| _, block := b.blocksAtPos(pos, false) |
| if block != nil { |
| searchBody = block.Body |
| } |
| |
| for _, attr := range searchBody.Attributes { |
| if attr.SrcRange.ContainsPos(pos) { |
| return attr |
| } |
| } |
| |
| return nil |
| } |
| |
| // OutermostExprAtPos implements the method of the same name for an *hcl.File |
| // that is backed by a *Body. |
| func (b *Body) OutermostExprAtPos(pos hcl.Pos) hcl.Expression { |
| attr := b.attributeAtPos(pos) |
| if attr == nil { |
| return nil |
| } |
| if !attr.Expr.Range().ContainsPos(pos) { |
| return nil |
| } |
| return attr.Expr |
| } |