elvish/edit/module.go

132 lines
2.8 KiB
Go
Raw Normal View History

2016-02-12 09:38:42 +08:00
package edit
import (
"errors"
"fmt"
2016-02-12 09:38:42 +08:00
"github.com/elves/elvish/eval"
"github.com/elves/elvish/util"
2016-02-12 09:38:42 +08:00
)
// Exposing editor functionalities as an elvish module.
2016-02-12 20:31:24 +08:00
// Errors thrown to Evaler.
2016-02-12 09:38:42 +08:00
var (
ErrTakeNoArg = errors.New("editor builtins take no arguments")
ErrEditorInactive = errors.New("editor inactive")
)
2016-02-12 20:31:24 +08:00
// makeModule builds a module from an Editor.
2016-02-12 09:38:42 +08:00
func makeModule(ed *Editor) eval.Namespace {
ns := eval.Namespace{}
// Populate builtins.
2016-02-12 10:20:10 +08:00
for _, b := range builtins {
ns[eval.FnPrefix+b.name] = eval.NewPtrVariable(&EditBuiltin{b, ed})
2016-02-12 09:38:42 +08:00
}
// 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)
2016-02-12 09:38:42 +08:00
return ns
}
// BindingTable adapts a binding table to eval.IndexSetter.
type BindingTable struct {
inner map[Key]Caller
}
func (BindingTable) Kind() string {
return "map"
}
func (bt BindingTable) Repr() string {
var builder eval.MapReprBuilder
for k, v := range bt.inner {
builder.WritePair(k.String(), v.Repr())
}
return builder.String()
}
2016-02-16 01:34:31 +08:00
func (bt BindingTable) IndexOne(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")
}
func (bt BindingTable) IndexSet(idx, v eval.Value) {
key := keyIndex(idx)
var f Caller
switch v := v.(type) {
case eval.String:
builtin, ok := builtinMap[string(v)]
if !ok {
throw(fmt.Errorf("no builtin named %s", v.Repr()))
}
f = builtin
case eval.CallerValue:
f = EvalCaller{v}
default:
throw(fmt.Errorf("bad function type %s", v.Kind()))
}
bt.inner[key] = f
}
func keyIndex(idx eval.Value) Key {
skey, ok := idx.(eval.String)
if !ok {
throw(errKeyMustBeString)
}
key, err := parseKey(string(skey))
if err != nil {
throw(err)
}
return key
}
2016-02-12 20:31:24 +08:00
// EditBuiltin adapts a Builtin to satisfy eval.Value and eval.Caller.
2016-02-12 09:38:42 +08:00
type EditBuiltin struct {
2016-02-12 10:20:10 +08:00
b Builtin
ed *Editor
2016-02-12 09:38:42 +08:00
}
func (*EditBuiltin) Kind() string {
return "fn"
2016-02-12 09:38:42 +08:00
}
func (eb *EditBuiltin) Repr() string {
2016-02-12 10:20:10 +08:00
return "<editor builtin " + eb.b.name + ">"
2016-02-12 09:38:42 +08:00
}
func (eb *EditBuiltin) Call(ec *eval.EvalCtx, args []eval.Value) {
if len(args) > 0 {
throw(ErrTakeNoArg)
}
if !eb.ed.active {
throw(ErrEditorInactive)
}
2016-02-12 10:20:10 +08:00
eb.b.impl(eb.ed)
2016-02-12 09:38:42 +08:00
}
func throw(e error) {
util.Throw(e)
2016-02-12 09:38:42 +08:00
}