github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/edit/complete/node_path.go (about)

     1  package complete
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/markusbkk/elvish/pkg/parse"
     7  )
     8  
     9  type nodePath []parse.Node
    10  
    11  // Returns the path of Node's from n to a leaf at position p. Leaf first in the
    12  // returned slice.
    13  func findNodePath(root parse.Node, p int) nodePath {
    14  	n := root
    15  descend:
    16  	for len(parse.Children(n)) > 0 {
    17  		for _, ch := range parse.Children(n) {
    18  			if rg := ch.Range(); rg.From <= p && p <= rg.To {
    19  				n = ch
    20  				continue descend
    21  			}
    22  		}
    23  		return nil
    24  	}
    25  	var path []parse.Node
    26  	for {
    27  		path = append(path, n)
    28  		if n == root {
    29  			break
    30  		}
    31  		n = parse.Parent(n)
    32  	}
    33  	return path
    34  }
    35  
    36  func (ns nodePath) match(ms ...nodesMatcher) bool {
    37  	for _, m := range ms {
    38  		ns2, ok := m.matchNodes(ns)
    39  		if !ok {
    40  			return false
    41  		}
    42  		ns = ns2
    43  	}
    44  	return true
    45  }
    46  
    47  type nodesMatcher interface {
    48  	// Matches from the beginning of nodes. Returns the remaining nodes and
    49  	// whether the match succeeded.
    50  	matchNodes([]parse.Node) ([]parse.Node, bool)
    51  }
    52  
    53  // Matches one node of a given type, without storing it.
    54  //
    55  // TODO: Avoid reflection with generics when Elvish requires Go 1.18.
    56  type typedMatcher struct{ typ reflect.Type }
    57  
    58  func typed(n parse.Node) nodesMatcher { return typedMatcher{reflect.TypeOf(n)} }
    59  
    60  func (m typedMatcher) matchNodes(ns []parse.Node) ([]parse.Node, bool) {
    61  	if len(ns) > 0 && reflect.TypeOf(ns[0]) == m.typ {
    62  		return ns[1:], true
    63  	}
    64  	return nil, false
    65  }
    66  
    67  var (
    68  	aChunk    = typed(&parse.Chunk{})
    69  	aPipeline = typed(&parse.Pipeline{})
    70  	aArray    = typed(&parse.Array{})
    71  	aRedir    = typed(&parse.Redir{})
    72  	aSep      = typed(&parse.Sep{})
    73  )
    74  
    75  // Matches one node of a certain type, and stores it into a pointer.
    76  //
    77  // TODO: Avoid reflection with generics when Elvish requires Go 1.18.
    78  type storeMatcher struct {
    79  	p   reflect.Value
    80  	typ reflect.Type
    81  }
    82  
    83  func store(p interface{}) nodesMatcher {
    84  	dst := reflect.ValueOf(p).Elem()
    85  	return storeMatcher{dst, dst.Type()}
    86  }
    87  
    88  func (m storeMatcher) matchNodes(ns []parse.Node) ([]parse.Node, bool) {
    89  	if len(ns) > 0 && reflect.TypeOf(ns[0]) == m.typ {
    90  		m.p.Set(reflect.ValueOf(ns[0]))
    91  		return ns[1:], true
    92  	}
    93  	return nil, false
    94  }
    95  
    96  // Matches an expression that can be evaluated statically. Consumes 3 nodes
    97  // (Primary, Indexing and Compound).
    98  type simpleExprMatcher struct {
    99  	ev       PureEvaler
   100  	s        string
   101  	compound *parse.Compound
   102  	quote    parse.PrimaryType
   103  }
   104  
   105  func simpleExpr(ev PureEvaler) *simpleExprMatcher {
   106  	return &simpleExprMatcher{ev: ev}
   107  }
   108  
   109  func (m *simpleExprMatcher) matchNodes(ns []parse.Node) ([]parse.Node, bool) {
   110  	if len(ns) < 3 {
   111  		return nil, false
   112  	}
   113  	primary, ok := ns[0].(*parse.Primary)
   114  	if !ok {
   115  		return nil, false
   116  	}
   117  	indexing, ok := ns[1].(*parse.Indexing)
   118  	if !ok {
   119  		return nil, false
   120  	}
   121  	compound, ok := ns[2].(*parse.Compound)
   122  	if !ok {
   123  		return nil, false
   124  	}
   125  	s, ok := m.ev.PurelyEvalPartialCompound(compound, indexing.To)
   126  	if !ok {
   127  		return nil, false
   128  	}
   129  	m.compound, m.quote, m.s = compound, primary.Type, s
   130  	return ns[3:], true
   131  }