github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/edit/binding_map.go (about)

     1  package edit
     2  
     3  import (
     4  	"errors"
     5  	"sort"
     6  
     7  	"github.com/markusbkk/elvish/pkg/eval"
     8  	"github.com/markusbkk/elvish/pkg/eval/vals"
     9  	"github.com/markusbkk/elvish/pkg/parse"
    10  	"github.com/markusbkk/elvish/pkg/ui"
    11  )
    12  
    13  var errValueShouldBeFn = errors.New("value should be function")
    14  
    15  // A special Map that converts its key to ui.Key and ensures that its values
    16  // satisfy eval.CallableValue.
    17  type bindingsMap struct {
    18  	vals.Map
    19  }
    20  
    21  var emptyBindingsMap = bindingsMap{vals.EmptyMap}
    22  
    23  // Repr returns the representation of the binding table as if it were an
    24  // ordinary map keyed by strings.
    25  func (bt bindingsMap) Repr(indent int) string {
    26  	var keys ui.Keys
    27  	for it := bt.Map.Iterator(); it.HasElem(); it.Next() {
    28  		k, _ := it.Elem()
    29  		keys = append(keys, k.(ui.Key))
    30  	}
    31  	sort.Sort(keys)
    32  
    33  	builder := vals.NewMapReprBuilder(indent)
    34  
    35  	for _, k := range keys {
    36  		v, _ := bt.Map.Index(k)
    37  		builder.WritePair(parse.Quote(k.String()), indent+2, vals.Repr(v, indent+2))
    38  	}
    39  
    40  	return builder.String()
    41  }
    42  
    43  // Index converts the index to ui.Key and uses the Index of the inner Map.
    44  func (bt bindingsMap) Index(index interface{}) (interface{}, error) {
    45  	key, err := toKey(index)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	return vals.Index(bt.Map, key)
    50  }
    51  
    52  func (bt bindingsMap) HasKey(k interface{}) bool {
    53  	_, ok := bt.Map.Index(k)
    54  	return ok
    55  }
    56  
    57  func (bt bindingsMap) GetKey(k ui.Key) eval.Callable {
    58  	v, ok := bt.Map.Index(k)
    59  	if !ok {
    60  		panic("get called when key not present")
    61  	}
    62  	return v.(eval.Callable)
    63  }
    64  
    65  // Assoc converts the index to ui.Key, ensures that the value is CallableValue,
    66  // uses the Assoc of the inner Map and converts the result to a BindingTable.
    67  func (bt bindingsMap) Assoc(k, v interface{}) (interface{}, error) {
    68  	key, err := toKey(k)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	f, ok := v.(eval.Callable)
    73  	if !ok {
    74  		return nil, errValueShouldBeFn
    75  	}
    76  	map2 := bt.Map.Assoc(key, f)
    77  	return bindingsMap{map2}, nil
    78  }
    79  
    80  // Dissoc converts the key to ui.Key and calls the Dissoc method of the inner
    81  // map.
    82  func (bt bindingsMap) Dissoc(k interface{}) interface{} {
    83  	key, err := toKey(k)
    84  	if err != nil {
    85  		// Key is invalid; dissoc is no-op.
    86  		return bt
    87  	}
    88  	return bindingsMap{bt.Map.Dissoc(key)}
    89  }
    90  
    91  func makeBindingMap(raw vals.Map) (bindingsMap, error) {
    92  	converted := vals.EmptyMap
    93  	for it := raw.Iterator(); it.HasElem(); it.Next() {
    94  		k, v := it.Elem()
    95  		f, ok := v.(eval.Callable)
    96  		if !ok {
    97  			return emptyBindingsMap, errValueShouldBeFn
    98  		}
    99  		key, err := toKey(k)
   100  		if err != nil {
   101  			return bindingsMap{}, err
   102  		}
   103  		converted = converted.Assoc(key, f)
   104  	}
   105  
   106  	return bindingsMap{converted}, nil
   107  }