Exemplo n.º 1
0
func (fac FnAsArgCompleter) Complete(words []string, ed *Editor) ([]*candidate, error) {
	in, err := makeClosedStdin()
	if err != nil {
		return nil, err
	}
	ports := []*eval.Port{in, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}}

	wordValues := make([]eval.Value, len(words))
	for i, word := range words {
		wordValues[i] = eval.String(word)
	}

	// XXX There is no source to pass to NewTopEvalCtx.
	ec := eval.NewTopEvalCtx(ed.evaler, "[editor completer]", "", ports)
	values, err := ec.PCaptureOutput(fac.Fn, wordValues)
	if err != nil {
		ed.notify("completer error: %v", err)
		return nil, err
	}

	cands := make([]*candidate, len(values))
	for i, v := range values {
		s := eval.ToString(v)
		cands[i] = &candidate{text: s}
	}
	return cands, nil
}
Exemplo n.º 2
0
Arquivo: call.go Projeto: zhsj/elvish
func callFnForCandidates(fn eval.FnValue, ev *eval.Evaler, args []string) ([]*candidate, error) {
	ports := []*eval.Port{eval.DevNullClosedChan, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}}

	argValues := make([]eval.Value, len(args))
	for i, arg := range args {
		argValues[i] = eval.String(arg)
	}

	// XXX There is no source to pass to NewTopEvalCtx.
	ec := eval.NewTopEvalCtx(ev, "[editor completer]", "", ports)
	values, err := ec.PCaptureOutput(fn, argValues, eval.NoOpts)
	if err != nil {
		return nil, errors.New("completer error: " + err.Error())
	}

	cands := make([]*candidate, len(values))
	for i, v := range values {
		switch v := v.(type) {
		case eval.String:
			cands[i] = &candidate{text: string(v)}
		case *candidate:
			cands[i] = v
		default:
			return nil, errors.New("completer must output string or candidate")
		}
	}
	return cands, nil
}
Exemplo n.º 3
0
func (hv History) IndexOne(idx eval.Value) eval.Value {
	hv.mutex.RLock()
	defer hv.mutex.RUnlock()

	slice, i, j := eval.ParseAndFixListIndex(eval.ToString(idx), hv.Len())
	if slice {
		cmds, err := hv.st.Cmds(i+1, j+1)
		maybeThrow(err)
		vs := make([]eval.Value, len(cmds))
		for i := range cmds {
			vs[i] = eval.String(cmds[i])
		}
		return eval.NewList(vs...)
	}
	s, err := hv.st.Cmd(i + 1)
	maybeThrow(err)
	return eval.String(s)
}
Exemplo n.º 4
0
func complFilenameFn(ec *eval.EvalCtx, word string) {
	cands, err := complFilenameInner(word, false)
	maybeThrow(err)
	out := ec.OutputChan()
	for _, cand := range cands {
		// TODO Preserve other parts of the candidate.
		out <- eval.String(cand.text)
	}
}
Exemplo n.º 5
0
// makeModule builds a module from an Editor.
func makeModule(ed *Editor) eval.Namespace {
	ns := eval.Namespace{}
	// Populate builtins.
	for _, b := range builtins {
		ns[eval.FnPrefix+b.name] = eval.NewPtrVariable(&EditBuiltin{b, ed})
	}
	// Populate binding tables in the variable $binding.
	// TODO Make binding specific to the Editor.
	binding := map[eval.Value]eval.Value{
		eval.String("insert"):     BindingTable{keyBindings[modeInsert]},
		eval.String("command"):    BindingTable{keyBindings[modeCommand]},
		eval.String("completion"): BindingTable{keyBindings[modeCompletion]},
		eval.String("navigation"): BindingTable{keyBindings[modeNavigation]},
		eval.String("history"):    BindingTable{keyBindings[modeHistory]},
	}
	ns["binding"] = eval.NewPtrVariable(eval.NewMap(binding))

	return ns
}
Exemplo n.º 6
0
func (hv History) Iterate(f func(eval.Value) bool) {
	hv.mutex.RLock()
	defer hv.mutex.RUnlock()

	n := hv.Len()
	err := hv.st.IterateCmds(1, n+1, func(cmd string) bool {
		return f(eval.String(cmd))
	})
	maybeThrow(err)
}
Exemplo n.º 7
0
func (bt BindingTable) Index(idx eval.Value) eval.Value {
	key := keyIndex(idx)
	switch f := bt.inner[key].(type) {
	case Builtin:
		return eval.String(f.name)
	case EvalCaller:
		return f.Caller
	}
	throw(errors.New("bug"))
	panic("unreachable")
}
Exemplo n.º 8
0
func (ct CompleterTable) IndexOne(idx eval.Value) eval.Value {
	head, ok := idx.(eval.String)
	if !ok {
		throw(ErrCompleterIndexMustBeString)
	}
	v := ct[string(head)]
	if fac, ok := v.(FnAsArgCompleter); ok {
		return fac.Fn
	}
	return eval.String("<get not implemented yet>")
}
Exemplo n.º 9
0
// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(addError func(error)) {
	ed.activeMutex.Lock()
	defer ed.activeMutex.Unlock()
	ed.active = false

	ed.mode = &ed.insert
	ed.tips = nil
	ed.dot = len(ed.line)
	if !ed.rpromptPersistent {
		ed.rprompt = nil
	}
	addError(ed.refresh(false, false))
	ed.file.WriteString("\n")

	// ed.reader.Stop()
	ed.reader.Quit()

	// Turn on autowrap.
	ed.file.WriteString("\033[?7h")
	// Turn off mouse tracking.
	//ed.file.WriteString("\033[?1000;1006l")

	// Disable bracketed paste.
	ed.file.WriteString("\033[?2004l")

	// restore termios
	err := ed.savedTermios.ApplyToFd(int(ed.file.Fd()))

	if err != nil {
		addError(fmt.Errorf("can't restore terminal attribute: %s", err))
	}
	ed.savedTermios = nil
	line := ed.line
	ed.editorState = editorState{}

	afterReadLine := ed.afterReadLine.Get().(eval.ListLike)
	if afterReadLine.Len() > 0 {
		opfunc := func(ec *eval.EvalCtx) {
			afterReadLine.Iterate(func(v eval.Value) bool {
				fn := v.(eval.FnValue)
				ex := ec.PCall(fn, []eval.Value{eval.String(line)}, eval.NoOpts)
				// TODO Pretty print.
				if ex != nil {
					fmt.Fprintln(os.Stderr, "function error: %s", ex.Error())
				}
				return true
			})
		}
		ed.evaler.Eval(eval.Op{opfunc, -1, -1}, "[after-readline]", "no source")
	}
}
Exemplo n.º 10
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
	)
	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:
	}
}
Exemplo n.º 11
0
// makeModule builds a module from an Editor.
func makeModule(ed *Editor) eval.Namespace {
	ns := eval.Namespace{}
	// Populate builtins.
	for _, b := range builtins {
		ns[eval.FnPrefix+b.name] = eval.NewPtrVariable(b)
	}
	// Populate binding tables in the variable $binding.
	// TODO Make binding specific to the Editor.
	binding := &eval.Struct{
		[]string{"insert", "command", "completion", "navigation", "history"},
		[]eval.Variable{
			eval.NewRoVariable(BindingTable{keyBindings[modeInsert]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeCommand]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeCompletion]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeNavigation]}),
			eval.NewRoVariable(BindingTable{keyBindings[modeHistory]}),
		},
	}

	ns["binding"] = eval.NewRoVariable(binding)
	ns["completer"] = eval.NewRoVariable(CompleterTable(argCompleter))
	ns[eval.FnPrefix+"complete-getopt"] = eval.NewRoVariable(
		// XXX Repr is "&le:complete-getopt" instead of "le:&complete-getopt"
		&eval.BuiltinFn{"le:complete-getopt", eval.WrapFn(complGetopt)})
	ns[eval.FnPrefix+"complete-files"] = eval.NewRoVariable(
		&eval.BuiltinFn{"le:complete-filename", eval.WrapFn(complFilenameFn)})

	ns["prompt"] = ed.ps1
	ns["rprompt"] = ed.rps1
	ns["rprompt-persistent"] = BoolExposer{&ed.rpromptPersistent}
	ns["history"] = eval.NewRoVariable(History{&ed.historyMutex, ed.store})

	ns["current-command"] = eval.MakeVariableFromCallback(
		func(v eval.Value) {
			if !ed.active {
				throw(ErrEditorInactive)
			}
			if s, ok := v.(eval.String); ok {
				ed.line = string(s)
				ed.dot = len(ed.line)
			} else {
				throw(errMustBeString)
			}
		},
		func() eval.Value { return eval.String(ed.line) },
	)
	ns["selected-file"] = eval.MakeRoVariableFromCallback(
		func() eval.Value {
			if !ed.active {
				throw(ErrEditorInactive)
			}
			if ed.mode.Mode() != modeNavigation {
				throw(errNotNav)
			}
			return eval.String(ed.navigation.current.selectedName())
		},
	)

	ns["abbr"] = eval.NewRoVariable(eval.MapStringString(ed.abbreviations))

	ns["before-readline"] = ed.beforeReadLine
	ns["after-readline"] = ed.afterReadLine

	ns[eval.FnPrefix+"styled"] = eval.NewRoVariable(&eval.BuiltinFn{"le:styled", eval.WrapFn(styledBuiltin)})

	return ns
}
Exemplo n.º 12
0
func (se StringExposer) Get() eval.Value {
	return eval.String(*se.valuePtr)
}
Exemplo n.º 13
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:
	}
}