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  }