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 }