src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/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 DefaultKey = 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 any) 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 //lint:ignore ST1005 We want this error to begin with "Ctrl" rather than "ctrl" 195 // since the user has to use the capitalized form when creating a key binding. 196 return Key{}, fmt.Errorf("Ctrl modifier with literal control char: %q", k.Rune) 197 } 198 // Convert literal control char to the equivalent canonical form, 199 // e.g. "\e" to Ctrl-'[' and "\t" to Ctrl-I. 200 k.Mod |= Ctrl 201 k.Rune += 0x40 202 } 203 // TODO(xiaq): The following assumptions about keys with Ctrl are not 204 // checked with all terminals. 205 if k.Mod&Ctrl != 0 { 206 // Keys with Ctrl as one of the modifiers and a single ASCII letter 207 // as the base rune do not distinguish between cases. So we 208 // normalize the base rune to upper case. 209 if 'a' <= k.Rune && k.Rune <= 'z' { 210 k.Rune += 'A' - 'a' 211 } 212 // Normalize Ctrl-I to Tab, Ctrl-J to Enter, and Ctrl-? to Backspace. 213 if k.Rune == 'I' { 214 k.Mod &= ^Ctrl 215 k.Rune = Tab 216 } else if k.Rune == 'J' { 217 k.Mod &= ^Ctrl 218 k.Rune = Enter 219 } 220 } 221 return k, nil 222 } 223 224 // Is this is a symbolic key name, such as `Enter`, we recognize? 225 for r, name := range keyNames { 226 if s == name { 227 k.Rune = r 228 return k, nil 229 } 230 } 231 232 return Key{}, fmt.Errorf("bad key: %s", parse.Quote(s)) 233 } 234 235 // Keys implements sort.Interface. 236 type Keys []Key 237 238 func (ks Keys) Len() int { return len(ks) } 239 func (ks Keys) Swap(i, j int) { ks[i], ks[j] = ks[j], ks[i] } 240 func (ks Keys) Less(i, j int) bool { 241 return ks[i].Mod < ks[j].Mod || 242 (ks[i].Mod == ks[j].Mod && ks[i].Rune < ks[j].Rune) 243 }