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