func complGetopt(ec *eval.EvalCtx, elemsv eval.IteratorValue, optsv eval.IteratorValue, argsv eval.IteratorValue) { var ( elems []string opts []*getopt.Option args []eval.FnValue variadic bool ) desc := make(map[*getopt.Option]string) // Convert arguments. elemsv.Iterate(func(v eval.Value) bool { elem, ok := v.(eval.String) if !ok { throwf("arg should be string, got %s", v.Kind()) } elems = append(elems, string(elem)) return true }) optsv.Iterate(func(v eval.Value) bool { m, ok := v.(eval.MapLike) if !ok { throwf("opt should be map-like, got %s", v.Kind()) } get := func(ks string) (string, bool) { kv := eval.String(ks) if !m.HasKey(kv) { return "", false } vv := m.IndexOne(kv) if vs, ok := vv.(eval.String); ok { return string(vs), true } else { throwf("%s should be string, got %s", ks, vs.Kind()) panic("unreachable") } } opt := &getopt.Option{} if s, ok := get("short"); ok { r, size := utf8.DecodeRuneInString(s) if r == utf8.RuneError || size != len(s) { throwf("short option should be exactly one rune, got %v", parse.Quote(s)) } opt.Short = r } if s, ok := get("long"); ok { opt.Long = s } if opt.Short == 0 && opt.Long == "" { throwf("opt should have at least one of short and long forms") } if s, ok := get("desc"); ok { desc[opt] = s } opts = append(opts, opt) return true }) argsv.Iterate(func(v eval.Value) bool { sv, ok := v.(eval.String) if ok { if string(sv) == "..." { variadic = true return true } throwf("string except for ... not allowed as argument handler, got %s", parse.Quote(string(sv))) } arg, ok := v.(eval.FnValue) if !ok { throwf("argument handler should be fn, got %s", v.Kind()) } args = append(args, arg) return true }) // TODO Configurable config g := getopt.Getopt{opts, getopt.GNUGetoptLong} _, parsedArgs, ctx := g.Parse(elems) out := ec.OutputChan() putShortOpt := func(opt *getopt.Option) { c := &candidate{text: "-" + string(opt.Short)} if d, ok := desc[opt]; ok { c.display.text = c.text + " (" + d + ")" } out <- c } putLongOpt := func(opt *getopt.Option) { c := &candidate{text: "--" + string(opt.Long)} if d, ok := desc[opt]; ok { c.display.text = c.text + " (" + d + ")" } out <- c } switch ctx.Type { case getopt.NewOptionOrArgument, getopt.Argument: // Find argument completer var argCompl eval.FnValue if len(parsedArgs) < len(args) { argCompl = args[len(parsedArgs)] } else if variadic { argCompl = args[len(args)-1] } if argCompl != nil { cands, err := callFnForCandidates(argCompl, ec.Evaler, []string{ctx.Text}) maybeThrow(err) for _, cand := range cands { out <- cand } } // TODO Notify that there is no suitable argument completer case getopt.NewOption: for _, opt := range opts { if opt.Short != 0 { putShortOpt(opt) } if opt.Long != "" { putLongOpt(opt) } } case getopt.NewLongOption: for _, opt := range opts { if opt.Long != "" { putLongOpt(opt) } } case getopt.LongOption: for _, opt := range opts { if strings.HasPrefix(opt.Long, ctx.Text) { putLongOpt(opt) } } case getopt.ChainShortOption: for _, opt := range opts { if opt.Short != 0 { // XXX loses chained options putShortOpt(opt) } } case getopt.OptionArgument: } }
func complGetopt(ec *eval.EvalCtx, elemsv eval.IteratorValue, optsv eval.IteratorValue, argsv eval.IteratorValue) { var ( elems []string opts []*getopt.Option args []eval.FnValue variadic bool ) // Convert arguments. elemsv.Iterate(func(v eval.Value) bool { elem, ok := v.(eval.String) if !ok { throwf("arg should be string, got %s", v.Kind()) } elems = append(elems, string(elem)) return true }) optsv.Iterate(func(v eval.Value) bool { m, ok := v.(eval.MapLike) if !ok { throwf("opt should be map-like, got %s", v.Kind()) } opt := &getopt.Option{} vshort := maybeIndex(m, eval.String("short")) if vshort != nil { sv, ok := vshort.(eval.String) if !ok { throwf("short option should be string, got %s", vshort.Kind()) } s := string(sv) r, size := utf8.DecodeRuneInString(s) if r == utf8.RuneError || size != len(s) { throwf("short option should be exactly one rune, got %v", parse.Quote(s)) } opt.Short = r } vlong := maybeIndex(m, eval.String("long")) if vlong != nil { s, ok := vlong.(eval.String) if !ok { throwf("long option should be string, got %s", vlong.Kind()) } opt.Long = string(s) } if vshort == nil && vlong == nil { throwf("opt should have at least one of short and long as keys") } // TODO support &desc opts = append(opts, opt) return true }) argsv.Iterate(func(v eval.Value) bool { sv, ok := v.(eval.String) if ok { if string(sv) == "..." { variadic = true return true } throwf("string except for ... not allowed as argument handler, got %s", parse.Quote(string(sv))) } arg, ok := v.(eval.FnValue) if !ok { throwf("argument handler should be fn, got %s", v.Kind()) } args = append(args, arg) return true }) // TODO Configurable config g := getopt.Getopt{opts, getopt.GNUGetoptLong} _, _, ctx := g.Parse(elems) out := ec.OutputChan() _ = variadic // XXX switch ctx.Type { case getopt.NewOptionOrArgument, getopt.Argument: case getopt.NewOption: for _, opt := range opts { if opt.Short != 0 { out <- eval.String("-" + string(opt.Short)) } if opt.Long != "" { out <- eval.String("--" + opt.Long) } } case getopt.NewLongOption: for _, opt := range opts { if opt.Long != "" { out <- eval.String("--" + opt.Long) } } case getopt.LongOption: for _, opt := range opts { if strings.HasPrefix(opt.Long, ctx.Text) { out <- eval.String("--" + opt.Long) } } case getopt.ChainShortOption: for _, opt := range opts { if opt.Short != 0 { // XXX loses chained options out <- eval.String("-" + string(opt.Short)) } } case getopt.OptionArgument: } }