github.com/elves/elvish@v0.15.0/pkg/edit/binding_map.go (about)

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