src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/edit/complete/ns_helper.go (about)

     1  package complete
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  
     7  	"src.elv.sh/pkg/eval"
     8  	"src.elv.sh/pkg/parse"
     9  	"src.elv.sh/pkg/parse/cmpd"
    10  	"src.elv.sh/pkg/parse/np"
    11  )
    12  
    13  var environ = os.Environ
    14  
    15  // Calls f for each variable name in namespace ns that can be found at the point
    16  // of np.
    17  func eachVariableInNs(ev *eval.Evaler, p np.Path, ns string, f func(s string)) {
    18  	switch ns {
    19  	case "", ":":
    20  		ev.Global().IterateKeysString(f)
    21  		ev.Builtin().IterateKeysString(f)
    22  		eachDefinedVariable(p[len(p)-1], p[0].Range().From, f)
    23  	case "e:":
    24  		eachExternal(func(cmd string) {
    25  			f(cmd + eval.FnSuffix)
    26  		})
    27  	case "E:":
    28  		for _, s := range environ() {
    29  			if i := strings.IndexByte(s, '='); i > 0 {
    30  				f(s[:i])
    31  			}
    32  		}
    33  	default:
    34  		// TODO: Support namespaces defined in the code too.
    35  		segs := eval.SplitQNameSegs(ns)
    36  		mod := ev.Global().IndexString(segs[0])
    37  		if mod == nil {
    38  			mod = ev.Builtin().IndexString(segs[0])
    39  		}
    40  		for _, seg := range segs[1:] {
    41  			if mod == nil {
    42  				return
    43  			}
    44  			mod = mod.Get().(*eval.Ns).IndexString(seg)
    45  		}
    46  		if mod != nil {
    47  			mod.Get().(*eval.Ns).IterateKeysString(f)
    48  		}
    49  	}
    50  }
    51  
    52  // Calls f for each variables defined in n that are visible at pos.
    53  func eachDefinedVariable(n parse.Node, pos int, f func(string)) {
    54  	if fn, ok := n.(*parse.Form); ok {
    55  		eachDefinedVariableInForm(fn, f)
    56  	}
    57  	if pn, ok := n.(*parse.Primary); ok && pn.Type == parse.Lambda {
    58  		for _, param := range pn.Elements {
    59  			if varRef, ok := cmpd.StringLiteral(param); ok {
    60  				_, name := eval.SplitSigil(varRef)
    61  				f(name)
    62  			}
    63  		}
    64  	}
    65  	for _, ch := range parse.Children(n) {
    66  		if ch.Range().From > pos {
    67  			break
    68  		}
    69  		if pn, ok := ch.(*parse.Primary); ok && pn.Type == parse.Lambda {
    70  			if pos >= pn.Range().To {
    71  				continue
    72  			}
    73  		}
    74  		eachDefinedVariable(ch, pos, f)
    75  	}
    76  }
    77  
    78  // Calls f for each variable defined in fn.
    79  func eachDefinedVariableInForm(fn *parse.Form, f func(string)) {
    80  	if fn.Head == nil {
    81  		return
    82  	}
    83  	switch head, _ := cmpd.StringLiteral(fn.Head); head {
    84  	case "var":
    85  		for _, arg := range fn.Args {
    86  			if parse.SourceText(arg) == "=" {
    87  				break
    88  			}
    89  			// TODO: This simplified version may not match the actual
    90  			// algorithm used by the compiler to parse an LHS.
    91  			if varRef, ok := cmpd.StringLiteral(arg); ok {
    92  				_, name := eval.SplitSigil(varRef)
    93  				f(name)
    94  			}
    95  		}
    96  	case "fn":
    97  		if len(fn.Args) >= 1 {
    98  			if name, ok := cmpd.StringLiteral(fn.Args[0]); ok {
    99  				f(name + eval.FnSuffix)
   100  			}
   101  		}
   102  	}
   103  }