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  }