github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/edit/vars.go (about) 1 package edit 2 3 import ( 4 "strings" 5 6 "github.com/markusbkk/elvish/pkg/eval" 7 "github.com/markusbkk/elvish/pkg/eval/errs" 8 "github.com/markusbkk/elvish/pkg/eval/vals" 9 "github.com/markusbkk/elvish/pkg/eval/vars" 10 ) 11 12 func initVarsAPI(ed *Editor, nb eval.NsBuilder) { 13 nb.AddGoFns(map[string]interface{}{ 14 "add-var": addVar, 15 "add-vars": addVars, 16 }) 17 } 18 19 //elvdoc:fn add-var 20 // 21 // ```elvish 22 // edit:add-var $name $init 23 // ``` 24 // 25 // Defines a new variable in the interactive REPL with an initial value. The new variable becomes 26 // available during the next REPL cycle. 27 // 28 // Equivalent to running `var $name = $init` at a REPL prompt, but `$name` can be 29 // dynamic. 30 // 31 // This is most useful for modules to modify the REPL namespace. Example: 32 // 33 // ```elvish-transcript 34 // ~> cat .config/elvish/lib/a.elv 35 // for i [(range 10)] { 36 // edit:add-var foo$i $i 37 // } 38 // ~> use a 39 // ~> put $foo1 $foo2 40 // ▶ (num 1) 41 // ▶ (num 2) 42 // ``` 43 // 44 // Note that if you use a variable as the `$init` argument, `edit:add-var` 45 // doesn't add the variable "itself" to the REPL namespace. The variable in the 46 // REPL namespace will have the initial value set to the variable's value, but 47 // it is not an alias of the original variable: 48 // 49 // ```elvish-transcript 50 // ~> cat .config/elvish/lib/b.elv 51 // var foo = foo 52 // edit:add-var foo $foo 53 // ~> use b 54 // ~> put $foo 55 // ▶ foo 56 // ~> set foo = bar 57 // ~> echo $b:foo 58 // foo 59 // ``` 60 // 61 // One common use of this command is to put the definitions of functions intended for REPL use in a 62 // module instead of your [`rc.elv`](command.html#rc-file). For example, if you want to define `ll` 63 // as `ls -l`, you can do so in your [`rc.elv`](command.html#rc-file) directly: 64 // 65 // ```elvish 66 // fn ll {|@a| ls -l $@a } 67 // ``` 68 // 69 // But if you move the definition into a module (say `util.elv` in one of the 70 // [module search directories](command.html#module-search-directories), this 71 // function can only be used as `util:ll` (after `use util`). To make it usable 72 // directly as `ll`, you can add the following to `util.elv`: 73 // 74 // ```elvish 75 // edit:add-var ll~ $ll~ 76 // ``` 77 // 78 // Another use case is to add a module or function to the REPL namespace 79 // conditionally. For example, to only import [the `unix` module](unix.html) 80 // when actually running on UNIX, you can put the following in 81 // [`rc.elv`](command.html#rc-file): 82 // 83 // ```elvish 84 // use platform 85 // if $platform:is-unix { 86 // use unix 87 // edit:add-var unix: $unix: 88 // } 89 // ``` 90 91 func addVar(fm *eval.Frame, name string, val interface{}) error { 92 if !isUnqualified(name) { 93 return errs.BadValue{ 94 What: "name argument to edit:add-var", 95 Valid: "unqualified variable name", Actual: name} 96 } 97 variable := eval.MakeVarFromName(name) 98 err := variable.Set(val) 99 if err != nil { 100 return err 101 } 102 fm.Evaler.ExtendGlobal(eval.BuildNs().AddVar(name, vars.FromInit(val))) 103 return nil 104 } 105 106 //elvdoc:fn add-vars 107 // 108 // ```elvish 109 // edit:add-vars $map 110 // ``` 111 // 112 // Takes a map from strings to arbitrary values. Equivalent to calling 113 // `edit:add-var` for each key-value pair in the map. 114 115 func addVars(fm *eval.Frame, m vals.Map) error { 116 nb := eval.BuildNs() 117 for it := m.Iterator(); it.HasElem(); it.Next() { 118 k, val := it.Elem() 119 name, ok := k.(string) 120 if !ok { 121 return errs.BadValue{ 122 What: "key of argument to edit:add-vars", 123 Valid: "string", Actual: vals.Kind(k)} 124 } 125 if !isUnqualified(name) { 126 return errs.BadValue{ 127 What: "key of argument to edit:add-vars", 128 Valid: "unqualified variable name", Actual: name} 129 } 130 variable := eval.MakeVarFromName(name) 131 err := variable.Set(val) 132 if err != nil { 133 return err 134 } 135 nb.AddVar(name, variable) 136 } 137 fm.Evaler.ExtendGlobal(nb) 138 return nil 139 } 140 141 func isUnqualified(name string) bool { 142 i := strings.IndexByte(name, ':') 143 return i == -1 || i == len(name)-1 144 }