1package hclsyntax 2 3import ( 4 "github.com/hashicorp/hcl2/hcl" 5) 6 7// ----------------------------------------------------------------------------- 8// The methods in this file are all optional extension methods that serve to 9// implement the methods of the same name on *hcl.File when its root body 10// is provided by this package. 11// ----------------------------------------------------------------------------- 12 13// BlocksAtPos implements the method of the same name for an *hcl.File that 14// is backed by a *Body. 15func (b *Body) BlocksAtPos(pos hcl.Pos) []*hcl.Block { 16 list, _ := b.blocksAtPos(pos, true) 17 return list 18} 19 20// InnermostBlockAtPos implements the method of the same name for an *hcl.File 21// that is backed by a *Body. 22func (b *Body) InnermostBlockAtPos(pos hcl.Pos) *hcl.Block { 23 _, innermost := b.blocksAtPos(pos, false) 24 return innermost.AsHCLBlock() 25} 26 27// OutermostBlockAtPos implements the method of the same name for an *hcl.File 28// that is backed by a *Body. 29func (b *Body) OutermostBlockAtPos(pos hcl.Pos) *hcl.Block { 30 return b.outermostBlockAtPos(pos).AsHCLBlock() 31} 32 33// blocksAtPos is the internal engine of both BlocksAtPos and 34// InnermostBlockAtPos, which both need to do the same logic but return a 35// differently-shaped result. 36// 37// list is nil if makeList is false, avoiding an allocation. Innermost is 38// always set, and if the returned list is non-nil it will always match the 39// final element from that list. 40func (b *Body) blocksAtPos(pos hcl.Pos, makeList bool) (list []*hcl.Block, innermost *Block) { 41 current := b 42 43Blocks: 44 for current != nil { 45 for _, block := range current.Blocks { 46 wholeRange := hcl.RangeBetween(block.TypeRange, block.CloseBraceRange) 47 if wholeRange.ContainsPos(pos) { 48 innermost = block 49 if makeList { 50 list = append(list, innermost.AsHCLBlock()) 51 } 52 current = block.Body 53 continue Blocks 54 } 55 } 56 57 // If we fall out here then none of the current body's nested blocks 58 // contain the position we are looking for, and so we're done. 59 break 60 } 61 62 return 63} 64 65// outermostBlockAtPos is the internal version of OutermostBlockAtPos that 66// returns a hclsyntax.Block rather than an hcl.Block, allowing for further 67// analysis if necessary. 68func (b *Body) outermostBlockAtPos(pos hcl.Pos) *Block { 69 // This is similar to blocksAtPos, but simpler because we know it only 70 // ever needs to search the first level of nested blocks. 71 72 for _, block := range b.Blocks { 73 wholeRange := hcl.RangeBetween(block.TypeRange, block.CloseBraceRange) 74 if wholeRange.ContainsPos(pos) { 75 return block 76 } 77 } 78 79 return nil 80} 81 82// AttributeAtPos implements the method of the same name for an *hcl.File 83// that is backed by a *Body. 84func (b *Body) AttributeAtPos(pos hcl.Pos) *hcl.Attribute { 85 return b.attributeAtPos(pos).AsHCLAttribute() 86} 87 88// attributeAtPos is the internal version of AttributeAtPos that returns a 89// hclsyntax.Block rather than an hcl.Block, allowing for further analysis if 90// necessary. 91func (b *Body) attributeAtPos(pos hcl.Pos) *Attribute { 92 searchBody := b 93 _, block := b.blocksAtPos(pos, false) 94 if block != nil { 95 searchBody = block.Body 96 } 97 98 for _, attr := range searchBody.Attributes { 99 if attr.SrcRange.ContainsPos(pos) { 100 return attr 101 } 102 } 103 104 return nil 105} 106 107// OutermostExprAtPos implements the method of the same name for an *hcl.File 108// that is backed by a *Body. 109func (b *Body) OutermostExprAtPos(pos hcl.Pos) hcl.Expression { 110 attr := b.attributeAtPos(pos) 111 if attr == nil { 112 return nil 113 } 114 if !attr.Expr.Range().ContainsPos(pos) { 115 return nil 116 } 117 return attr.Expr 118} 119