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  }