diff --git a/edit/completers.go b/edit/completers.go index e4e82eeb..2166f480 100644 --- a/edit/completers.go +++ b/edit/completers.go @@ -36,6 +36,11 @@ func complVariable(n parse.Node, ed *Editor) []*candidate { // Collect matching variables. var varnames []string + for varname := range ed.evaler.Builtin() { + if strings.HasPrefix(varname, head) { + varnames = append(varnames, varname) + } + } for varname := range ed.evaler.Global() { if strings.HasPrefix(varname, head) { varnames = append(varnames, varname) @@ -104,6 +109,11 @@ func complFormHeadInner(head string, ed *Editor) []*candidate { for special := range isBuiltinSpecial { foundCommand(special) } + for variable := range ed.evaler.Builtin() { + if strings.HasPrefix(variable, eval.FnPrefix) { + foundCommand(variable[len(eval.FnPrefix):]) + } + } for variable := range ed.evaler.Global() { if strings.HasPrefix(variable, eval.FnPrefix) { foundCommand(variable[len(eval.FnPrefix):]) diff --git a/edit/stylists.go b/edit/stylists.go index 80811d27..fd3daa58 100644 --- a/edit/stylists.go +++ b/edit/stylists.go @@ -34,8 +34,9 @@ func goodFormHead(head string, ed *Editor) bool { // XXX don't stat twice return util.IsExecutable(head) || isDir(head) } else { - return ed.evaler.Global()[eval.FnPrefix+head] != nil || - ed.isExternal[head] + return ed.isExternal[head] || + ed.evaler.Builtin()[eval.FnPrefix+head] != nil || + ed.evaler.Global()[eval.FnPrefix+head] != nil } } diff --git a/eval/eval.go b/eval/eval.go index dc8e4251..32cfc8fc 100644 --- a/eval/eval.go +++ b/eval/eval.go @@ -32,6 +32,7 @@ type Namespace map[string]Variable // Evaler is used to evaluate elvish sources. It maintains runtime context // shared among all evalCtx instances. type Evaler struct { + builtin Namespace global Namespace modules map[string]Namespace store *store.Store @@ -57,11 +58,9 @@ func (ec *EvalCtx) evaling(n parse.Node) { // NewEvaler creates a new Evaler. func NewEvaler(st *store.Store) *Evaler { - ev := &Evaler{nil, map[string]Namespace{}, st, nil, nil} - // Construct initial global namespace pid := String(strconv.Itoa(syscall.Getpid())) - ev.global = Namespace{ + builtin := Namespace{ "pid": NewRoVariable(pid), "ok": NewRoVariable(OK), "true": NewRoVariable(Bool(true)), @@ -70,14 +69,14 @@ func NewEvaler(st *store.Store) *Evaler { "pwd": PwdVariable{}, } for _, b := range builtinFns { - ev.global[FnPrefix+b.Name] = NewRoVariable(b) + builtin[FnPrefix+b.Name] = NewRoVariable(b) } - return ev + return &Evaler{builtin, Namespace{}, map[string]Namespace{}, st, nil, nil} } func (e *Evaler) searchPaths() []string { - return e.global["paths"].(*EnvPathList).get() + return e.builtin["paths"].(*EnvPathList).get() } func (e *Evaler) AddModule(name string, ns Namespace) { @@ -326,8 +325,13 @@ func (ev *Evaler) Source(fname string) error { return ev.SourceText(src) } +// Builtin returns the builtin namespace. +func (ev *Evaler) Builtin() Namespace { + return map[string]Variable(ev.builtin) +} + // Global returns the global namespace. -func (ev *Evaler) Global() map[string]Variable { +func (ev *Evaler) Global() Namespace { return map[string]Variable(ev.global) } @@ -339,11 +343,16 @@ func (ec *EvalCtx) ResolveVar(ns, name string) Variable { return ec.local[name] case "up": return ec.up[name] + case "builtin": + return ec.builtin[name] case "": if v, ok := ec.local[name]; ok { return v } - return ec.up[name] + if v, ok := ec.up[name]; ok { + return v + } + return ec.builtin[name] case "env", "external", "e", "E": if strings.HasPrefix(name, FnPrefix) { return NewRoVariable(ExternalCmd{name[len(FnPrefix):]}) diff --git a/eval/eval_test.go b/eval/eval_test.go index 9c9a26da..0978eebd 100644 --- a/eval/eval_test.go +++ b/eval/eval_test.go @@ -15,8 +15,8 @@ import ( func TestNewEvaler(t *testing.T) { ev := NewEvaler(nil) pid := strconv.Itoa(syscall.Getpid()) - if ToString(ev.global["pid"].Get()) != pid { - t.Errorf(`ev.global["pid"] = %v, want %v`, ev.global["pid"], pid) + if ToString(ev.builtin["pid"].Get()) != pid { + t.Errorf(`ev.builtin["pid"] = %v, want %v`, ev.builtin["pid"], pid) } }