예제 #1
0
파일: compl-getopt.go 프로젝트: zhsj/elvish
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:
	}
}
예제 #2
0
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:
	}
}