github.com/elves/elvish@v0.15.0/pkg/edit/highlight.go (about)

     1  package edit
     2  
     3  import (
     4  	"os"
     5  	"os/exec"
     6  
     7  	"github.com/elves/elvish/pkg/cli"
     8  	"github.com/elves/elvish/pkg/edit/highlight"
     9  	"github.com/elves/elvish/pkg/eval"
    10  	"github.com/elves/elvish/pkg/fsutil"
    11  	"github.com/elves/elvish/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  	case first == "builtin:":
    53  		if hasFn(ev.Builtin(), rest) {
    54  			return true
    55  		}
    56  	default:
    57  		// Qualified name. Find the top-level module first.
    58  		if hasQualifiedFn(ev, first, rest) {
    59  			return true
    60  		}
    61  	}
    62  
    63  	// If all failed, it can still be an external command.
    64  	return hasExternalCommand(cmd)
    65  }
    66  
    67  func hasQualifiedFn(ev *eval.Evaler, firstNs string, rest string) bool {
    68  	if rest == "" {
    69  		return false
    70  	}
    71  	modVal, ok := ev.Global().Index(firstNs)
    72  	if !ok {
    73  		modVal, ok = ev.Builtin().Index(firstNs)
    74  		if !ok {
    75  			return false
    76  		}
    77  	}
    78  	mod, ok := modVal.(*eval.Ns)
    79  	if !ok {
    80  		return false
    81  	}
    82  	segs := eval.SplitQNameSegs(rest)
    83  	for _, seg := range segs[:len(segs)-1] {
    84  		modVal, ok = mod.Index(seg)
    85  		if !ok {
    86  			return false
    87  		}
    88  		mod, ok = modVal.(*eval.Ns)
    89  		if !ok {
    90  			return false
    91  		}
    92  	}
    93  	return hasFn(mod, segs[len(segs)-1])
    94  }
    95  
    96  func hasFn(ns *eval.Ns, name string) bool {
    97  	fnVar, ok := ns.Index(name + eval.FnSuffix)
    98  	if !ok {
    99  		return false
   100  	}
   101  	_, ok = fnVar.(eval.Callable)
   102  	return ok
   103  }
   104  
   105  func isDirOrExecutable(fname string) bool {
   106  	stat, err := os.Stat(fname)
   107  	return err == nil && (stat.IsDir() || stat.Mode()&0111 != 0)
   108  }
   109  
   110  func hasExternalCommand(cmd string) bool {
   111  	_, err := exec.LookPath(cmd)
   112  	return err == nil
   113  }