2016-01-30 23:47:11 +08:00
|
|
|
package edit
|
|
|
|
|
|
|
|
import (
|
2016-02-09 03:40:39 +08:00
|
|
|
"bufio"
|
2016-01-31 09:29:46 +08:00
|
|
|
"errors"
|
2016-01-30 23:47:11 +08:00
|
|
|
"fmt"
|
2016-02-09 03:40:39 +08:00
|
|
|
"os"
|
2016-01-30 23:47:11 +08:00
|
|
|
"strings"
|
2016-02-09 03:40:39 +08:00
|
|
|
"sync"
|
2016-01-30 23:47:11 +08:00
|
|
|
|
|
|
|
"github.com/elves/elvish/eval"
|
|
|
|
)
|
|
|
|
|
2016-02-09 08:21:38 +08:00
|
|
|
var keyBindings = map[bufferMode]map[Key]Caller{
|
|
|
|
modeInsert: map[Key]Caller{
|
2016-02-09 05:57:58 +08:00
|
|
|
DefaultBinding: builtin(defaultInsert),
|
|
|
|
// Moving.
|
2016-02-09 02:28:40 +08:00
|
|
|
Key{Left, 0}: builtin(moveDotLeft),
|
|
|
|
Key{Right, 0}: builtin(moveDotRight),
|
2016-02-09 05:57:58 +08:00
|
|
|
Key{Up, Alt}: builtin(moveDotUp),
|
|
|
|
Key{Down, Alt}: builtin(moveDotDown),
|
2016-02-09 02:28:40 +08:00
|
|
|
Key{Left, Ctrl}: builtin(moveDotLeftWord),
|
|
|
|
Key{Right, Ctrl}: builtin(moveDotRightWord),
|
|
|
|
Key{Home, 0}: builtin(moveDotSOL),
|
|
|
|
Key{End, 0}: builtin(moveDotEOL),
|
2016-02-09 05:57:58 +08:00
|
|
|
// Killing.
|
|
|
|
Key{'U', Ctrl}: builtin(killLineLeft),
|
|
|
|
Key{'K', Ctrl}: builtin(killLineRight),
|
|
|
|
Key{'W', Ctrl}: builtin(killWordLeft),
|
|
|
|
Key{Backspace, 0}: builtin(killRuneLeft),
|
|
|
|
// Some terminal send ^H on backspace
|
|
|
|
Key{'H', Ctrl}: builtin(killRuneLeft),
|
|
|
|
Key{Delete, 0}: builtin(killRuneRight),
|
|
|
|
// Inserting.
|
|
|
|
Key{'.', Alt}: builtin(insertLastWord),
|
|
|
|
Key{Enter, Alt}: builtin(insertKey),
|
|
|
|
// Controls.
|
|
|
|
Key{Enter, 0}: builtin(returnLine),
|
|
|
|
Key{'D', Ctrl}: builtin(returnEOF),
|
2016-02-11 06:57:28 +08:00
|
|
|
// Key{'[', Ctrl}: builtin(startCommand),
|
2016-02-09 05:57:58 +08:00
|
|
|
Key{Tab, 0}: builtin(completePrefixOrStartCompletion),
|
|
|
|
Key{Up, 0}: builtin(startHistory),
|
|
|
|
Key{'N', Ctrl}: builtin(startNavigation),
|
|
|
|
},
|
2016-02-09 08:21:38 +08:00
|
|
|
modeCommand: map[Key]Caller{
|
2016-02-09 05:57:58 +08:00
|
|
|
DefaultBinding: builtin(defaultCommand),
|
|
|
|
// Moving.
|
|
|
|
Key{'h', 0}: builtin(moveDotLeft),
|
|
|
|
Key{'l', 0}: builtin(moveDotRight),
|
|
|
|
Key{'k', 0}: builtin(moveDotUp),
|
|
|
|
Key{'j', 0}: builtin(moveDotDown),
|
|
|
|
Key{'b', 0}: builtin(moveDotLeftWord),
|
|
|
|
Key{'w', 0}: builtin(moveDotRightWord),
|
|
|
|
Key{'0', 0}: builtin(moveDotSOL),
|
|
|
|
Key{'$', 0}: builtin(moveDotEOL),
|
|
|
|
// Killing.
|
|
|
|
Key{'x', 0}: builtin(killRuneRight),
|
|
|
|
Key{'D', 0}: builtin(killLineRight),
|
|
|
|
// Controls.
|
|
|
|
Key{'i', 0}: builtin(startInsert),
|
2016-01-30 23:47:11 +08:00
|
|
|
},
|
2016-02-09 08:21:38 +08:00
|
|
|
modeCompletion: map[Key]Caller{
|
2016-02-09 02:28:40 +08:00
|
|
|
Key{'[', Ctrl}: builtin(cancelCompletion),
|
|
|
|
Key{Up, 0}: builtin(selectCandUp),
|
|
|
|
Key{Down, 0}: builtin(selectCandDown),
|
|
|
|
Key{Left, 0}: builtin(selectCandLeft),
|
|
|
|
Key{Right, 0}: builtin(selectCandRight),
|
|
|
|
Key{Tab, 0}: builtin(cycleCandRight),
|
|
|
|
DefaultBinding: builtin(defaultCompletion),
|
2016-01-30 23:47:11 +08:00
|
|
|
},
|
2016-02-09 08:21:38 +08:00
|
|
|
modeNavigation: map[Key]Caller{
|
2016-02-09 02:28:40 +08:00
|
|
|
Key{Up, 0}: builtin(selectNavUp),
|
|
|
|
Key{Down, 0}: builtin(selectNavDown),
|
|
|
|
Key{Left, 0}: builtin(ascendNav),
|
|
|
|
Key{Right, 0}: builtin(descendNav),
|
|
|
|
DefaultBinding: builtin(defaultNavigation),
|
2016-01-30 23:47:11 +08:00
|
|
|
},
|
2016-02-09 08:21:38 +08:00
|
|
|
modeHistory: map[Key]Caller{
|
2016-02-09 02:28:40 +08:00
|
|
|
Key{'[', Ctrl}: builtin(startInsert),
|
|
|
|
Key{Up, 0}: builtin(selectHistoryPrev),
|
|
|
|
Key{Down, 0}: builtin(selectHistoryNextOrQuit),
|
|
|
|
DefaultBinding: builtin(defaultHistory),
|
2016-01-30 23:47:11 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2016-02-08 06:23:16 +08:00
|
|
|
errInvalidKey = errors.New("invalid key to bind to")
|
|
|
|
errInvalidFunction = errors.New("invalid function to bind")
|
2016-01-30 23:47:11 +08:00
|
|
|
)
|
|
|
|
|
2016-02-09 08:21:38 +08:00
|
|
|
// EvalCaller adapts an eval.Caller to a Caller.
|
|
|
|
type EvalCaller struct {
|
2016-02-09 03:40:39 +08:00
|
|
|
eval.Caller
|
|
|
|
}
|
|
|
|
|
2016-02-09 08:21:38 +08:00
|
|
|
func (c EvalCaller) Call(ed *Editor) {
|
2016-02-09 03:40:39 +08:00
|
|
|
// Input
|
|
|
|
devnull, err := os.Open("/dev/null")
|
|
|
|
if err != nil {
|
|
|
|
Logger.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer devnull.Close()
|
|
|
|
in := make(chan eval.Value)
|
|
|
|
close(in)
|
|
|
|
|
|
|
|
// Output
|
|
|
|
rout, out, err := os.Pipe()
|
|
|
|
if err != nil {
|
|
|
|
Logger.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
chanOut := make(chan eval.Value)
|
|
|
|
|
|
|
|
// Goroutines to collect output.
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(2)
|
|
|
|
go func() {
|
|
|
|
rd := bufio.NewReader(rout)
|
|
|
|
for {
|
|
|
|
line, err := rd.ReadString('\n')
|
|
|
|
Logger.Println("function writes bytes", line)
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rout.Close()
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
for v := range chanOut {
|
|
|
|
Logger.Println("function writes Value", v.Repr())
|
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
|
|
|
|
ports := []*eval.Port{
|
|
|
|
{File: devnull, Chan: in},
|
|
|
|
{File: out, Chan: chanOut},
|
|
|
|
{File: out, Chan: chanOut},
|
|
|
|
}
|
|
|
|
// XXX There is no source to pass to NewTopEvalCtx.
|
|
|
|
ec := eval.NewTopEvalCtx(ed.evaler, "[editor]", "", ports)
|
|
|
|
ex := ec.PCall(c.Caller, []eval.Value{})
|
|
|
|
if ex != nil {
|
|
|
|
// XXX will disappear very quickly
|
|
|
|
ed.pushTip("function error: " + ex.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
out.Close()
|
|
|
|
close(chanOut)
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
2016-02-09 02:15:52 +08:00
|
|
|
// Bind binds a key to a editor builtin or shell function.
|
|
|
|
func (ed *Editor) Bind(key string, function eval.Value) error {
|
|
|
|
// TODO Modify the binding table in ed instead of a global data structure.
|
|
|
|
k, err := parseKey(key)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-02-09 08:21:38 +08:00
|
|
|
var f Caller
|
2016-02-09 03:40:39 +08:00
|
|
|
switch function := function.(type) {
|
|
|
|
case eval.String:
|
|
|
|
builtin, ok := builtins[string(function)]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("no builtin named %s", function.Repr())
|
|
|
|
}
|
|
|
|
f = builtin
|
|
|
|
case eval.Caller:
|
2016-02-09 08:21:38 +08:00
|
|
|
f = EvalCaller{function}
|
2016-02-09 03:40:39 +08:00
|
|
|
default:
|
2016-02-12 09:40:34 +08:00
|
|
|
return fmt.Errorf("bad function type %s", function.Kind())
|
2016-02-09 02:15:52 +08:00
|
|
|
}
|
|
|
|
|
2016-02-09 03:40:39 +08:00
|
|
|
keyBindings[modeInsert][k] = f
|
2016-02-09 02:15:52 +08:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-01-30 23:47:11 +08:00
|
|
|
var modifier = map[string]Mod{
|
|
|
|
"s": Shift, "shift": Shift,
|
|
|
|
"a": Alt, "alt": Alt,
|
|
|
|
"m": Alt, "meta": Alt,
|
|
|
|
"c": Ctrl, "ctrl": Ctrl,
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseKey(s string) (Key, error) {
|
|
|
|
var k Key
|
|
|
|
// parse modifiers
|
|
|
|
for {
|
|
|
|
i := strings.IndexAny(s, "+-")
|
|
|
|
if i == -1 {
|
|
|
|
break
|
|
|
|
}
|
2016-02-09 02:14:30 +08:00
|
|
|
modname := strings.ToLower(s[:i])
|
|
|
|
mod, ok := modifier[modname]
|
2016-01-30 23:47:11 +08:00
|
|
|
if !ok {
|
2016-02-09 02:14:30 +08:00
|
|
|
return Key{}, fmt.Errorf("bad modifier: %q", modname)
|
2016-01-30 23:47:11 +08:00
|
|
|
}
|
|
|
|
k.Mod |= mod
|
|
|
|
s = s[i+1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(s) == 1 {
|
|
|
|
k.Rune = rune(s[0])
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for r, name := range keyNames {
|
|
|
|
if s == name {
|
|
|
|
k.Rune = r
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, name := range functionKeyNames[1:] {
|
|
|
|
if s == name {
|
|
|
|
k.Rune = rune(-i - 1)
|
|
|
|
return k, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Key{}, fmt.Errorf("bad key: %q", s)
|
|
|
|
}
|