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 }