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 }