github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/edit/ui/key.go (about)

     1  package ui
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/u-root/u-root/cmds/core/elvish/hash"
    10  	"github.com/u-root/u-root/cmds/core/elvish/parse"
    11  	"github.com/u-root/u-root/cmds/core/elvish/util"
    12  )
    13  
    14  var ErrKeyMustBeString = errors.New("key must be key or string value")
    15  
    16  // Key represents a single keyboard input, typically assembled from a escape
    17  // sequence.
    18  type Key struct {
    19  	Rune rune
    20  	Mod  Mod
    21  }
    22  
    23  // Default is used in the key binding table to indicate default binding.
    24  var Default = Key{DefaultBindingRune, 0}
    25  
    26  // Mod represents a modifier key.
    27  type Mod byte
    28  
    29  // Values for Mod.
    30  const (
    31  	// Shift is the shift modifier. It is only applied to special keys (e.g.
    32  	// Shift-F1). For instance 'A' and '@' which are typically entered with the
    33  	// shift key pressed, are not considered to be shift-modified.
    34  	Shift Mod = 1 << iota
    35  	// Alt is the alt modifier, traditionally known as the meta modifier.
    36  	Alt
    37  	Ctrl
    38  )
    39  
    40  const functionKeyOffset = 1000
    41  
    42  // Special negative runes to represent function keys, used in the Rune field of
    43  // the Key struct.
    44  const (
    45  	// DefaultBindingRune is a special value to represent default binding.
    46  	DefaultBindingRune rune = iota - functionKeyOffset
    47  
    48  	F1
    49  	F2
    50  	F3
    51  	F4
    52  	F5
    53  	F6
    54  	F7
    55  	F8
    56  	F9
    57  	F10
    58  	F11
    59  	F12
    60  
    61  	Up
    62  	Down
    63  	Right
    64  	Left
    65  
    66  	Home
    67  	Insert
    68  	Delete
    69  	End
    70  	PageUp
    71  	PageDown
    72  
    73  	// Some function key names are just aliases for their ASCII representation
    74  
    75  	Tab       = '\t'
    76  	Enter     = '\n'
    77  	Backspace = 0x7f
    78  )
    79  
    80  // functionKey stores the names of function keys, in the same order they appeared above.
    81  var functionKeyNames = [...]string{
    82  	"Default",
    83  	"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
    84  	"Up", "Down", "Right", "Left",
    85  	"Home", "Insert", "Delete", "End", "PageUp", "PageDown",
    86  }
    87  
    88  // keyNames stores the name of function keys with a positive rune.
    89  var keyNames = map[rune]string{
    90  	Tab: "Tab", Enter: "Enter", Backspace: "Backspace",
    91  }
    92  
    93  func (k Key) Kind() string {
    94  	return "edit:Key"
    95  }
    96  
    97  func (k Key) Equal(other interface{}) bool {
    98  	return k == other
    99  }
   100  
   101  func (k Key) Hash() uint32 {
   102  	h := hash.DJBInit
   103  	h = hash.DJBCombine(h, uint32(k.Rune))
   104  	h = hash.DJBCombine(h, uint32(k.Mod))
   105  	return h
   106  }
   107  
   108  func (k Key) Repr(int) string {
   109  	return "(edit:key " + parse.Quote(k.String()) + ")"
   110  }
   111  
   112  func (k Key) String() string {
   113  	var b bytes.Buffer
   114  	if k.Mod&Ctrl != 0 {
   115  		b.WriteString("Ctrl-")
   116  	}
   117  	if k.Mod&Alt != 0 {
   118  		b.WriteString("Alt-")
   119  	}
   120  	if k.Mod&Shift != 0 {
   121  		b.WriteString("Shift-")
   122  	}
   123  	if k.Rune > 0 {
   124  		if name, ok := keyNames[k.Rune]; ok {
   125  			b.WriteString(name)
   126  		} else {
   127  			b.WriteRune(k.Rune)
   128  		}
   129  	} else {
   130  		i := int(k.Rune + functionKeyOffset)
   131  		if i >= len(functionKeyNames) {
   132  			fmt.Fprintf(&b, "(bad function key %d)", k.Rune)
   133  		} else {
   134  			b.WriteString(functionKeyNames[i])
   135  		}
   136  	}
   137  	return b.String()
   138  }
   139  
   140  // modifierByName maps a name to an modifier. It is used for parsing keys where
   141  // the modifier string is first turned to lower case, so that all of C, c,
   142  // CTRL, Ctrl and ctrl can represent the Ctrl modifier.
   143  var modifierByName = map[string]Mod{
   144  	"s": Shift, "shift": Shift,
   145  	"a": Alt, "alt": Alt,
   146  	"m": Alt, "meta": Alt,
   147  	"c": Ctrl, "ctrl": Ctrl,
   148  }
   149  
   150  // parseKey parses a key. The syntax is:
   151  //
   152  // Key = { Mod ('+' | '-') } BareKey
   153  //
   154  // BareKey = FunctionKeyName | SingleRune
   155  func parseKey(s string) (Key, error) {
   156  	var k Key
   157  	// parse modifiers
   158  	for {
   159  		i := strings.IndexAny(s, "+-")
   160  		if i == -1 {
   161  			break
   162  		}
   163  		modname := strings.ToLower(s[:i])
   164  		mod, ok := modifierByName[modname]
   165  		if !ok {
   166  			return Key{}, fmt.Errorf("bad modifier: %q", modname)
   167  		}
   168  		k.Mod |= mod
   169  		s = s[i+1:]
   170  	}
   171  
   172  	if len(s) == 1 {
   173  		k.Rune = rune(s[0])
   174  		// XXX The following assumptions about keys with Ctrl are not checked
   175  		// with all terminals.
   176  		if k.Mod&Ctrl != 0 {
   177  			// Keys with Ctrl as one of the modifiers and a single ASCII letter
   178  			// as the base rune do not distinguish between cases. So we
   179  			// normalize the base rune to upper case.
   180  			if 'a' <= k.Rune && k.Rune <= 'z' {
   181  				k.Rune += 'A' - 'a'
   182  			}
   183  			// Tab is equivalent to Ctrl-I and Ctrl-J is equivalent to Enter.
   184  			// Normalize Ctrl-I to Tab and Ctrl-J to Enter.
   185  			if k.Rune == 'I' {
   186  				k.Mod &= ^Ctrl
   187  				k.Rune = Tab
   188  			} else if k.Rune == 'J' {
   189  				k.Mod &= ^Ctrl
   190  				k.Rune = Enter
   191  			}
   192  		}
   193  		return k, nil
   194  	}
   195  
   196  	for r, name := range keyNames {
   197  		if s == name {
   198  			k.Rune = r
   199  			return k, nil
   200  		}
   201  	}
   202  
   203  	for i, name := range functionKeyNames {
   204  		if s == name {
   205  			k.Rune = rune(i - functionKeyOffset)
   206  			return k, nil
   207  		}
   208  	}
   209  
   210  	return Key{}, fmt.Errorf("bad key: %q", s)
   211  }
   212  
   213  // ToKey converts an Elvish Value to a Key. If the passed Value is not Key or
   214  // String, it throws an error.
   215  func ToKey(k interface{}) Key {
   216  	switch k := k.(type) {
   217  	case Key:
   218  		return k
   219  	case string:
   220  		key, err := parseKey(string(k))
   221  		if err != nil {
   222  			util.Throw(err)
   223  		}
   224  		return key
   225  	default:
   226  		util.Throw(ErrKeyMustBeString)
   227  		panic("unreachable")
   228  	}
   229  }
   230  
   231  // Keys implements sort.Interface.
   232  type Keys []Key
   233  
   234  func (ks Keys) Len() int      { return len(ks) }
   235  func (ks Keys) Swap(i, j int) { ks[i], ks[j] = ks[j], ks[i] }
   236  func (ks Keys) Less(i, j int) bool {
   237  	return ks[i].Mod < ks[j].Mod ||
   238  		(ks[i].Mod == ks[j].Mod && ks[i].Rune < ks[j].Rune)
   239  }