github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/edit/highlight.go (about) 1 package edit 2 3 import ( 4 "os" 5 "os/exec" 6 7 "src.elv.sh/pkg/cli" 8 "src.elv.sh/pkg/edit/highlight" 9 "src.elv.sh/pkg/eval" 10 "src.elv.sh/pkg/fsutil" 11 "src.elv.sh/pkg/parse" 12 ) 13 14 func initHighlighter(appSpec *cli.AppSpec, ev *eval.Evaler) { 15 appSpec.Highlighter = highlight.NewHighlighter(highlight.Config{ 16 Check: func(tree parse.Tree) error { return check(ev, tree) }, 17 HasCommand: func(cmd string) bool { return hasCommand(ev, cmd) }, 18 }) 19 } 20 21 func check(ev *eval.Evaler, tree parse.Tree) error { 22 err := ev.CheckTree(tree, nil) 23 if err == nil { 24 return nil 25 } 26 return err 27 } 28 29 func hasCommand(ev *eval.Evaler, cmd string) bool { 30 if eval.IsBuiltinSpecial[cmd] { 31 return true 32 } 33 if fsutil.DontSearch(cmd) { 34 return isDirOrExecutable(cmd) || hasExternalCommand(cmd) 35 } 36 37 sigil, qname := eval.SplitSigil(cmd) 38 if sigil != "" { 39 // The @ sign is only valid when referring to external commands. 40 return hasExternalCommand(cmd) 41 } 42 43 first, rest := eval.SplitQName(qname) 44 switch { 45 case rest == "": 46 // Unqualified name; try builtin and global. 47 if hasFn(ev.Builtin(), first) || hasFn(ev.Global(), first) { 48 return true 49 } 50 case first == "e:": 51 return hasExternalCommand(rest) 52 default: 53 // Qualified name. Find the top-level module first. 54 if hasQualifiedFn(ev, first, rest) { 55 return true 56 } 57 } 58 59 // If all failed, it can still be an external command. 60 return hasExternalCommand(cmd) 61 } 62 63 func hasQualifiedFn(ev *eval.Evaler, firstNs string, rest string) bool { 64 if rest == "" { 65 return false 66 } 67 modVal, ok := ev.Global().Index(firstNs) 68 if !ok { 69 modVal, ok = ev.Builtin().Index(firstNs) 70 if !ok { 71 return false 72 } 73 } 74 mod, ok := modVal.(*eval.Ns) 75 if !ok { 76 return false 77 } 78 segs := eval.SplitQNameSegs(rest) 79 for _, seg := range segs[:len(segs)-1] { 80 modVal, ok = mod.Index(seg) 81 if !ok { 82 return false 83 } 84 mod, ok = modVal.(*eval.Ns) 85 if !ok { 86 return false 87 } 88 } 89 return hasFn(mod, segs[len(segs)-1]) 90 } 91 92 func hasFn(ns *eval.Ns, name string) bool { 93 fnVar, ok := ns.Index(name + eval.FnSuffix) 94 if !ok { 95 return false 96 } 97 _, ok = fnVar.(eval.Callable) 98 return ok 99 } 100 101 func isDirOrExecutable(fname string) bool { 102 stat, err := os.Stat(fname) 103 return err == nil && (stat.IsDir() || stat.Mode()&0111 != 0) 104 } 105 106 func hasExternalCommand(cmd string) bool { 107 _, err := exec.LookPath(cmd) 108 return err == nil 109 }