keycodes.c (30047B)
1 #include <assert.h> 2 #include <inttypes.h> 3 #include <limits.h> 4 #include <stdbool.h> 5 #include <stdio.h> 6 #include <string.h> 7 #include <uv.h> 8 9 #include "nvim/api/private/defs.h" 10 #include "nvim/ascii_defs.h" 11 #include "nvim/charset.h" 12 #include "nvim/errors.h" 13 #include "nvim/eval/typval_defs.h" 14 #include "nvim/eval/vars.h" 15 #include "nvim/gettext_defs.h" 16 #include "nvim/globals.h" 17 #include "nvim/keycodes.h" 18 #include "nvim/macros_defs.h" 19 #include "nvim/mbyte.h" 20 #include "nvim/mbyte_defs.h" 21 #include "nvim/memory.h" 22 #include "nvim/message.h" 23 #include "nvim/mouse.h" 24 #include "nvim/option_vars.h" 25 #include "nvim/strings.h" 26 27 #include "keycode_names.generated.h" 28 #include "keycodes.c.generated.h" 29 30 // Some useful tables. 31 32 static const struct modmasktable { 33 uint16_t mod_mask; ///< Bit-mask for particular key modifier. 34 uint16_t mod_flag; ///< Bit(s) for particular key modifier. 35 char name; ///< Single letter name of modifier. 36 } mod_mask_table[] = { 37 { MOD_MASK_ALT, MOD_MASK_ALT, 'M' }, 38 { MOD_MASK_META, MOD_MASK_META, 'T' }, 39 { MOD_MASK_CTRL, MOD_MASK_CTRL, 'C' }, 40 { MOD_MASK_SHIFT, MOD_MASK_SHIFT, 'S' }, 41 { MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, '2' }, 42 { MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, '3' }, 43 { MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, '4' }, 44 { MOD_MASK_CMD, MOD_MASK_CMD, 'D' }, 45 // 'A' must be the last one 46 { MOD_MASK_ALT, MOD_MASK_ALT, 'A' }, 47 { 0, 0, NUL } 48 // NOTE: when adding an entry, update MAX_KEY_NAME_LEN! 49 }; 50 51 // Shifted key terminal codes and their unshifted equivalent. 52 // Don't add mouse codes here, they are handled separately! 53 54 #define MOD_KEYS_ENTRY_SIZE 5 55 56 static uint8_t modifier_keys_table[] = { 57 // mod mask with modifier without modifier 58 MOD_MASK_SHIFT, '&', '9', '@', '1', // begin 59 MOD_MASK_SHIFT, '&', '0', '@', '2', // cancel 60 MOD_MASK_SHIFT, '*', '1', '@', '4', // command 61 MOD_MASK_SHIFT, '*', '2', '@', '5', // copy 62 MOD_MASK_SHIFT, '*', '3', '@', '6', // create 63 MOD_MASK_SHIFT, '*', '4', 'k', 'D', // delete char 64 MOD_MASK_SHIFT, '*', '5', 'k', 'L', // delete line 65 MOD_MASK_SHIFT, '*', '7', '@', '7', // end 66 MOD_MASK_CTRL, KS_EXTRA, KE_C_END, '@', '7', // end 67 MOD_MASK_SHIFT, '*', '9', '@', '9', // exit 68 MOD_MASK_SHIFT, '*', '0', '@', '0', // find 69 MOD_MASK_SHIFT, '#', '1', '%', '1', // help 70 MOD_MASK_SHIFT, '#', '2', 'k', 'h', // home 71 MOD_MASK_CTRL, KS_EXTRA, KE_C_HOME, 'k', 'h', // home 72 MOD_MASK_SHIFT, '#', '3', 'k', 'I', // insert 73 MOD_MASK_SHIFT, '#', '4', 'k', 'l', // left arrow 74 MOD_MASK_CTRL, KS_EXTRA, KE_C_LEFT, 'k', 'l', // left arrow 75 MOD_MASK_SHIFT, '%', 'a', '%', '3', // message 76 MOD_MASK_SHIFT, '%', 'b', '%', '4', // move 77 MOD_MASK_SHIFT, '%', 'c', '%', '5', // next 78 MOD_MASK_SHIFT, '%', 'd', '%', '7', // options 79 MOD_MASK_SHIFT, '%', 'e', '%', '8', // previous 80 MOD_MASK_SHIFT, '%', 'f', '%', '9', // print 81 MOD_MASK_SHIFT, '%', 'g', '%', '0', // redo 82 MOD_MASK_SHIFT, '%', 'h', '&', '3', // replace 83 MOD_MASK_SHIFT, '%', 'i', 'k', 'r', // right arr. 84 MOD_MASK_CTRL, KS_EXTRA, KE_C_RIGHT, 'k', 'r', // right arr. 85 MOD_MASK_SHIFT, '%', 'j', '&', '5', // resume 86 MOD_MASK_SHIFT, '!', '1', '&', '6', // save 87 MOD_MASK_SHIFT, '!', '2', '&', '7', // suspend 88 MOD_MASK_SHIFT, '!', '3', '&', '8', // undo 89 MOD_MASK_SHIFT, KS_EXTRA, KE_S_UP, 'k', 'u', // up arrow 90 MOD_MASK_SHIFT, KS_EXTRA, KE_S_DOWN, 'k', 'd', // down arrow 91 92 // vt100 F1 93 MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF1, KS_EXTRA, KE_XF1, 94 MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF2, KS_EXTRA, KE_XF2, 95 MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF3, KS_EXTRA, KE_XF3, 96 MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF4, KS_EXTRA, KE_XF4, 97 98 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F1, 'k', '1', // F1 99 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F2, 'k', '2', 100 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F3, 'k', '3', 101 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F4, 'k', '4', 102 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F5, 'k', '5', 103 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F6, 'k', '6', 104 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F7, 'k', '7', 105 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F8, 'k', '8', 106 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F9, 'k', '9', 107 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F10, 'k', ';', // F10 108 109 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F11, 'F', '1', 110 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F12, 'F', '2', 111 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F13, 'F', '3', 112 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F14, 'F', '4', 113 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F15, 'F', '5', 114 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F16, 'F', '6', 115 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F17, 'F', '7', 116 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F18, 'F', '8', 117 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F19, 'F', '9', 118 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F20, 'F', 'A', 119 120 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F21, 'F', 'B', 121 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F22, 'F', 'C', 122 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F23, 'F', 'D', 123 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F24, 'F', 'E', 124 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F25, 'F', 'F', 125 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F26, 'F', 'G', 126 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F27, 'F', 'H', 127 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F28, 'F', 'I', 128 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F29, 'F', 'J', 129 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F30, 'F', 'K', 130 131 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F31, 'F', 'L', 132 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F32, 'F', 'M', 133 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F33, 'F', 'N', 134 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F34, 'F', 'O', 135 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F35, 'F', 'P', 136 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F36, 'F', 'Q', 137 MOD_MASK_SHIFT, KS_EXTRA, KE_S_F37, 'F', 'R', 138 139 // TAB pseudo code 140 MOD_MASK_SHIFT, 'k', 'B', KS_EXTRA, KE_TAB, 141 142 NUL 143 }; 144 145 static struct mousetable { 146 int pseudo_code; // Code for pseudo mouse event 147 int button; // Which mouse button is it? 148 bool is_click; // Is it a mouse button click event? 149 bool is_drag; // Is it a mouse drag event? 150 } mouse_table[] = { 151 { KE_LEFTMOUSE, MOUSE_LEFT, true, false }, 152 { KE_LEFTDRAG, MOUSE_LEFT, false, true }, 153 { KE_LEFTRELEASE, MOUSE_LEFT, false, false }, 154 { KE_MIDDLEMOUSE, MOUSE_MIDDLE, true, false }, 155 { KE_MIDDLEDRAG, MOUSE_MIDDLE, false, true }, 156 { KE_MIDDLERELEASE, MOUSE_MIDDLE, false, false }, 157 { KE_RIGHTMOUSE, MOUSE_RIGHT, true, false }, 158 { KE_RIGHTDRAG, MOUSE_RIGHT, false, true }, 159 { KE_RIGHTRELEASE, MOUSE_RIGHT, false, false }, 160 { KE_X1MOUSE, MOUSE_X1, true, false }, 161 { KE_X1DRAG, MOUSE_X1, false, true }, 162 { KE_X1RELEASE, MOUSE_X1, false, false }, 163 { KE_X2MOUSE, MOUSE_X2, true, false }, 164 { KE_X2DRAG, MOUSE_X2, false, true }, 165 { KE_X2RELEASE, MOUSE_X2, false, false }, 166 // DRAG without CLICK 167 { KE_MOUSEMOVE, MOUSE_RELEASE, false, true }, 168 // RELEASE without CLICK 169 { KE_IGNORE, MOUSE_RELEASE, false, false }, 170 { 0, 0, 0, 0 }, 171 }; 172 173 /// Return the modifier mask bit (#MOD_MASK_*) corresponding to mod name 174 /// 175 /// E.g. 'S' for shift, 'C' for ctrl. 176 int name_to_mod_mask(int c) 177 FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT 178 { 179 c = TOUPPER_ASC(c); 180 for (size_t i = 0; mod_mask_table[i].mod_mask != 0; i++) { 181 if (c == (uint8_t)mod_mask_table[i].name) { 182 return mod_mask_table[i].mod_flag; 183 } 184 } 185 return 0; 186 } 187 188 /// Check if there is a special key code for "key" with specified modifiers 189 /// 190 /// @param[in] key Initial key code. 191 /// @param[in,out] modifiers Initial modifiers, is adjusted to have simplified 192 /// modifiers. 193 /// 194 /// @return Simplified key code. 195 int simplify_key(const int key, int *modifiers) 196 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL 197 { 198 if (!(*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) { 199 return key; 200 } 201 202 // TAB is a special case. 203 if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) { 204 *modifiers &= ~MOD_MASK_SHIFT; 205 return K_S_TAB; 206 } 207 const int key0 = KEY2TERMCAP0(key); 208 const int key1 = KEY2TERMCAP1(key); 209 for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) { 210 if (key0 == modifier_keys_table[i + 3] 211 && key1 == modifier_keys_table[i + 4] 212 && (*modifiers & modifier_keys_table[i])) { 213 *modifiers &= ~modifier_keys_table[i]; 214 return TERMCAP2KEY(modifier_keys_table[i + 1], 215 modifier_keys_table[i + 2]); 216 } 217 } 218 return key; 219 } 220 221 /// Change <xKey> to <Key> 222 int handle_x_keys(const int key) 223 FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT 224 { 225 switch (key) { 226 case K_XUP: 227 return K_UP; 228 case K_XDOWN: 229 return K_DOWN; 230 case K_XLEFT: 231 return K_LEFT; 232 case K_XRIGHT: 233 return K_RIGHT; 234 case K_XHOME: 235 return K_HOME; 236 case K_ZHOME: 237 return K_HOME; 238 case K_XEND: 239 return K_END; 240 case K_ZEND: 241 return K_END; 242 case K_XF1: 243 return K_F1; 244 case K_XF2: 245 return K_F2; 246 case K_XF3: 247 return K_F3; 248 case K_XF4: 249 return K_F4; 250 case K_S_XF1: 251 return K_S_F1; 252 case K_S_XF2: 253 return K_S_F2; 254 case K_S_XF3: 255 return K_S_F3; 256 case K_S_XF4: 257 return K_S_F4; 258 } 259 return key; 260 } 261 262 /// @return a string which contains the name of the given key when the given modifiers are down. 263 char *get_special_key_name(int c, int modifiers) 264 { 265 static char string[MAX_KEY_NAME_LEN + 1]; 266 267 string[0] = '<'; 268 int idx = 1; 269 270 // Key that stands for a normal character. 271 if (IS_SPECIAL(c) && KEY2TERMCAP0(c) == KS_KEY) { 272 c = KEY2TERMCAP1(c); 273 } 274 275 // Translate shifted special keys into unshifted keys and set modifier. 276 // Same for CTRL and ALT modifiers. 277 if (IS_SPECIAL(c)) { 278 for (int i = 0; modifier_keys_table[i] != 0; i += MOD_KEYS_ENTRY_SIZE) { 279 if (KEY2TERMCAP0(c) == (int)modifier_keys_table[i + 1] 280 && (int)KEY2TERMCAP1(c) == (int)modifier_keys_table[i + 2]) { 281 modifiers |= modifier_keys_table[i]; 282 c = TERMCAP2KEY(modifier_keys_table[i + 3], 283 modifier_keys_table[i + 4]); 284 break; 285 } 286 } 287 } 288 289 // try to find the key in the special key table 290 int table_idx = find_special_key_in_table(c); 291 292 // When not a known special key, and not a printable character, try to 293 // extract modifiers. 294 if (c > 0 295 && utf_char2len(c) == 1) { 296 if (table_idx < 0 297 && (!vim_isprintc(c) || (c & 0x7f) == ' ') 298 && (c & 0x80)) { 299 c &= 0x7f; 300 modifiers |= MOD_MASK_ALT; 301 // try again, to find the un-alted key in the special key table 302 table_idx = find_special_key_in_table(c); 303 } 304 if (table_idx < 0 && !vim_isprintc(c) && c < ' ') { 305 c += '@'; 306 modifiers |= MOD_MASK_CTRL; 307 } 308 } 309 310 // translate the modifier into a string 311 for (int i = 0; mod_mask_table[i].name != 'A'; i++) { 312 if ((modifiers & mod_mask_table[i].mod_mask) 313 == mod_mask_table[i].mod_flag) { 314 string[idx++] = mod_mask_table[i].name; 315 string[idx++] = '-'; 316 } 317 } 318 319 if (table_idx < 0) { // unknown special key, may output t_xx 320 if (IS_SPECIAL(c)) { 321 string[idx++] = 't'; 322 string[idx++] = '_'; 323 string[idx++] = (char)(uint8_t)KEY2TERMCAP0(c); 324 string[idx++] = (char)(uint8_t)KEY2TERMCAP1(c); 325 } else { 326 // Not a special key, only modifiers, output directly. 327 int len = utf_char2len(c); 328 if (len == 1 && vim_isprintc(c)) { 329 string[idx++] = (char)(uint8_t)c; 330 } else if (len > 1) { 331 idx += utf_char2bytes(c, string + idx); 332 } else { 333 char *s = transchar(c); 334 while (*s) { 335 string[idx++] = *s++; 336 } 337 } 338 } 339 } else { // use name of special key 340 const String *s = &key_names_table[table_idx].name; 341 if ((int)s->size + idx + 2 <= MAX_KEY_NAME_LEN) { 342 STRCPY(string + idx, s->data); 343 idx += (int)s->size; 344 } 345 } 346 string[idx++] = '>'; 347 string[idx] = NUL; 348 349 return string; 350 } 351 352 /// Try translating a <> name ("keycode"). 353 /// 354 /// @param[in,out] srcp Source from which <> are translated. Is advanced to 355 /// after the <> name if there is a match. 356 /// @param[in] src_len Length of the srcp. 357 /// @param[out] dst Location where translation result will be kept. It must 358 // be at least 19 bytes per "<x>" form. 359 /// @param[in] flags FSK_ values 360 /// @param[in] escape_ks escape K_SPECIAL bytes in the character 361 /// @param[out] did_simplify found <C-H>, etc. 362 /// 363 /// @return Number of characters added to dst, zero for no match. 364 unsigned trans_special(const char **const srcp, const size_t src_len, char *const dst, 365 const int flags, const bool escape_ks, bool *const did_simplify) 366 FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT 367 { 368 int modifiers = 0; 369 int key = find_special_key(srcp, src_len, &modifiers, flags, did_simplify); 370 if (key == 0) { 371 return 0; 372 } 373 374 return special_to_buf(key, modifiers, escape_ks, dst); 375 } 376 377 /// Put the character sequence for "key" with "modifiers" into "dst" and return 378 /// the resulting length. 379 /// When "escape_ks" is true escape K_SPECIAL bytes in the character. 380 /// The sequence is not NUL terminated. 381 /// This is how characters in a string are encoded. 382 unsigned special_to_buf(int key, int modifiers, bool escape_ks, char *dst) 383 { 384 unsigned dlen = 0; 385 386 // Put the appropriate modifier in a string. 387 if (modifiers != 0) { 388 dst[dlen++] = (char)(uint8_t)K_SPECIAL; 389 dst[dlen++] = (char)(uint8_t)KS_MODIFIER; 390 dst[dlen++] = (char)(uint8_t)modifiers; 391 } 392 393 if (IS_SPECIAL(key)) { 394 dst[dlen++] = (char)(uint8_t)K_SPECIAL; 395 dst[dlen++] = (char)(uint8_t)KEY2TERMCAP0(key); 396 dst[dlen++] = (char)(uint8_t)KEY2TERMCAP1(key); 397 } else if (escape_ks) { 398 char *after = add_char2buf(key, dst + dlen); 399 assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX); 400 dlen = (unsigned)(after - dst); 401 } else { 402 dlen += (unsigned)utf_char2bytes(key, dst + dlen); 403 } 404 405 return dlen; 406 } 407 408 /// Try translating a <> name 409 /// 410 /// @param[in,out] srcp Translated <> name. Is advanced to after the <> name. 411 /// @param[in] src_len srcp length. 412 /// @param[out] modp Location where information about modifiers is saved. 413 /// @param[in] flags FSK_ values 414 /// @param[out] did_simplify FSK_SIMPLIFY and found <C-H>, etc. 415 /// 416 /// @return Key and modifiers or 0 if there is no match. 417 int find_special_key(const char **const srcp, const size_t src_len, int *const modp, 418 const int flags, bool *const did_simplify) 419 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 3) 420 { 421 const char *bp; 422 const char *const end = *srcp + src_len - 1; 423 const bool in_string = flags & FSK_IN_STRING; 424 uvarnumber_T n; 425 int l; 426 427 if (src_len == 0) { 428 return 0; 429 } 430 431 const char *src = *srcp; 432 if (src[0] != '<') { 433 return 0; 434 } 435 if (src[1] == '*') { // <*xxx>: do not simplify 436 src++; 437 } 438 439 // Find end of modifier list 440 const char *last_dash = src; 441 for (bp = src + 1; bp <= end && (*bp == '-' || ascii_isident(*bp)); bp++) { 442 if (*bp == '-') { 443 last_dash = bp; 444 if (bp + 1 <= end) { 445 l = utfc_ptr2len_len(bp + 1, (int)(end - bp) + 1); 446 // Anything accepted, like <C-?>. 447 // <C-"> or <M-"> are not special in strings as " is 448 // the string delimiter. With a backslash it works: <M-\"> 449 if (end - bp > l && !(in_string && bp[1] == '"') && bp[l + 1] == '>') { 450 bp += l; 451 } else if (end - bp > 2 && in_string && bp[1] == '\\' 452 && bp[2] == '"' && bp[3] == '>') { 453 bp += 2; 454 } 455 } 456 } 457 if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { 458 bp += 3; // skip t_xx, xx may be '-' or '>' 459 } else if (end - bp > 4 && STRNICMP(bp, "char-", 5) == 0) { 460 vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0, true, NULL); 461 if (l == 0) { 462 emsg(_(e_invarg)); 463 return 0; 464 } 465 bp += l + 5; 466 break; 467 } 468 } 469 470 if (bp <= end && *bp == '>') { // found matching '>' 471 int key; 472 const char *end_of_name = bp + 1; 473 474 // Which modifiers are given? 475 int modifiers = 0x0; 476 for (bp = src + 1; bp < last_dash; bp++) { 477 if (*bp != '-') { 478 int bit = name_to_mod_mask((uint8_t)(*bp)); 479 if (bit == 0x0) { 480 break; // Illegal modifier name 481 } 482 modifiers |= bit; 483 } 484 } 485 486 // Legal modifier name. 487 if (bp >= last_dash) { 488 if (STRNICMP(last_dash + 1, "char-", 5) == 0 489 && ascii_isdigit(last_dash[6])) { 490 // <Char-123> or <Char-033> or <Char-0x33> 491 vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, true, NULL); 492 if (l == 0) { 493 emsg(_(e_invarg)); 494 return 0; 495 } 496 key = (int)n; 497 } else { 498 int off = 1; 499 500 // Modifier with single letter, or special key name. 501 if (in_string && last_dash[1] == '\\' && last_dash[2] == '"') { 502 // Special case for a double-quoted string 503 off = l = 2; 504 } else { 505 l = utfc_ptr2len(last_dash + 1); 506 } 507 if (modifiers != 0 && last_dash[l + 1] == '>') { 508 key = utf_ptr2char(last_dash + off); 509 } else { 510 key = get_special_key_code(last_dash + off); 511 if (!(flags & FSK_KEEP_X_KEY)) { 512 key = handle_x_keys(key); 513 } 514 } 515 } 516 517 // get_special_key_code() may return NUL for invalid 518 // special key name. 519 if (key != NUL) { 520 // Only use a modifier when there is no special key code that 521 // includes the modifier. 522 key = simplify_key(key, &modifiers); 523 524 if (!(flags & FSK_KEYCODE)) { 525 // don't want keycode, use single byte code 526 if (key == K_BS) { 527 key = BS; 528 } else if (key == K_DEL || key == K_KDEL) { 529 key = DEL; 530 } 531 } 532 533 // Normal Key with modifier: 534 // Try to make a single byte code (except for Alt/Meta modifiers). 535 if (!IS_SPECIAL(key)) { 536 key = extract_modifiers(key, &modifiers, flags & FSK_SIMPLIFY, did_simplify); 537 } 538 539 *modp = modifiers; 540 *srcp = end_of_name; 541 return key; 542 } // else { ELOG("unknown key: '%s'", src); } 543 } 544 } 545 return 0; 546 } 547 548 /// Try to include modifiers (except alt/meta) in the key. 549 /// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc. 550 /// @param[in] simplify if false, don't do Ctrl 551 /// @param[out] did_simplify set when it is not NULL and "simplify" is true and 552 /// Ctrl is removed from modifiers 553 static int extract_modifiers(int key, int *modp, const bool simplify, bool *const did_simplify) 554 { 555 int modifiers = *modp; 556 557 if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { 558 key = TOUPPER_ASC(key); 559 // With <C-S-a> we keep the shift modifier. 560 // With <S-a>, <A-S-a> and <S-A> we don't keep the shift modifier. 561 if (!(modifiers & MOD_MASK_CTRL)) { 562 modifiers &= ~MOD_MASK_SHIFT; 563 } 564 } 565 566 // <C-H> and <C-h> mean the same thing, always use "H" 567 if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) { 568 key = TOUPPER_ASC(key); 569 } 570 571 if (simplify && (modifiers & MOD_MASK_CTRL) 572 && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { 573 key = CTRL_CHR(key); 574 modifiers &= ~MOD_MASK_CTRL; 575 if (key == NUL) { // <C-@> is <Nul> 576 key = K_ZERO; 577 } 578 if (did_simplify != NULL) { 579 *did_simplify = true; 580 } 581 } 582 583 *modp = modifiers; 584 return key; 585 } 586 587 /// Try to find key "c" in the special key table. 588 /// @return the index when found, -1 when not found. 589 int find_special_key_in_table(int c) 590 { 591 for (int i = 0; i < (int)ARRAY_SIZE(key_names_table); i++) { 592 if (c == key_names_table[i].key && !key_names_table[i].is_alt) { 593 return i; 594 } 595 } 596 597 return -1; 598 } 599 600 /// Find the special key with the given name 601 /// 602 /// @param[in] name Name of the special. Does not have to end with NUL, it is 603 /// assumed to end before the first non-idchar. If name starts 604 /// with "t_" the next two characters are interpreted as 605 /// a termcap name. 606 /// 607 /// @return Key code or 0 if not found. 608 int get_special_key_code(const char *name) 609 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 610 { 611 if (name[0] == 't' && name[1] == '_' && name[2] != NUL && name[3] != NUL) { 612 return TERMCAP2KEY((uint8_t)name[2], (uint8_t)name[3]); 613 } 614 615 const char *name_end = name; 616 while (ascii_isident(*name_end)) { 617 name_end++; 618 } 619 620 int idx = get_special_key_code_hash(name, (size_t)(name_end - name)); 621 return idx >= 0 ? key_names_table[idx].key : 0; 622 } 623 624 /// Look up the given mouse code to return the relevant information in the other arguments. 625 /// @return which button is down or was released. 626 int get_mouse_button(int code, bool *is_click, bool *is_drag) 627 { 628 for (int i = 0; mouse_table[i].pseudo_code; i++) { 629 if (code == mouse_table[i].pseudo_code) { 630 *is_click = mouse_table[i].is_click; 631 *is_drag = mouse_table[i].is_drag; 632 return mouse_table[i].button; 633 } 634 } 635 return 0; // Shouldn't get here 636 } 637 638 /// Replace any terminal code strings with the equivalent internal representation. 639 /// 640 /// Used for the "from" and "to" part of a mapping, and the "to" part of a menu command. 641 /// Any strings like "<C-UP>" are also replaced, unless `special` is false. 642 /// K_SPECIAL by itself is replaced by K_SPECIAL KS_SPECIAL KE_FILLER. 643 /// 644 /// When "flags" has REPTERM_FROM_PART, trailing <C-v> is included, otherwise it is removed (to make 645 /// ":map xx ^V" map xx to nothing). When cpo_val contains CPO_BSLASH, a backslash can be used in 646 /// place of <C-v>. All other <C-v> characters are removed. 647 /// 648 /// @param[in] from What characters to replace. 649 /// @param[in] from_len Length of the "from" argument. 650 /// @param[out] bufp Location where results were saved in case of success (allocated). 651 /// If `*bufp` is non-NULL, it will be used directly, 652 /// and is assumed to be 128 bytes long (enough for transcoding LHS of mapping), 653 /// and will be set to NULL in case of failure. 654 /// @param[in] sid_arg Script ID to use for <SID>, or 0 to use current_sctx 655 /// @param[in] flags REPTERM_FROM_PART see above 656 /// REPTERM_DO_LT also translate <lt> 657 /// REPTERM_NO_SPECIAL do not accept <key> notation 658 /// REPTERM_NO_SIMPLIFY do not simplify <C-H> into 0x08, etc. 659 /// @param[out] did_simplify set when some <C-H> code was simplified, unless it is NULL. 660 /// @param[in] cpo_val The value of 'cpoptions' to use. Only CPO_BSLASH matters. 661 /// 662 /// @return The same as what `*bufp` is set to. 663 char *replace_termcodes(const char *const from, const size_t from_len, char **const bufp, 664 const scid_T sid_arg, const int flags, bool *const did_simplify, 665 const char *const cpo_val) 666 FUNC_ATTR_NONNULL_ARG(1, 3, 7) 667 { 668 size_t dlen = 0; 669 const char *const end = from + from_len - 1; 670 671 // backslash is a special character 672 const bool do_backslash = (vim_strchr(cpo_val, CPO_BSLASH) == NULL); 673 const bool do_special = !(flags & REPTERM_NO_SPECIAL); 674 675 bool allocated = (*bufp == NULL); 676 677 // Allocate space for the translation. Worst case a single character is 678 // replaced by 6 bytes (shifted special key), plus a NUL at the end. 679 const size_t buf_len = allocated ? from_len * 6 + 1 : 128; 680 char *result = allocated ? xmalloc(buf_len) : *bufp; // buffer for resulting string 681 682 const char *src = from; 683 684 // Copy each byte from *from to result[dlen] 685 while (src <= end) { 686 if (!allocated && dlen + 64 > buf_len) { 687 return NULL; 688 } 689 // Check for special <> keycodes, like "<C-S-LeftMouse>" 690 if (do_special && ((flags & REPTERM_DO_LT) || ((end - src) >= 3 691 && strncmp(src, "<lt>", 4) != 0))) { 692 // Change <SID>Func to K_SNR <script-nr> _Func. This name is used 693 // for script-local user functions. 694 // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) 695 if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { 696 if (sid_arg < 0 || (sid_arg == 0 && current_sctx.sc_sid <= 0)) { 697 emsg(_(e_usingsid)); 698 } else { 699 const scid_T sid = sid_arg != 0 ? sid_arg : current_sctx.sc_sid; 700 src += 5; 701 result[dlen++] = (char)K_SPECIAL; 702 result[dlen++] = (char)KS_EXTRA; 703 result[dlen++] = KE_SNR; 704 snprintf(result + dlen, buf_len - dlen, "%" PRIdSCID, sid); 705 dlen += strlen(result + dlen); 706 result[dlen++] = '_'; 707 continue; 708 } 709 } 710 711 size_t slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, 712 FSK_KEYCODE | ((flags & REPTERM_NO_SIMPLIFY) ? 0 : FSK_SIMPLIFY), 713 true, did_simplify); 714 if (slen) { 715 dlen += slen; 716 continue; 717 } 718 } 719 720 if (do_special) { 721 char *p, *s; 722 int len; 723 724 // Replace <Leader> by the value of "mapleader". 725 // Replace <LocalLeader> by the value of "maplocalleader". 726 // If "mapleader" or "maplocalleader" isn't set use a backslash. 727 if (end - src >= 7 && STRNICMP(src, "<Leader>", 8) == 0) { 728 len = 8; 729 p = get_var_value("g:mapleader"); 730 } else if (end - src >= 12 && STRNICMP(src, "<LocalLeader>", 13) == 0) { 731 len = 13; 732 p = get_var_value("g:maplocalleader"); 733 } else { 734 len = 0; 735 p = NULL; 736 } 737 738 if (len != 0) { 739 // Allow up to 8 * 6 characters for "mapleader". 740 if (p == NULL || *p == NUL || strlen(p) > 8 * 6) { 741 s = "\\"; 742 } else { 743 s = p; 744 } 745 while (*s != NUL) { 746 result[dlen++] = *s++; 747 } 748 src += len; 749 continue; 750 } 751 } 752 753 // Remove CTRL-V and ignore the next character. 754 // For "from" side the CTRL-V at the end is included, for the "to" 755 // part it is removed. 756 // If 'cpoptions' does not contain 'B', also accept a backslash. 757 char key = *src; 758 if (key == Ctrl_V || (do_backslash && key == '\\')) { 759 src++; // skip CTRL-V or backslash 760 if (src > end) { 761 if (flags & REPTERM_FROM_PART) { 762 result[dlen++] = key; 763 } 764 break; 765 } 766 } 767 768 // skip multibyte char correctly 769 for (ssize_t i = utfc_ptr2len_len(src, (int)(end - src) + 1); i > 0; i--) { 770 // If the character is K_SPECIAL, replace it with K_SPECIAL 771 // KS_SPECIAL KE_FILLER. 772 if (*src == (char)K_SPECIAL) { 773 result[dlen++] = (char)K_SPECIAL; 774 result[dlen++] = (char)KS_SPECIAL; 775 result[dlen++] = KE_FILLER; 776 } else { 777 result[dlen++] = *src; 778 } 779 src++; 780 } 781 } 782 result[dlen] = NUL; 783 784 if (allocated) { 785 *bufp = xrealloc(result, dlen + 1); 786 } 787 788 return *bufp; 789 } 790 791 /// Add character "c" to buffer "s" 792 /// 793 /// Escapes the special meaning of K_SPECIAL, handles multi-byte 794 /// characters. 795 /// 796 /// @param[in] c Character to add. 797 /// @param[out] s Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes. 798 /// 799 /// @return Pointer to after the added bytes. 800 char *add_char2buf(int c, char *s) 801 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT 802 { 803 char temp[MB_MAXBYTES + 1]; 804 const int len = utf_char2bytes(c, temp); 805 for (int i = 0; i < len; i++) { 806 c = (uint8_t)temp[i]; 807 // Need to escape K_SPECIAL like in the typeahead buffer. 808 if (c == K_SPECIAL) { 809 *s++ = (char)(uint8_t)K_SPECIAL; 810 *s++ = (char)(uint8_t)KS_SPECIAL; 811 *s++ = KE_FILLER; 812 } else { 813 *s++ = (char)(uint8_t)c; 814 } 815 } 816 return s; 817 } 818 819 /// Copy "p" to allocated memory, escaping K_SPECIAL so that the result 820 /// can be put in the typeahead buffer. 821 char *vim_strsave_escape_ks(char *p) 822 { 823 // Need a buffer to hold up to three times as much. Four in case of an 824 // illegal utf-8 byte: 825 // 0xc0 -> 0xc3 - 0x80 -> 0xc3 K_SPECIAL KS_SPECIAL KE_FILLER 826 char *res = xmalloc(strlen(p) * 4 + 1); 827 char *d = res; 828 for (char *s = p; *s != NUL;) { 829 if ((uint8_t)s[0] == K_SPECIAL && s[1] != NUL && s[2] != NUL) { 830 // Copy special key unmodified. 831 *d++ = *s++; 832 *d++ = *s++; 833 *d++ = *s++; 834 } else { 835 // Add character, possibly multi-byte to destination, escaping 836 // K_SPECIAL. Be careful, it can be an illegal byte! 837 d = add_char2buf(utf_ptr2char(s), d); 838 s += utf_ptr2len(s); 839 } 840 } 841 *d = NUL; 842 843 return res; 844 } 845 846 /// Remove escaping from K_SPECIAL characters. Reverse of 847 /// vim_strsave_escape_ks(). Works in-place. 848 void vim_unescape_ks(char *p) 849 { 850 uint8_t *s = (uint8_t *)p; 851 uint8_t *d = (uint8_t *)p; 852 853 while (*s != NUL) { 854 if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) { 855 *d++ = K_SPECIAL; 856 s += 3; 857 } else { 858 *d++ = *s++; 859 } 860 } 861 *d = NUL; 862 }