From d2d119af7f2881bb96414172647c5be6a57aec54 Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Sun, 21 Feb 2016 14:32:13 +0100 Subject: [PATCH] Move functionality of edit/search.go to util. --- edit/completers.go | 3 ++- edit/stylists.go | 5 ++-- eval/externalcmd.go | 4 ++- eval/search.go | 49 +++++----------------------------- util/search.go | 64 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 47 deletions(-) create mode 100644 util/search.go diff --git a/edit/completers.go b/edit/completers.go index 45206eb9..e4e82eeb 100644 --- a/edit/completers.go +++ b/edit/completers.go @@ -9,6 +9,7 @@ import ( "github.com/elves/elvish/eval" "github.com/elves/elvish/parse" + "github.com/elves/elvish/util" ) // A completer takes the current node @@ -86,7 +87,7 @@ func complFormHead(cn *parse.Compound, head string, ed *Editor) []*candidate { } func complFormHeadInner(head string, ed *Editor) []*candidate { - if eval.DontSearch(head) { + if util.DontSearch(head) { return complArgInner(head, ed, true) } diff --git a/edit/stylists.go b/edit/stylists.go index 79215486..80811d27 100644 --- a/edit/stylists.go +++ b/edit/stylists.go @@ -3,6 +3,7 @@ package edit import ( "github.com/elves/elvish/eval" "github.com/elves/elvish/parse" + "github.com/elves/elvish/util" ) // stylist takes a Node and Editor, and returns a style string. The Node is @@ -29,9 +30,9 @@ func colorFormHead(n parse.Node, ed *Editor) string { func goodFormHead(head string, ed *Editor) bool { if isBuiltinSpecial[head] { return true - } else if eval.DontSearch(head) { + } else if util.DontSearch(head) { // XXX don't stat twice - return eval.IsExecutable(head) || isDir(head) + return util.IsExecutable(head) || isDir(head) } else { return ed.evaler.Global()[eval.FnPrefix+head] != nil || ed.isExternal[head] diff --git a/eval/externalcmd.go b/eval/externalcmd.go index fce0589b..d20c5ae6 100644 --- a/eval/externalcmd.go +++ b/eval/externalcmd.go @@ -5,6 +5,8 @@ import ( "fmt" "os" "syscall" + + "github.com/elves/elvish/util" ) // FdNil is a special impossible fd value used for "close fd" in @@ -28,7 +30,7 @@ func (e ExternalCmd) Repr(int) string { // Call calls an external command. func (e ExternalCmd) Call(ec *EvalCtx, argVals []Value) { - if DontSearch(e.Name) { + if util.DontSearch(e.Name) { stat, err := os.Stat(e.Name) if err == nil && stat.IsDir() { // implicit cd diff --git a/eval/search.go b/eval/search.go index d33b7850..5749a6e9 100644 --- a/eval/search.go +++ b/eval/search.go @@ -2,60 +2,23 @@ package eval import ( "fmt" - "io/ioutil" - "os" - "strings" "github.com/elves/elvish/parse" + "github.com/elves/elvish/util" ) // Search tries to resolve an external command and return the full (possibly // relative) path. func (ev *Evaler) Search(exe string) (string, error) { - if DontSearch(exe) { - if IsExecutable(exe) { - return exe, nil - } - return "", fmt.Errorf("external command %s not executable", parse.Quote(exe)) + path, err := util.Search(ev.searchPaths(), exe) + if err != nil { + return "", fmt.Errorf("search %s: %s", parse.Quote(exe), err.Error()) } - for _, p := range ev.searchPaths() { - full := p + "/" + exe - if IsExecutable(full) { - return full, nil - } - } - return "", fmt.Errorf("external command %s not found", parse.Quote(exe)) + return path, nil } // AllExecutables writes the names of all executable files in the search path // to a channel. func (ev *Evaler) AllExecutables(names chan<- string) { - for _, dir := range ev.searchPaths() { - // XXX Ignore error - infos, _ := ioutil.ReadDir(dir) - for _, info := range infos { - if !info.IsDir() && (info.Mode()&0111 != 0) { - names <- info.Name() - } - } - } -} - -// DontSearch determines whether the path to an external command should be -// taken literally and not searched. -func DontSearch(exe string) bool { - return exe == ".." || - strings.HasPrefix(exe, "/") || - strings.HasPrefix(exe, "./") || - strings.HasPrefix(exe, "../") -} - -// IsExecutable determines whether path refers to an executable file. -func IsExecutable(path string) bool { - fi, err := os.Stat(path) - if err != nil { - return false - } - fm := fi.Mode() - return !fm.IsDir() && (fm&0111 != 0) + util.AllExecutables(ev.searchPaths(), names) } diff --git a/util/search.go b/util/search.go new file mode 100644 index 00000000..f4f360bb --- /dev/null +++ b/util/search.go @@ -0,0 +1,64 @@ +package util + +import ( + "errors" + "io/ioutil" + "os" + "strings" +) + +var ( + ErrNotExecutable = errors.New("not executable") + ErrNotFound = errors.New("not found") +) + +// Search tries to resolve an external command and return the full (possibly +// relative) path. +func Search(paths []string, exe string) (string, error) { + if DontSearch(exe) { + if IsExecutable(exe) { + return exe, nil + } + return "", ErrNotExecutable + } + for _, p := range paths { + full := p + "/" + exe + if IsExecutable(full) { + return full, nil + } + } + return "", ErrNotFound +} + +// AllExecutables writes the names of all executable files in the search path +// to a channel. +func AllExecutables(paths []string, names chan<- string) { + for _, dir := range paths { + // XXX Ignore error + infos, _ := ioutil.ReadDir(dir) + for _, info := range infos { + if !info.IsDir() && (info.Mode()&0111 != 0) { + names <- info.Name() + } + } + } +} + +// DontSearch determines whether the path to an external command should be +// taken literally and not searched. +func DontSearch(exe string) bool { + return exe == ".." || + strings.HasPrefix(exe, "/") || + strings.HasPrefix(exe, "./") || + strings.HasPrefix(exe, "../") +} + +// IsExecutable determines whether path refers to an executable file. +func IsExecutable(path string) bool { + fi, err := os.Stat(path) + if err != nil { + return false + } + fm := fi.Mode() + return !fm.IsDir() && (fm&0111 != 0) +}