github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/cmds/elvish/edit/completion/matcher.go (about)

     1  package completion
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  
     7  	"github.com/u-root/u-root/cmds/elvish/eval"
     8  	"github.com/u-root/u-root/cmds/elvish/eval/vals"
     9  	"github.com/u-root/u-root/cmds/elvish/util"
    10  	"github.com/u-root/u-root/cmds/elvish/hashmap"
    11  )
    12  
    13  var (
    14  	errIncorrectNumOfResults    = errors.New("matcher must return a bool for each candidate")
    15  	errMatcherMustBeFn          = errors.New("matcher must be a function")
    16  	errMatcherInputMustBeString = errors.New("matcher input must be string")
    17  )
    18  
    19  var (
    20  	matchPrefix = eval.NewBuiltinFn(
    21  		"edit:match-prefix", wrapMatcher(strings.HasPrefix))
    22  	matchSubstr = eval.NewBuiltinFn(
    23  		"edit:match-substr", wrapMatcher(strings.Contains))
    24  	matchSubseq = eval.NewBuiltinFn(
    25  		"edit:match-subseq", wrapMatcher(util.HasSubseq))
    26  )
    27  
    28  func lookupMatcher(m hashmap.Map, name string) (eval.Callable, bool) {
    29  	key := name
    30  	if !hashmap.HasKey(m, key) {
    31  		// Use fallback matcher
    32  		if !hashmap.HasKey(m, "") {
    33  			return nil, false
    34  		}
    35  		key = ""
    36  	}
    37  	value, _ := m.Index(key)
    38  	matcher, ok := value.(eval.Callable)
    39  	return matcher, ok
    40  }
    41  
    42  func wrapMatcher(matcher func(s, p string) bool) interface{} {
    43  	return func(fm *eval.Frame,
    44  		opts eval.RawOptions, pattern string, inputs eval.Inputs) {
    45  
    46  		var options struct {
    47  			IgnoreCase bool
    48  			SmartCase  bool
    49  		}
    50  		opts.Scan(&options)
    51  		switch {
    52  		case options.IgnoreCase && options.SmartCase:
    53  			throwf("-ignore-case and -smart-case cannot be used together")
    54  		case options.IgnoreCase:
    55  			innerMatcher := matcher
    56  			matcher = func(s, p string) bool {
    57  				return innerMatcher(strings.ToLower(s), strings.ToLower(p))
    58  			}
    59  		case options.SmartCase:
    60  			innerMatcher := matcher
    61  			matcher = func(s, p string) bool {
    62  				if p == strings.ToLower(p) {
    63  					// Ignore case is pattern is all lower case.
    64  					return innerMatcher(strings.ToLower(s), p)
    65  				} else {
    66  					return innerMatcher(s, p)
    67  				}
    68  			}
    69  		}
    70  
    71  		out := fm.OutputChan()
    72  		inputs(func(v interface{}) {
    73  			s, ok := v.(string)
    74  			if !ok {
    75  				throw(errMatcherInputMustBeString)
    76  			}
    77  			out <- vals.Bool(matcher(s, pattern))
    78  		})
    79  	}
    80  }