To: vim_dev@googlegroups.com Subject: Patch 8.1.2145 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.2145 Problem: Cannot map when modifyOtherKeys is enabled. Solution: Add the mapping twice, both with modifier and as 0x08. Use only the first one when modifyOtherKeys has been detected. Files: src/term.c, src/eval.c, src/getchar.c, src/globals.h, src/gui_mac.c, src/gui_w32.c, src/highlight.c, src/if_ole.cpp, src/main.c, src/map.c, src/menu.c, src/misc2.c, src/option.c, src/proto/misc2.pro, src/proto/term.pro, src/testdir/test_termcodes.vim, src/structs.h, src/terminal.c, src/usercmd.c, src/vim.h *** ../vim-8.1.2144/src/term.c 2019-10-12 21:08:37.002660427 +0200 --- src/term.c 2019-10-13 16:32:33.182348952 +0200 *************** *** 4845,4850 **** --- 4845,4851 ---- else if ((arg[0] == 27 && argc == 3 && trail == '~') || (argc == 2 && trail == 'u')) { + seenModifyOtherKeys = TRUE; if (trail == 'u') key = arg[0]; else *************** *** 4853,4865 **** modifiers = decode_modifiers(arg[1]); // Some keys already have Shift included, pass them as ! // normal keys. if (modifiers == MOD_MASK_SHIFT && ((key >= '@' && key <= 'Z') || key == '^' || key == '_' || (key >= '{' && key <= '~'))) modifiers = 0; // insert modifiers with KS_MODIFIER new_slen = modifiers2keycode(modifiers, &key, string); slen = csi_len; --- 4854,4873 ---- modifiers = decode_modifiers(arg[1]); // Some keys already have Shift included, pass them as ! // normal keys. Not when Ctrl is also used, because ! // and are different. if (modifiers == MOD_MASK_SHIFT && ((key >= '@' && key <= 'Z') || key == '^' || key == '_' || (key >= '{' && key <= '~'))) modifiers = 0; + // When used with Ctrl we always make a letter upper case, + // so that mapping and are the same. Typing + // also uses "H" but modifier is different. + if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) + key = TOUPPER_ASC(key); + // insert modifiers with KS_MODIFIER new_slen = modifiers2keycode(modifiers, &key, string); slen = csi_len; *************** *** 5340,5357 **** * pointer to it is returned. If something fails *bufp is set to NULL and from * is returned. * ! * CTRL-V characters are removed. When "from_part" is TRUE, a trailing CTRL-V ! * is included, otherwise it is removed (for ":map xx ^V", maps xx to ! * nothing). When 'cpoptions' does not contain 'B', a backslash can be used ! * instead of a CTRL-V. */ char_u * replace_termcodes( char_u *from, char_u **bufp, ! int from_part, ! int do_lt, /* also translate */ ! int special) /* always accept notation */ { int i; int slen; --- 5348,5373 ---- * pointer to it is returned. If something fails *bufp is set to NULL and from * is returned. * ! * CTRL-V characters are removed. When "flags" has REPTERM_FROM_PART, a ! * trailing CTRL-V is included, otherwise it is removed (for ":map xx ^V", maps ! * xx to nothing). When 'cpoptions' does not contain 'B', a backslash can be ! * used instead of a CTRL-V. ! * ! * Flags: ! * REPTERM_FROM_PART see above ! * REPTERM_DO_LT also translate ! * REPTERM_SPECIAL always accept notation ! * REPTERM_NO_SIMPLIFY do not simplify to 0x08 and set 8th bit for ! * ! * "did_simplify" is set when some or code was simplified, unless ! * it is NULL. */ char_u * replace_termcodes( char_u *from, char_u **bufp, ! int flags, ! int *did_simplify) { int i; int slen; *************** *** 5364,5370 **** char_u *result; /* buffer for resulting string */ do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); ! do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) || special; do_key_code = (vim_strchr(p_cpo, CPO_KEYCODE) == NULL); /* --- 5380,5387 ---- char_u *result; /* buffer for resulting string */ do_backslash = (vim_strchr(p_cpo, CPO_BSLASH) == NULL); ! do_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL) ! || (flags & REPTERM_SPECIAL); do_key_code = (vim_strchr(p_cpo, CPO_KEYCODE) == NULL); /* *************** *** 5383,5389 **** /* * Check for #n at start only: function key n */ ! if (from_part && src[0] == '#' && VIM_ISDIGIT(src[1])) /* function key */ { result[dlen++] = K_SPECIAL; result[dlen++] = 'k'; --- 5400,5406 ---- /* * Check for #n at start only: function key n */ ! if ((flags & REPTERM_FROM_PART) && src[0] == '#' && VIM_ISDIGIT(src[1])) { result[dlen++] = K_SPECIAL; result[dlen++] = 'k'; *************** *** 5403,5409 **** * If 'cpoptions' does not contain '<', check for special key codes, * like "" */ ! if (do_special && (do_lt || STRNCMP(src, "", 4) != 0)) { #ifdef FEAT_EVAL /* --- 5420,5427 ---- * If 'cpoptions' does not contain '<', check for special key codes, * like "" */ ! if (do_special && ((flags & REPTERM_DO_LT) ! || STRNCMP(src, "", 4) != 0)) { #ifdef FEAT_EVAL /* *************** *** 5429,5435 **** } #endif ! slen = trans_special(&src, result + dlen, TRUE, FALSE); if (slen) { dlen += slen; --- 5447,5454 ---- } #endif ! slen = trans_special(&src, result + dlen, TRUE, FALSE, ! (flags & REPTERM_NO_SIMPLIFY) == 0, did_simplify); if (slen) { dlen += slen; *************** *** 5509,5515 **** ++src; /* skip CTRL-V or backslash */ if (*src == NUL) { ! if (from_part) result[dlen++] = key; break; } --- 5528,5534 ---- ++src; /* skip CTRL-V or backslash */ if (*src == NUL) { ! if (flags & REPTERM_FROM_PART) result[dlen++] = key; break; } *** ../vim-8.1.2144/src/eval.c 2019-10-06 22:00:08.293244132 +0200 --- src/eval.c 2019-10-12 22:54:09.036941168 +0200 *************** *** 3526,3532 **** break; /* Special key, e.g.: "\" */ ! case '<': extra = trans_special(&p, name, TRUE, TRUE); if (extra != 0) { name += extra; --- 3526,3533 ---- break; /* Special key, e.g.: "\" */ ! case '<': extra = trans_special(&p, name, TRUE, TRUE, ! TRUE, NULL); if (extra != 0) { name += extra; *** ../vim-8.1.2144/src/getchar.c 2019-10-10 21:49:24.974264458 +0200 --- src/getchar.c 2019-10-13 16:30:20.398643743 +0200 *************** *** 52,58 **** */ static int block_redo = FALSE; ! static int KeyNoremap = 0; /* remapping flags */ /* * Variables used by vgetorpeek() and flush_buffers(). --- 52,58 ---- */ static int block_redo = FALSE; ! static int KeyNoremap = 0; // remapping flags /* * Variables used by vgetorpeek() and flush_buffers(). *************** *** 1771,1777 **** if (!no_reduce_keys) { // A modifier was not used for a mapping, apply it to ASCII ! // keys. if ((mod_mask & MOD_MASK_CTRL) && ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_'))) --- 1771,1777 ---- if (!no_reduce_keys) { // A modifier was not used for a mapping, apply it to ASCII ! // keys. Shift would already have been applied. if ((mod_mask & MOD_MASK_CTRL) && ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_'))) *************** *** 2240,2245 **** --- 2240,2246 ---- // Skip ":lmap" mappings if keys were mapped. if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State) + && !(mp->m_simplified && seenModifyOtherKeys) && ((mp->m_mode & LANGMAP) == 0 || typebuf.tb_maplen == 0)) { #ifdef FEAT_LANGMAP *** ../vim-8.1.2144/src/globals.h 2019-10-10 21:13:59.962360351 +0200 --- src/globals.h 2019-10-12 21:30:03.017623342 +0200 *************** *** 1002,1007 **** --- 1002,1011 ---- EXTERN int reg_recording INIT(= 0); // register for recording or zero EXTERN int reg_executing INIT(= 0); // register being executed or zero + // Set when a modifyOtherKeys sequence was seen, then simplified mappings will + // no longer be used. + EXTERN int seenModifyOtherKeys INIT(= FALSE); + EXTERN int no_mapping INIT(= FALSE); // currently no mapping allowed EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed EXTERN int allow_keys INIT(= FALSE); // allow key codes when no_mapping *** ../vim-8.1.2144/src/gui_mac.c 2019-06-14 21:36:51.014437500 +0200 --- src/gui_mac.c 2019-10-12 22:52:04.457341368 +0200 *************** *** 2177,2183 **** key_char = simplify_key(key_char, (int *)&vimModifiers); /* Interpret META, include SHIFT, etc. */ ! key_char = extract_modifiers(key_char, (int *)&vimModifiers); if (key_char == CSI) key_char = K_CSI; --- 2177,2184 ---- key_char = simplify_key(key_char, (int *)&vimModifiers); /* Interpret META, include SHIFT, etc. */ ! key_char = extract_modifiers(key_char, (int *)&vimModifiers, ! TRUE, NULL); if (key_char == CSI) key_char = K_CSI; *************** *** 4772,4778 **** char_u *p_actext; p_actext = menu->actext; ! key = find_special_key(&p_actext, &modifiers, FALSE, FALSE, FALSE); if (*p_actext != 0) key = 0; /* error: trailing text */ /* find_special_key() returns a keycode with as many of the --- 4773,4780 ---- char_u *p_actext; p_actext = menu->actext; ! key = find_special_key(&p_actext, &modifiers, FALSE, FALSE, FALSE, ! TRUE, NULL); if (*p_actext != 0) key = 0; /* error: trailing text */ /* find_special_key() returns a keycode with as many of the *** ../vim-8.1.2144/src/gui_w32.c 2019-09-21 23:09:00.975830710 +0200 --- src/gui_w32.c 2019-10-12 22:50:19.669659794 +0200 *************** *** 850,856 **** modifiers &= ~MOD_MASK_SHIFT; /* Interpret the ALT key as making the key META, include SHIFT, etc. */ ! ch = extract_modifiers(ch, &modifiers); if (ch == CSI) ch = K_CSI; --- 850,856 ---- modifiers &= ~MOD_MASK_SHIFT; /* Interpret the ALT key as making the key META, include SHIFT, etc. */ ! ch = extract_modifiers(ch, &modifiers, TRUE, NULL); if (ch == CSI) ch = K_CSI; *** ../vim-8.1.2144/src/highlight.c 2019-10-06 22:00:08.297244105 +0200 --- src/highlight.c 2019-10-12 22:54:29.120874876 +0200 *************** *** 1417,1423 **** */ for (p = arg, off = 0; off < 100 - 6 && *p; ) { ! len = trans_special(&p, buf + off, FALSE, FALSE); if (len > 0) // recognized special char off += len; else // copy as normal char --- 1417,1424 ---- */ for (p = arg, off = 0; off < 100 - 6 && *p; ) { ! len = trans_special(&p, buf + off, FALSE, FALSE, ! TRUE, NULL); if (len > 0) // recognized special char off += len; else // copy as normal char *** ../vim-8.1.2144/src/if_ole.cpp 2019-05-25 20:21:24.677951017 +0200 --- src/if_ole.cpp 2019-10-12 21:53:01.919597212 +0200 *************** *** 330,336 **** } /* Translate key codes like */ ! str = replace_termcodes((char_u *)buffer, &ptr, FALSE, TRUE, FALSE); /* If ptr was set, then a new buffer was allocated, * so we can free the old one. --- 330,336 ---- } /* Translate key codes like */ ! str = replace_termcodes((char_u *)buffer, &ptr, REPTERM_DO_LT, NULL); /* If ptr was set, then a new buffer was allocated, * so we can free the old one. *** ../vim-8.1.2144/src/main.c 2019-09-25 21:43:07.275251603 +0200 --- src/main.c 2019-10-12 21:46:49.873275123 +0200 *************** *** 4339,4345 **** * sequence is recognised - needed for a real backslash. */ p_cpo = (char_u *)"Bk"; ! str = replace_termcodes((char_u *)str, &ptr, FALSE, TRUE, FALSE); p_cpo = cpo_save; if (*ptr != NUL) /* trailing CTRL-V results in nothing */ --- 4339,4345 ---- * sequence is recognised - needed for a real backslash. */ p_cpo = (char_u *)"Bk"; ! str = replace_termcodes((char_u *)str, &ptr, REPTERM_DO_LT, NULL); p_cpo = cpo_save; if (*ptr != NUL) /* trailing CTRL-V results in nothing */ *** ../vim-8.1.2144/src/map.c 2019-09-03 22:08:13.673035035 +0200 --- src/map.c 2019-10-13 15:33:19.963928416 +0200 *************** *** 256,273 **** char_u *p; int n; int len = 0; // init for GCC - char_u *newstr; int hasarg; int haskey; ! int did_it = FALSE; ! int did_local = FALSE; ! int round; char_u *keys_buf = NULL; char_u *arg_buf = NULL; int retval = 0; int do_backslash; - int hash; - int new_hash; mapblock_T **abbr_table; mapblock_T **map_table; int unique = FALSE; --- 256,270 ---- char_u *p; int n; int len = 0; // init for GCC int hasarg; int haskey; ! int do_print; ! int keyround; char_u *keys_buf = NULL; + char_u *alt_keys_buf = NULL; char_u *arg_buf = NULL; int retval = 0; int do_backslash; mapblock_T **abbr_table; mapblock_T **map_table; int unique = FALSE; *************** *** 277,282 **** --- 274,280 ---- #ifdef FEAT_EVAL int expr = FALSE; #endif + int did_simplify = FALSE; int noremap; char_u *orig_rhs; *************** *** 375,380 **** --- 373,379 ---- rhs = p; hasarg = (*rhs != NUL); haskey = (*keys != NUL); + do_print = !haskey || (maptype != 1 && !hasarg); // check for :unmap without argument if (maptype == 1 && !haskey) *************** *** 389,761 **** // replace_termcodes() may move the result to allocated memory, which // needs to be freed later (*keys_buf and *arg_buf). // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. if (haskey) ! keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special); orig_rhs = rhs; if (hasarg) { if (STRICMP(rhs, "") == 0) // "" means nothing rhs = (char_u *)""; else ! rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special); } ! // check arguments and translate function keys ! if (haskey) { ! len = (int)STRLEN(keys); ! if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars { ! retval = 1; ! goto theend; } ! if (abbrev && maptype != 1) { ! // If an abbreviation ends in a keyword character, the ! // rest must be all keyword-char or all non-keyword-char. ! // Otherwise we won't be able to find the start of it in a ! // vi-compatible way. ! if (has_mbyte) { ! int first, last; ! int same = -1; ! first = vim_iswordp(keys); ! last = first; ! p = keys + (*mb_ptr2len)(keys); ! n = 1; ! while (p < keys + len) ! { ! ++n; // nr of (multi-byte) chars ! last = vim_iswordp(p); // type of last char ! if (same == -1 && last != first) ! same = n - 1; // count of same char type ! p += (*mb_ptr2len)(p); ! } ! if (last && n > 2 && same >= 0 && same < n - 1) { ! retval = 1; ! goto theend; } ! } ! else if (vim_iswordc(keys[len - 1])) // ends in keyword char for (n = 0; n < len - 2; ++n) if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2])) { retval = 1; goto theend; } ! // An abbreviation cannot contain white space. ! for (n = 0; n < len; ++n) ! if (VIM_ISWHITE(keys[n])) ! { ! retval = 1; ! goto theend; ! } } - } ! if (haskey && hasarg && abbrev) // if we will add an abbreviation ! no_abbr = FALSE; // reset flag that indicates there are // no abbreviations ! if (!haskey || (maptype != 1 && !hasarg)) ! msg_start(); ! // Check if a new local mapping wasn't already defined globally. ! if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) ! { ! // need to loop over all global hash lists ! for (hash = 0; hash < 256 && !got_int; ++hash) { ! if (abbrev) ! { ! if (hash != 0) // there is only one abbreviation list ! break; ! mp = first_abbr; ! } ! else ! mp = maphash[hash]; ! for ( ; mp != NULL && !got_int; mp = mp->m_next) { ! // check entries with the same mode ! if ((mp->m_mode & mode) != 0 ! && mp->m_keylen == len ! && unique ! && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) { ! if (abbrev) ! semsg(_("E224: global abbreviation already exists for %s"), ! mp->m_keys); ! else ! semsg(_("E225: global mapping already exists for %s"), ! mp->m_keys); ! retval = 5; ! goto theend; } } } - } ! // When listing global mappings, also list buffer-local ones here. ! if (map_table != curbuf->b_maphash && !hasarg && maptype != 1) ! { ! // need to loop over all global hash lists ! for (hash = 0; hash < 256 && !got_int; ++hash) { ! if (abbrev) ! { ! if (hash != 0) // there is only one abbreviation list ! break; ! mp = curbuf->b_first_abbr; ! } ! else ! mp = curbuf->b_maphash[hash]; ! for ( ; mp != NULL && !got_int; mp = mp->m_next) { ! // check entries with the same mode ! if ((mp->m_mode & mode) != 0) { ! if (!haskey) // show all entries ! { ! showmap(mp, TRUE); ! did_local = TRUE; ! } ! else { ! n = mp->m_keylen; ! if (STRNCMP(mp->m_keys, keys, ! (size_t)(n < len ? n : len)) == 0) { showmap(mp, TRUE); did_local = TRUE; } } } } } - } ! // Find an entry in the maphash[] list that matches. ! // For :unmap we may loop two times: once to try to unmap an entry with a ! // matching 'from' part, a second time, if the first fails, to unmap an ! // entry with a matching 'to' part. This was done to allow ":ab foo bar" ! // to be unmapped by typing ":unab foo", where "foo" will be replaced by ! // "bar" because of the abbreviation. ! for (round = 0; (round == 0 || maptype == 1) && round <= 1 ! && !did_it && !got_int; ++round) ! { ! // need to loop over all hash lists ! for (hash = 0; hash < 256 && !got_int; ++hash) { ! if (abbrev) ! { ! if (hash > 0) // there is only one abbreviation list ! break; ! mpp = abbr_table; ! } ! else ! mpp = &(map_table[hash]); ! for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) { ! ! if (!(mp->m_mode & mode)) // skip entries with wrong mode ! { ! mpp = &(mp->m_next); ! continue; ! } ! if (!haskey) // show all entries { ! showmap(mp, map_table != maphash); ! did_it = TRUE; } ! else // do we have a match? { ! if (round) // second round: Try unmap "rhs" string { ! n = (int)STRLEN(mp->m_str); ! p = mp->m_str; } ! else { ! n = mp->m_keylen; ! p = mp->m_keys; } ! if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0) { ! if (maptype == 1) // delete entry { ! // Only accept a full match. For abbreviations we ! // ignore trailing space when matching with the ! // "lhs", since an abbreviation can't have ! // trailing space. ! if (n != len && (!abbrev || round || n > len || *skipwhite(keys + n) != NUL)) { mpp = &(mp->m_next); continue; } ! // We reset the indicated mode bits. If nothing is ! // left the entry is deleted below. ! mp->m_mode &= ~mode; ! did_it = TRUE; // remember we did something ! } ! else if (!hasarg) // show matching entry ! { ! showmap(mp, map_table != maphash); ! did_it = TRUE; ! } ! else if (n != len) // new entry is ambiguous ! { ! mpp = &(mp->m_next); ! continue; ! } ! else if (unique) ! { ! if (abbrev) ! semsg(_("E226: abbreviation already exists for %s"), ! p); else - semsg(_("E227: mapping already exists for %s"), p); - retval = 5; - goto theend; - } - else // new rhs for existing entry - { - mp->m_mode &= ~mode; // remove mode bits - if (mp->m_mode == 0 && !did_it) // reuse entry { ! newstr = vim_strsave(rhs); ! if (newstr == NULL) { ! retval = 4; // no mem ! goto theend; ! } ! vim_free(mp->m_str); ! mp->m_str = newstr; ! vim_free(mp->m_orig_str); ! mp->m_orig_str = vim_strsave(orig_rhs); ! mp->m_noremap = noremap; ! mp->m_nowait = nowait; ! mp->m_silent = silent; ! mp->m_mode = mode; #ifdef FEAT_EVAL ! mp->m_expr = expr; ! mp->m_script_ctx = current_sctx; ! mp->m_script_ctx.sc_lnum += sourcing_lnum; #endif ! did_it = TRUE; } - } - if (mp->m_mode == 0) // entry can be deleted - { - map_free(mpp); - continue; // continue with *mpp - } ! // May need to put this entry into another hash list. ! new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); ! if (!abbrev && new_hash != hash) ! { ! *mpp = mp->m_next; ! mp->m_next = map_table[new_hash]; ! map_table[new_hash] = mp; ! continue; // continue with *mpp } } } - mpp = &(mp->m_next); } } - } ! if (maptype == 1) // delete entry ! { ! if (!did_it) ! retval = 2; // no match ! else if (*keys == Ctrl_C) { ! // If CTRL-C has been unmapped, reuse it for Interrupting. ! if (map_table == curbuf->b_maphash) ! curbuf->b_mapped_ctrl_c &= ~mode; ! else ! mapped_ctrl_c &= ~mode; } - goto theend; - } ! if (!haskey || !hasarg) // print entries ! { ! if (!did_it && !did_local) { ! if (abbrev) ! msg(_("No abbreviation found")); ! else ! msg(_("No mapping found")); } - goto theend; // listing finished - } ! if (did_it) // have added the new entry already ! goto theend; ! // Get here when adding a new entry to the maphash[] list or abbrlist. ! mp = ALLOC_ONE(mapblock_T); ! if (mp == NULL) ! { ! retval = 4; // no mem ! goto theend; ! } ! // If CTRL-C has been mapped, don't always use it for Interrupting. ! if (*keys == Ctrl_C) ! { ! if (map_table == curbuf->b_maphash) ! curbuf->b_mapped_ctrl_c |= mode; ! else ! mapped_ctrl_c |= mode; ! } ! mp->m_keys = vim_strsave(keys); ! mp->m_str = vim_strsave(rhs); ! mp->m_orig_str = vim_strsave(orig_rhs); ! if (mp->m_keys == NULL || mp->m_str == NULL) ! { ! vim_free(mp->m_keys); ! vim_free(mp->m_str); ! vim_free(mp->m_orig_str); ! vim_free(mp); ! retval = 4; // no mem ! goto theend; ! } ! mp->m_keylen = (int)STRLEN(mp->m_keys); ! mp->m_noremap = noremap; ! mp->m_nowait = nowait; ! mp->m_silent = silent; ! mp->m_mode = mode; #ifdef FEAT_EVAL ! mp->m_expr = expr; ! mp->m_script_ctx = current_sctx; ! mp->m_script_ctx.sc_lnum += sourcing_lnum; #endif ! // add the new entry in front of the abbrlist or maphash[] list ! if (abbrev) ! { ! mp->m_next = *abbr_table; ! *abbr_table = mp; ! } ! else ! { ! n = MAP_HASH(mp->m_mode, mp->m_keys[0]); ! mp->m_next = map_table[n]; ! map_table[n] = mp; } theend: vim_free(keys_buf); vim_free(arg_buf); return retval; } --- 388,814 ---- // replace_termcodes() may move the result to allocated memory, which // needs to be freed later (*keys_buf and *arg_buf). // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. + // If something like is simplified to 0x08 then mark it as simplified + // and also add a n entry with a modifier, which will work when + // modifyOtherKeys is working. if (haskey) ! { ! char_u *new_keys; ! int flags = REPTERM_FROM_PART | REPTERM_DO_LT; ! ! if (special) ! flags |= REPTERM_SPECIAL; ! new_keys = replace_termcodes(keys, &keys_buf, flags, &did_simplify); ! if (did_simplify) ! (void)replace_termcodes(keys, &alt_keys_buf, ! flags | REPTERM_NO_SIMPLIFY, NULL); ! keys = new_keys; ! } orig_rhs = rhs; if (hasarg) { if (STRICMP(rhs, "") == 0) // "" means nothing rhs = (char_u *)""; else ! rhs = replace_termcodes(rhs, &arg_buf, ! REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL); } ! /* ! * The following is done twice if we have two versions of keys: ! * "alt_keys_buf" is not NULL. ! */ ! for (keyround = 1; keyround <= 2; ++keyround) { ! int did_it = FALSE; ! int did_local = FALSE; ! int round; ! int hash; ! int new_hash; ! ! if (keyround == 2) { ! if (alt_keys_buf == NULL) ! break; ! keys = alt_keys_buf; } + else if (alt_keys_buf != NULL && do_print) + // when printing always use the not-simplified map + keys = alt_keys_buf; ! // check arguments and translate function keys ! if (haskey) { ! len = (int)STRLEN(keys); ! if (len > MAXMAPLEN) // maximum length of MAXMAPLEN chars { ! retval = 1; ! goto theend; ! } ! if (abbrev && maptype != 1) ! { ! // If an abbreviation ends in a keyword character, the ! // rest must be all keyword-char or all non-keyword-char. ! // Otherwise we won't be able to find the start of it in a ! // vi-compatible way. ! if (has_mbyte) { ! int first, last; ! int same = -1; ! ! first = vim_iswordp(keys); ! last = first; ! p = keys + (*mb_ptr2len)(keys); ! n = 1; ! while (p < keys + len) ! { ! ++n; // nr of (multi-byte) chars ! last = vim_iswordp(p); // type of last char ! if (same == -1 && last != first) ! same = n - 1; // count of same char type ! p += (*mb_ptr2len)(p); ! } ! if (last && n > 2 && same >= 0 && same < n - 1) ! { ! retval = 1; ! goto theend; ! } } ! else if (vim_iswordc(keys[len - 1])) ! // ends in keyword char for (n = 0; n < len - 2; ++n) if (vim_iswordc(keys[n]) != vim_iswordc(keys[len - 2])) { retval = 1; goto theend; } ! // An abbreviation cannot contain white space. ! for (n = 0; n < len; ++n) ! if (VIM_ISWHITE(keys[n])) ! { ! retval = 1; ! goto theend; ! } ! } } ! if (haskey && hasarg && abbrev) // if we will add an abbreviation ! no_abbr = FALSE; // reset flag that indicates there are // no abbreviations ! if (do_print) ! msg_start(); ! // Check if a new local mapping wasn't already defined globally. ! if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1) { ! // need to loop over all global hash lists ! for (hash = 0; hash < 256 && !got_int; ++hash) { ! if (abbrev) { ! if (hash != 0) // there is only one abbreviation list ! break; ! mp = first_abbr; ! } ! else ! mp = maphash[hash]; ! for ( ; mp != NULL && !got_int; mp = mp->m_next) ! { ! // check entries with the same mode ! if ((mp->m_mode & mode) != 0 ! && mp->m_keylen == len ! && unique ! && STRNCMP(mp->m_keys, keys, (size_t)len) == 0) ! { ! if (abbrev) ! semsg(_( ! "E224: global abbreviation already exists for %s"), ! mp->m_keys); ! else ! semsg(_( ! "E225: global mapping already exists for %s"), ! mp->m_keys); ! retval = 5; ! goto theend; ! } } } } ! // When listing global mappings, also list buffer-local ones here. ! if (map_table != curbuf->b_maphash && !hasarg && maptype != 1) { ! // need to loop over all global hash lists ! for (hash = 0; hash < 256 && !got_int; ++hash) { ! if (abbrev) { ! if (hash != 0) // there is only one abbreviation list ! break; ! mp = curbuf->b_first_abbr; ! } ! else ! mp = curbuf->b_maphash[hash]; ! for ( ; mp != NULL && !got_int; mp = mp->m_next) ! { ! // check entries with the same mode ! if ((mp->m_mode & mode) != 0) { ! if (!haskey) // show all entries { showmap(mp, TRUE); did_local = TRUE; } + else + { + n = mp->m_keylen; + if (STRNCMP(mp->m_keys, keys, + (size_t)(n < len ? n : len)) == 0) + { + showmap(mp, TRUE); + did_local = TRUE; + } + } } } } } ! // Find an entry in the maphash[] list that matches. ! // For :unmap we may loop two times: once to try to unmap an entry with ! // a matching 'from' part, a second time, if the first fails, to unmap ! // an entry with a matching 'to' part. This was done to allow ":ab foo ! // bar" to be unmapped by typing ":unab foo", where "foo" will be ! // replaced by "bar" because of the abbreviation. ! for (round = 0; (round == 0 || maptype == 1) && round <= 1 ! && !did_it && !got_int; ++round) { ! // need to loop over all hash lists ! for (hash = 0; hash < 256 && !got_int; ++hash) { ! if (abbrev) { ! if (hash > 0) // there is only one abbreviation list ! break; ! mpp = abbr_table; } ! else ! mpp = &(map_table[hash]); ! for (mp = *mpp; mp != NULL && !got_int; mp = *mpp) { ! ! if (!(mp->m_mode & mode)) // skip entries with wrong mode { ! mpp = &(mp->m_next); ! continue; } ! if (!haskey) // show all entries { ! showmap(mp, map_table != maphash); ! did_it = TRUE; } ! else // do we have a match? { ! if (round) // second round: Try unmap "rhs" string { ! n = (int)STRLEN(mp->m_str); ! p = mp->m_str; ! } ! else ! { ! n = mp->m_keylen; ! p = mp->m_keys; ! } ! if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0) ! { ! if (maptype == 1) ! { ! // Delete entry. ! // Only accept a full match. For abbreviations ! // we ignore trailing space when matching with ! // the "lhs", since an abbreviation can't have ! // trailing space. ! if (n != len && (!abbrev || round || n > len || *skipwhite(keys + n) != NUL)) + { + mpp = &(mp->m_next); + continue; + } + // We reset the indicated mode bits. If nothing + // is left the entry is deleted below. + mp->m_mode &= ~mode; + did_it = TRUE; // remember we did something + } + else if (!hasarg) // show matching entry + { + showmap(mp, map_table != maphash); + did_it = TRUE; + } + else if (n != len) // new entry is ambiguous { mpp = &(mp->m_next); continue; } ! else if (unique) ! { ! if (abbrev) ! semsg(_( ! "E226: abbreviation already exists for %s"), ! p); ! else ! semsg(_( ! "E227: mapping already exists for %s"), ! p); ! retval = 5; ! goto theend; ! } else { ! // new rhs for existing entry ! mp->m_mode &= ~mode; // remove mode bits ! if (mp->m_mode == 0 && !did_it) // reuse entry { ! char_u *newstr = vim_strsave(rhs); ! ! if (newstr == NULL) ! { ! retval = 4; // no mem ! goto theend; ! } ! vim_free(mp->m_str); ! mp->m_str = newstr; ! vim_free(mp->m_orig_str); ! mp->m_orig_str = vim_strsave(orig_rhs); ! mp->m_noremap = noremap; ! mp->m_nowait = nowait; ! mp->m_silent = silent; ! mp->m_mode = mode; ! mp->m_simplified = ! did_simplify && keyround == 1; #ifdef FEAT_EVAL ! mp->m_expr = expr; ! mp->m_script_ctx = current_sctx; ! mp->m_script_ctx.sc_lnum += sourcing_lnum; #endif ! did_it = TRUE; ! } ! } ! if (mp->m_mode == 0) // entry can be deleted ! { ! map_free(mpp); ! continue; // continue with *mpp } ! // May need to put this entry into another hash ! // list. ! new_hash = MAP_HASH(mp->m_mode, mp->m_keys[0]); ! if (!abbrev && new_hash != hash) ! { ! *mpp = mp->m_next; ! mp->m_next = map_table[new_hash]; ! map_table[new_hash] = mp; ! continue; // continue with *mpp ! } } } + mpp = &(mp->m_next); } } } ! if (maptype == 1) { ! // delete entry ! if (!did_it) ! retval = 2; // no match ! else if (*keys == Ctrl_C) ! { ! // If CTRL-C has been unmapped, reuse it for Interrupting. ! if (map_table == curbuf->b_maphash) ! curbuf->b_mapped_ctrl_c &= ~mode; ! else ! mapped_ctrl_c &= ~mode; ! } ! continue; } ! if (!haskey || !hasarg) { ! // print entries ! if (!did_it && !did_local) ! { ! if (abbrev) ! msg(_("No abbreviation found")); ! else ! msg(_("No mapping found")); ! } ! goto theend; // listing finished } ! if (did_it) ! continue; // have added the new entry already ! // Get here when adding a new entry to the maphash[] list or abbrlist. ! mp = ALLOC_ONE(mapblock_T); ! if (mp == NULL) ! { ! retval = 4; // no mem ! goto theend; ! } ! // If CTRL-C has been mapped, don't always use it for Interrupting. ! if (*keys == Ctrl_C) ! { ! if (map_table == curbuf->b_maphash) ! curbuf->b_mapped_ctrl_c |= mode; ! else ! mapped_ctrl_c |= mode; ! } ! mp->m_keys = vim_strsave(keys); ! mp->m_str = vim_strsave(rhs); ! mp->m_orig_str = vim_strsave(orig_rhs); ! if (mp->m_keys == NULL || mp->m_str == NULL) ! { ! vim_free(mp->m_keys); ! vim_free(mp->m_str); ! vim_free(mp->m_orig_str); ! vim_free(mp); ! retval = 4; // no mem ! goto theend; ! } ! mp->m_keylen = (int)STRLEN(mp->m_keys); ! mp->m_noremap = noremap; ! mp->m_nowait = nowait; ! mp->m_silent = silent; ! mp->m_mode = mode; ! mp->m_simplified = did_simplify && keyround == 1; #ifdef FEAT_EVAL ! mp->m_expr = expr; ! mp->m_script_ctx = current_sctx; ! mp->m_script_ctx.sc_lnum += sourcing_lnum; #endif ! // add the new entry in front of the abbrlist or maphash[] list ! if (abbrev) ! { ! mp->m_next = *abbr_table; ! *abbr_table = mp; ! } ! else ! { ! n = MAP_HASH(mp->m_mode, mp->m_keys[0]); ! mp->m_next = map_table[n]; ! map_table[n] = mp; ! } } theend: vim_free(keys_buf); + vim_free(alt_keys_buf); vim_free(arg_buf); return retval; } *************** *** 934,940 **** char_u *buf; int retval; ! rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE); retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr); vim_free(buf); --- 987,993 ---- char_u *buf; int retval; ! rhs = replace_termcodes(str, &buf, REPTERM_DO_LT, NULL); retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr); vim_free(buf); *************** *** 2036,2042 **** mode = get_map_mode(&which, 0); ! keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE); rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); vim_free(keys_buf); --- 2089,2096 ---- mode = get_map_mode(&which, 0); ! keys = replace_termcodes(keys, &keys_buf, ! REPTERM_FROM_PART | REPTERM_DO_LT, NULL); rhs = check_map(keys, mode, exact, FALSE, abbr, &mp, &buffer_local); vim_free(keys_buf); *** ../vim-8.1.2144/src/menu.c 2019-08-20 20:13:40.326821952 +0200 --- src/menu.c 2019-10-12 21:51:21.328038913 +0200 *************** *** 372,378 **** else if (modes & MENU_TIP_MODE) map_buf = NULL; /* Menu tips are plain text. */ else ! map_to = replace_termcodes(map_to, &map_buf, FALSE, TRUE, special); menuarg.modes = modes; #ifdef FEAT_TOOLBAR menuarg.iconfile = icon; --- 372,379 ---- else if (modes & MENU_TIP_MODE) map_buf = NULL; /* Menu tips are plain text. */ else ! map_to = replace_termcodes(map_to, &map_buf, ! REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL); menuarg.modes = modes; #ifdef FEAT_TOOLBAR menuarg.iconfile = icon; *** ../vim-8.1.2144/src/misc2.c 2019-10-12 16:12:50.968492825 +0200 --- src/misc2.c 2019-10-13 15:57:54.153813986 +0200 *************** *** 2696,2707 **** char_u **srcp, char_u *dst, int keycode, // prefer key code, e.g. K_DEL instead of DEL ! int in_string) // TRUE when inside a double quoted string { int modifiers = 0; int key; ! key = find_special_key(srcp, &modifiers, keycode, FALSE, in_string); if (key == 0) return 0; --- 2696,2710 ---- char_u **srcp, char_u *dst, int keycode, // prefer key code, e.g. K_DEL instead of DEL ! int in_string, // TRUE when inside a double quoted string ! int simplify, // simplify and ! int *did_simplify) // found or { int modifiers = 0; int key; ! key = find_special_key(srcp, &modifiers, keycode, FALSE, in_string, ! simplify, did_simplify); if (key == 0) return 0; *************** *** 2753,2761 **** find_special_key( char_u **srcp, int *modp, ! int keycode, /* prefer key code, e.g. K_DEL instead of DEL */ ! int keep_x_key, /* don't translate xHome to Home key */ ! int in_string) /* TRUE in string, double quote is escaped */ { char_u *last_dash; char_u *end_of_name; --- 2756,2766 ---- find_special_key( char_u **srcp, int *modp, ! int keycode, // prefer key code, e.g. K_DEL instead of DEL ! int keep_x_key, // don't translate xHome to Home key ! int in_string, // TRUE in string, double quote is escaped ! int simplify, // simplify and ! int *did_simplify) // found or { char_u *last_dash; char_u *end_of_name; *************** *** 2835,2841 **** && VIM_ISDIGIT(last_dash[6])) { /* or or */ ! vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, TRUE); if (l == 0) { emsg(_(e_invarg)); --- 2840,2847 ---- && VIM_ISDIGIT(last_dash[6])) { /* or or */ ! vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, ! &n, 0, TRUE); if (l == 0) { emsg(_(e_invarg)); *************** *** 2885,2895 **** key = DEL; } ! /* ! * Normal Key with modifier: Try to make a single byte code. ! */ if (!IS_SPECIAL(key)) ! key = extract_modifiers(key, &modifiers); *modp = modifiers; *srcp = end_of_name; --- 2891,2900 ---- key = DEL; } ! // Normal Key with modifier: Try to make a single byte code. if (!IS_SPECIAL(key)) ! key = extract_modifiers(key, &modifiers, ! simplify, did_simplify); *modp = modifiers; *srcp = end_of_name; *************** *** 2903,2928 **** /* * Try to include modifiers in the key. * Changes "Shift-a" to 'A', "Alt-A" to 0xc0, etc. */ int ! extract_modifiers(int key, int *modp) { int modifiers = *modp; #ifdef MACOS_X ! /* Command-key really special, no fancynest */ if (!(modifiers & MOD_MASK_CMD)) #endif if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { key = TOUPPER_ASC(key); ! modifiers &= ~MOD_MASK_SHIFT; } ! if ((modifiers & MOD_MASK_CTRL) #ifdef EBCDIC ! /* * TODO: EBCDIC Better use: ! * && (Ctrl_chr(key) || key == '?') ! * ??? */ && strchr("?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_", key) != NULL #else --- 2908,2944 ---- /* * Try to include modifiers in the key. * Changes "Shift-a" to 'A', "Alt-A" to 0xc0, etc. + * When "simplify" is FALSE don't do Ctrl and Alt. + * When "simplify" is TRUE and Ctrl or Alt is removed from modifiers set + * "did_simplify" when it's not NULL. */ int ! extract_modifiers(int key, int *modp, int simplify, int *did_simplify) { int modifiers = *modp; #ifdef MACOS_X ! // Command-key really special, no fancynest if (!(modifiers & MOD_MASK_CMD)) #endif if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { key = TOUPPER_ASC(key); ! // With and we keep the shift modifier. ! // With and we don't keep the shift modifier. ! if (simplify || modifiers == MOD_MASK_SHIFT) ! modifiers &= ~MOD_MASK_SHIFT; } ! ! // and mean the same thing, always use "H" ! if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key)) ! key = TOUPPER_ASC(key); ! ! if (simplify && (modifiers & MOD_MASK_CTRL) #ifdef EBCDIC ! // TODO: EBCDIC Better use: ! // && (Ctrl_chr(key) || key == '?') ! // ??? && strchr("?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_", key) != NULL #else *************** *** 2935,2950 **** /* is */ if (key == 0) key = K_ZERO; } #ifdef MACOS_X /* Command-key really special, no fancynest */ if (!(modifiers & MOD_MASK_CMD)) #endif ! if ((modifiers & MOD_MASK_ALT) && key < 0x80 && !enc_dbcs) // avoid creating a lead byte { key |= 0x80; modifiers &= ~MOD_MASK_ALT; /* remove the META modifier */ } *modp = modifiers; --- 2951,2971 ---- /* is */ if (key == 0) key = K_ZERO; + if (did_simplify != NULL) + *did_simplify = TRUE; } + #ifdef MACOS_X /* Command-key really special, no fancynest */ if (!(modifiers & MOD_MASK_CMD)) #endif ! if (simplify && (modifiers & MOD_MASK_ALT) && key < 0x80 && !enc_dbcs) // avoid creating a lead byte { key |= 0x80; modifiers &= ~MOD_MASK_ALT; /* remove the META modifier */ + if (did_simplify != NULL) + *did_simplify = TRUE; } *modp = modifiers; *** ../vim-8.1.2144/src/option.c 2019-10-05 21:35:12.228950931 +0200 --- src/option.c 2019-10-12 22:53:38.777040254 +0200 *************** *** 4495,4501 **** { --arg; /* put arg at the '<' */ modifiers = 0; ! key = find_special_key(&arg, &modifiers, TRUE, TRUE, FALSE); if (modifiers) /* can't handle modifiers here */ key = 0; } --- 4495,4501 ---- { --arg; /* put arg at the '<' */ modifiers = 0; ! key = find_special_key(&arg, &modifiers, TRUE, TRUE, FALSE, TRUE, NULL); if (modifiers) /* can't handle modifiers here */ key = 0; } *** ../vim-8.1.2144/src/proto/misc2.pro 2019-09-21 20:46:14.728275744 +0200 --- src/proto/misc2.pro 2019-10-12 22:55:37.116647113 +0200 *************** *** 67,76 **** int simplify_key(int key, int *modifiers); int handle_x_keys(int key); char_u *get_special_key_name(int c, int modifiers); ! int trans_special(char_u **srcp, char_u *dst, int keycode, int in_string); int special_to_buf(int key, int modifiers, int keycode, char_u *dst); ! int find_special_key(char_u **srcp, int *modp, int keycode, int keep_x_key, int in_string); ! int extract_modifiers(int key, int *modp); int find_special_key_in_table(int c); int get_special_key_code(char_u *name); char_u *get_key_name(int i); --- 67,76 ---- int simplify_key(int key, int *modifiers); int handle_x_keys(int key); char_u *get_special_key_name(int c, int modifiers); ! int trans_special(char_u **srcp, char_u *dst, int keycode, int in_string, int simplify, int *did_simplify); int special_to_buf(int key, int modifiers, int keycode, char_u *dst); ! int find_special_key(char_u **srcp, int *modp, int keycode, int keep_x_key, int in_string, int simplify, int *did_simplify); ! int extract_modifiers(int key, int *modp, int simplify, int *did_simplify); int find_special_key_in_table(int c); int get_special_key_code(char_u *name); char_u *get_key_name(int i); *** ../vim-8.1.2144/src/proto/term.pro 2019-09-23 21:16:51.387544361 +0200 --- src/proto/term.pro 2019-10-12 21:47:56.652964165 +0200 *************** *** 67,73 **** int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen); void term_get_fg_color(char_u *r, char_u *g, char_u *b); void term_get_bg_color(char_u *r, char_u *g, char_u *b); ! char_u *replace_termcodes(char_u *from, char_u **bufp, int from_part, int do_lt, int special); void show_termcodes(void); int show_one_termcode(char_u *name, char_u *code, int printit); char_u *translate_mapping(char_u *str); --- 67,73 ---- int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen); void term_get_fg_color(char_u *r, char_u *g, char_u *b); void term_get_bg_color(char_u *r, char_u *g, char_u *b); ! char_u *replace_termcodes(char_u *from, char_u **bufp, int flags, int *did_simplify); void show_termcodes(void); int show_one_termcode(char_u *name, char_u *code, int printit); char_u *translate_mapping(char_u *str); *** ../vim-8.1.2144/src/testdir/test_termcodes.vim 2019-10-12 18:22:46.957511288 +0200 --- src/testdir/test_termcodes.vim 2019-10-13 16:37:26.833546697 +0200 *************** *** 862,868 **** " The mode doesn't need to be enabled, the codes are always detected. func RunTest_modifyOtherKeys(func) new ! set timeoutlen=20 " Shift-X is send as 'X' with the shift modifier call feedkeys('a' .. a:func('X', 2) .. "\", 'Lx!') --- 862,868 ---- " The mode doesn't need to be enabled, the codes are always detected. func RunTest_modifyOtherKeys(func) new ! set timeoutlen=10 " Shift-X is send as 'X' with the shift modifier call feedkeys('a' .. a:func('X', 2) .. "\", 'Lx!') *************** *** 902,912 **** set timeoutlen& endfunc ! func Test_modifyOtherKeys_CSI27() call RunTest_modifyOtherKeys(function('GetEscCodeCSI27')) - endfunc - - func Test_modifyOtherKeys_CSIu() call RunTest_modifyOtherKeys(function('GetEscCodeCSIu')) endfunc --- 902,909 ---- set timeoutlen& endfunc ! func Test_modifyOtherKeys_basic() call RunTest_modifyOtherKeys(function('GetEscCodeCSI27')) call RunTest_modifyOtherKeys(function('GetEscCodeCSIu')) endfunc *************** *** 928,934 **** func RunTest_mapping_works_with_shift(func) new ! set timeoutlen=20 call RunTest_mapping_shift('@', a:func) call RunTest_mapping_shift('A', a:func) --- 925,931 ---- func RunTest_mapping_works_with_shift(func) new ! set timeoutlen=10 call RunTest_mapping_shift('@', a:func) call RunTest_mapping_shift('A', a:func) *************** *** 944,950 **** set timeoutlen& endfunc ! func Test_mapping_works_with_shift() call RunTest_mapping_works_with_shift(function('GetEscCodeCSI27')) call RunTest_mapping_works_with_shift(function('GetEscCodeCSIu')) endfunc --- 941,1028 ---- set timeoutlen& endfunc ! func Test_mapping_works_with_shift_plain() call RunTest_mapping_works_with_shift(function('GetEscCodeCSI27')) call RunTest_mapping_works_with_shift(function('GetEscCodeCSIu')) endfunc + + func RunTest_mapping_mods(map, key, func, code) + call setline(1, '') + exe 'inoremap ' .. a:map .. ' xyz' + call feedkeys('a' .. a:func(a:key, a:code) .. "\", 'Lx!') + call assert_equal("xyz", getline(1)) + exe 'iunmap ' .. a:map + endfunc + + func RunTest_mapping_works_with_mods(func, mods, code) + new + set timeoutlen=10 + + if a:mods !~ 'S' + " Shift by itself has no effect + call RunTest_mapping_mods('<' .. a:mods .. '-@>', '@', a:func, a:code) + endif + call RunTest_mapping_mods('<' .. a:mods .. '-A>', 'A', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-Z>', 'Z', a:func, a:code) + if a:mods !~ 'S' + " with Shift code is always upper case + call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'a', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'z', a:func, a:code) + endif + if a:mods != 'A' + " with Alt code is not in upper case + call RunTest_mapping_mods('<' .. a:mods .. '-a>', 'A', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-z>', 'Z', a:func, a:code) + endif + call RunTest_mapping_mods('<' .. a:mods .. '-á>', 'á', a:func, a:code) + if a:mods !~ 'S' + " Shift by itself has no effect + call RunTest_mapping_mods('<' .. a:mods .. '-^>', '^', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-_>', '_', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-{>', '{', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-\|>', '|', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-}>', '}', a:func, a:code) + call RunTest_mapping_mods('<' .. a:mods .. '-~>', '~', a:func, a:code) + endif + + bwipe! + set timeoutlen& + endfunc + + func Test_mapping_works_with_shift() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S', 2) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S', 2) + endfunc + + func Test_mapping_works_with_ctrl() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C', 5) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C', 5) + endfunc + + func Test_mapping_works_with_shift_ctrl() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S', 6) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S', 6) + endfunc + + " Below we also test the "u" code with Alt, This works, but libvterm would not + " send the Alt key like this but by prefixing an Esc. + + func Test_mapping_works_with_alt() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'A', 3) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'A', 3) + endfunc + + func Test_mapping_works_with_shift_alt() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'S-A', 4) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'S-A', 4) + endfunc + + func Test_mapping_works_with_ctrl_alt() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-A', 7) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-A', 7) + endfunc + + func Test_mapping_works_with_shift_ctrl_alt() + call RunTest_mapping_works_with_mods(function('GetEscCodeCSI27'), 'C-S-A', 8) + call RunTest_mapping_works_with_mods(function('GetEscCodeCSIu'), 'C-S-A', 8) + endfunc *** ../vim-8.1.2144/src/structs.h 2019-10-09 22:01:20.599438001 +0200 --- src/structs.h 2019-10-13 12:42:34.776055302 +0200 *************** *** 1172,1177 **** --- 1172,1179 ---- char_u *m_orig_str; // rhs as entered by the user int m_keylen; // strlen(m_keys) int m_mode; // valid mode + int m_simplified; // m_keys was simplified, do not use this map + // if seenModifyOtherKeys is TRUE int m_noremap; // if non-zero no re-mapping for m_str char m_silent; // used, don't echo commands char m_nowait; // used *** ../vim-8.1.2144/src/terminal.c 2019-10-10 21:13:59.962360351 +0200 --- src/terminal.c 2019-10-13 13:46:43.253595570 +0200 *************** *** 772,778 **** p = skiptowhite(cmd); *p = NUL; ! keys = replace_termcodes(ep + 1, &buf, TRUE, TRUE, TRUE); opt.jo_set2 |= JO2_EOF_CHARS; opt.jo_eof_chars = vim_strsave(keys); vim_free(buf); --- 772,779 ---- p = skiptowhite(cmd); *p = NUL; ! keys = replace_termcodes(ep + 1, &buf, ! REPTERM_FROM_PART | REPTERM_DO_LT | REPTERM_SPECIAL, NULL); opt.jo_set2 |= JO2_EOF_CHARS; opt.jo_eof_chars = vim_strsave(keys); vim_free(buf); *************** *** 1372,1378 **** } // add modifiers for the typed key ! mod |= mod_mask; /* * Convert special keys to vterm keys: --- 1373,1384 ---- } // add modifiers for the typed key ! if (mod_mask & MOD_MASK_SHIFT) ! mod |= VTERM_MOD_SHIFT; ! if (mod_mask & MOD_MASK_CTRL) ! mod |= VTERM_MOD_CTRL; ! if (mod_mask & (MOD_MASK_ALT | MOD_MASK_META)) ! mod |= VTERM_MOD_ALT; /* * Convert special keys to vterm keys: *** ../vim-8.1.2144/src/usercmd.c 2019-08-18 22:25:54.669447972 +0200 --- src/usercmd.c 2019-10-12 21:52:36.699707297 +0200 *************** *** 868,874 **** char_u *rep_buf = NULL; garray_T *gap; ! replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE); if (rep_buf == NULL) { // Can't replace termcodes - try using the string as is --- 868,874 ---- char_u *rep_buf = NULL; garray_T *gap; ! replace_termcodes(rep, &rep_buf, 0, NULL); if (rep_buf == NULL) { // Can't replace termcodes - try using the string as is *** ../vim-8.1.2144/src/vim.h 2019-09-21 20:46:14.728275744 +0200 --- src/vim.h 2019-10-12 21:47:07.149194164 +0200 *************** *** 2633,2636 **** --- 2633,2642 ---- #define CLIP_ZINDEX 32000 + // Flags for replace_termcodes() + #define REPTERM_FROM_PART 1 + #define REPTERM_DO_LT 2 + #define REPTERM_SPECIAL 4 + #define REPTERM_NO_SIMPLIFY 8 + #endif // VIM__H *** ../vim-8.1.2144/src/version.c 2019-10-12 21:08:37.006660412 +0200 --- src/version.c 2019-10-13 16:40:35.672957071 +0200 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 2145, /**/ -- Kisses may last for as much as, but no more than, five minutes. [real standing law in Iowa, United States of America] /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///