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

     1  package completion
     2  
     3  import (
     4  	"strings"
     5  	"unicode/utf8"
     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/getopt"
    10  	"github.com/u-root/u-root/cmds/elvish/parse"
    11  	"github.com/u-root/u-root/cmds/elvish/hashmap"
    12  )
    13  
    14  func complGetopt(fm *eval.Frame, elemsv, optsv, argsv interface{}) {
    15  	var (
    16  		elems    []string
    17  		opts     []*getopt.Option
    18  		args     []eval.Callable
    19  		variadic bool
    20  	)
    21  	desc := make(map[*getopt.Option]string)
    22  	// Convert arguments.
    23  	err := vals.Iterate(elemsv, func(v interface{}) bool {
    24  		elem, ok := v.(string)
    25  		if !ok {
    26  			throwf("arg should be string, got %s", vals.Kind(v))
    27  		}
    28  		elems = append(elems, elem)
    29  		return true
    30  	})
    31  	maybeThrow(err)
    32  	err = vals.Iterate(optsv, func(v interface{}) bool {
    33  		m, ok := v.(hashmap.Map)
    34  		if !ok {
    35  			throwf("opt should be map, got %s", vals.Kind(v))
    36  		}
    37  		get := func(k string) (string, bool) {
    38  			v, ok := m.Index(k)
    39  			if !ok {
    40  				return "", false
    41  			}
    42  			if vs, ok := v.(string); ok {
    43  				return vs, true
    44  			}
    45  			throwf("%s should be string, got %s", k, vals.Kind(v))
    46  			panic("unreachable")
    47  		}
    48  
    49  		opt := &getopt.Option{}
    50  		if s, ok := get("short"); ok {
    51  			r, size := utf8.DecodeRuneInString(s)
    52  			if r == utf8.RuneError || size != len(s) {
    53  				throwf("short option should be exactly one rune, got %v", parse.Quote(s))
    54  			}
    55  			opt.Short = r
    56  		}
    57  		if s, ok := get("long"); ok {
    58  			opt.Long = s
    59  		}
    60  		if opt.Short == 0 && opt.Long == "" {
    61  			throwf("opt should have at least one of short and long forms")
    62  		}
    63  		if s, ok := get("desc"); ok {
    64  			desc[opt] = s
    65  		}
    66  		opts = append(opts, opt)
    67  		return true
    68  	})
    69  	maybeThrow(err)
    70  	err = vals.Iterate(argsv, func(v interface{}) bool {
    71  		sv, ok := v.(string)
    72  		if ok {
    73  			if sv == "..." {
    74  				variadic = true
    75  				return true
    76  			}
    77  			throwf("string except for ... not allowed as argument handler, got %s", parse.Quote(sv))
    78  		}
    79  		arg, ok := v.(eval.Callable)
    80  		if !ok {
    81  			throwf("argument handler should be fn, got %s", vals.Kind(v))
    82  		}
    83  		args = append(args, arg)
    84  		return true
    85  	})
    86  	maybeThrow(err)
    87  
    88  	// TODO Configurable config
    89  	g := getopt.Getopt{opts, getopt.GNUGetoptLong}
    90  	_, parsedArgs, ctx := g.Parse(elems)
    91  	out := fm.OutputChan()
    92  
    93  	putShortOpt := func(opt *getopt.Option) {
    94  		c := &complexCandidate{stem: "-" + string(opt.Short)}
    95  		if d, ok := desc[opt]; ok {
    96  			c.displaySuffix = " (" + d + ")"
    97  		}
    98  		out <- c
    99  	}
   100  	putLongOpt := func(opt *getopt.Option) {
   101  		c := &complexCandidate{stem: "--" + opt.Long}
   102  		if d, ok := desc[opt]; ok {
   103  			c.displaySuffix = " (" + d + ")"
   104  		}
   105  		out <- c
   106  	}
   107  
   108  	switch ctx.Type {
   109  	case getopt.NewOptionOrArgument, getopt.Argument:
   110  		// Find argument completer
   111  		var argCompl eval.Callable
   112  		if len(parsedArgs) < len(args) {
   113  			argCompl = args[len(parsedArgs)]
   114  		} else if variadic {
   115  			argCompl = args[len(args)-1]
   116  		}
   117  		if argCompl != nil {
   118  			rawCands := make(chan rawCandidate)
   119  			defer close(rawCands)
   120  			go func() {
   121  				for rc := range rawCands {
   122  					out <- rc
   123  				}
   124  			}()
   125  			err := callArgCompleter(argCompl, fm.Evaler, []string{ctx.Text}, rawCands)
   126  			maybeThrow(err)
   127  		}
   128  		// TODO Notify that there is no suitable argument completer
   129  	case getopt.NewOption:
   130  		for _, opt := range opts {
   131  			if opt.Short != 0 {
   132  				putShortOpt(opt)
   133  			}
   134  			if opt.Long != "" {
   135  				putLongOpt(opt)
   136  			}
   137  		}
   138  	case getopt.NewLongOption:
   139  		for _, opt := range opts {
   140  			if opt.Long != "" {
   141  				putLongOpt(opt)
   142  			}
   143  		}
   144  	case getopt.LongOption:
   145  		for _, opt := range opts {
   146  			if strings.HasPrefix(opt.Long, ctx.Text) {
   147  				putLongOpt(opt)
   148  			}
   149  		}
   150  	case getopt.ChainShortOption:
   151  		for _, opt := range opts {
   152  			if opt.Short != 0 {
   153  				// XXX loses chained options
   154  				putShortOpt(opt)
   155  			}
   156  		}
   157  	case getopt.OptionArgument:
   158  	}
   159  }