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