github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/ui/key.go (about)

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