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 }