changeset 18301:506bf60a30a0 v8.1.2145

patch 8.1.2145: cannot map <C-H> when modifyOtherKeys is enabled Commit: https://github.com/vim/vim/commit/459fd785e4a8d044147a3f83a5fca8748528aa84 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Oct 13 16:43:39 2019 +0200 patch 8.1.2145: cannot map <C-H> when modifyOtherKeys is enabled Problem: Cannot map <C-H> when modifyOtherKeys is enabled. Solution: Add the <C-H> mapping twice, both with modifier and as 0x08. Use only the first one when modifyOtherKeys has been detected.
author Bram Moolenaar <Bram@vim.org>
date Sun, 13 Oct 2019 16:45:04 +0200
parents e502ed410b63
children 066fcaab07a9
files 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/structs.h src/term.c src/terminal.c src/testdir/test_termcodes.vim src/usercmd.c src/version.c src/vim.h
diffstat 21 files changed, 541 insertions(+), 343 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -3526,7 +3526,8 @@ get_string_tv(char_u **arg, typval_T *re
 			  break;
 
 			    /* Special key, e.g.: "\<C-W>" */
-		case '<': extra = trans_special(&p, name, TRUE, TRUE);
+		case '<': extra = trans_special(&p, name, TRUE, TRUE,
+								   TRUE, NULL);
 			  if (extra != 0)
 			  {
 			      name += extra;
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -52,7 +52,7 @@ static int typeahead_char = 0;		/* typea
  */
 static int	block_redo = FALSE;
 
-static int		KeyNoremap = 0;	    /* remapping flags */
+static int	KeyNoremap = 0;	    // remapping flags
 
 /*
  * Variables used by vgetorpeek() and flush_buffers().
@@ -1771,7 +1771,7 @@ vgetc(void)
 	    if (!no_reduce_keys)
 	    {
 		// A modifier was not used for a mapping, apply it to ASCII
-		// keys.
+		// keys.  Shift would already have been applied.
 		if ((mod_mask & MOD_MASK_CTRL)
 			&& ((c >= '`' && c <= 0x7f)
 			    || (c >= '@' && c <= '_')))
@@ -2240,6 +2240,7 @@ handle_mapping(
 	    // 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
--- a/src/globals.h
+++ b/src/globals.h
@@ -1002,6 +1002,10 @@ EXTERN int ex_no_reprint INIT(= FALSE); 
 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
--- a/src/gui_mac.c
+++ b/src/gui_mac.c
@@ -2177,7 +2177,8 @@ gui_mac_unicode_key_event(
 	    key_char = simplify_key(key_char, (int *)&vimModifiers);
 
 	    /* Interpret META, include SHIFT, etc. */
-	    key_char = extract_modifiers(key_char, (int *)&vimModifiers);
+	    key_char = extract_modifiers(key_char, (int *)&vimModifiers,
+		    TRUE, NULL);
 	    if (key_char == CSI)
 		key_char = K_CSI;
 
@@ -4772,7 +4773,8 @@ gui_mch_add_menu_item(vimmenu_T *menu, i
 	char_u	    *p_actext;
 
 	p_actext = menu->actext;
-	key = find_special_key(&p_actext, &modifiers, FALSE, FALSE, FALSE);
+	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
--- a/src/gui_w32.c
+++ b/src/gui_w32.c
@@ -850,7 +850,7 @@ char_to_string(int ch, char_u *string, i
 	modifiers &= ~MOD_MASK_SHIFT;
 
     /* Interpret the ALT key as making the key META, include SHIFT, etc. */
-    ch = extract_modifiers(ch, &modifiers);
+    ch = extract_modifiers(ch, &modifiers, TRUE, NULL);
     if (ch == CSI)
 	ch = K_CSI;
 
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -1417,7 +1417,8 @@ do_highlight(
 		 */
 		for (p = arg, off = 0; off < 100 - 6 && *p; )
 		{
-		    len = trans_special(&p, buf + off, FALSE, FALSE);
+		    len = trans_special(&p, buf + off, FALSE, FALSE,
+								   TRUE, NULL);
 		    if (len > 0)	    // recognized special char
 			off += len;
 		    else		    // copy as normal char
--- a/src/if_ole.cpp
+++ b/src/if_ole.cpp
@@ -330,7 +330,7 @@ CVim::SendKeys(BSTR keys)
     }
 
     /* Translate key codes like <Esc> */
-    str = replace_termcodes((char_u *)buffer, &ptr, FALSE, TRUE, FALSE);
+    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.
--- a/src/main.c
+++ b/src/main.c
@@ -4339,7 +4339,7 @@ server_to_input_buf(char_u *str)
      *  <lt> sequence is recognised - needed for a real backslash.
      */
     p_cpo = (char_u *)"Bk";
-    str = replace_termcodes((char_u *)str, &ptr, FALSE, TRUE, FALSE);
+    str = replace_termcodes((char_u *)str, &ptr, REPTERM_DO_LT, NULL);
     p_cpo = cpo_save;
 
     if (*ptr != NUL)	/* trailing CTRL-V results in nothing */
--- a/src/map.c
+++ b/src/map.c
@@ -256,18 +256,15 @@ do_map(
     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;
+    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;
-    int		hash;
-    int		new_hash;
     mapblock_T	**abbr_table;
     mapblock_T	**map_table;
     int		unique = FALSE;
@@ -277,6 +274,7 @@ do_map(
 #ifdef FEAT_EVAL
     int		expr = FALSE;
 #endif
+    int		did_simplify = FALSE;
     int		noremap;
     char_u      *orig_rhs;
 
@@ -375,6 +373,7 @@ do_map(
     rhs = p;
     hasarg = (*rhs != NUL);
     haskey = (*keys != NUL);
+    do_print = !haskey || (maptype != 1 && !hasarg);
 
     // check for :unmap without argument
     if (maptype == 1 && !haskey)
@@ -389,373 +388,427 @@ do_map(
     // 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 <C-H> 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)
-	keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, special);
+    {
+	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, "<nop>") == 0)	    // "<Nop>" means nothing
 	    rhs = (char_u *)"";
 	else
-	    rhs = replace_termcodes(rhs, &arg_buf, FALSE, TRUE, special);
+	    rhs = replace_termcodes(rhs, &arg_buf,
+			REPTERM_DO_LT | (special ? REPTERM_SPECIAL : 0), NULL);
     }
 
-    // check arguments and translate function keys
-    if (haskey)
+    /*
+     * The following is done twice if we have two versions of keys:
+     * "alt_keys_buf" is not NULL.
+     */
+    for (keyround = 1; keyround <= 2; ++keyround)
     {
-	len = (int)STRLEN(keys);
-	if (len > MAXMAPLEN)		// maximum length of MAXMAPLEN chars
+	int	did_it = FALSE;
+	int	did_local = FALSE;
+	int	round;
+	int	hash;
+	int	new_hash;
+
+	if (keyround == 2)
 	{
-	    retval = 1;
-	    goto theend;
+	    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;
 
-	if (abbrev && maptype != 1)
+	// check arguments and translate function keys
+	if (haskey)
 	{
-	    // 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)
+	    len = (int)STRLEN(keys);
+	    if (len > MAXMAPLEN)	// maximum length of MAXMAPLEN chars
 	    {
-		int	first, last;
-		int	same = -1;
+		retval = 1;
+		goto theend;
+	    }
 
-		first = vim_iswordp(keys);
-		last = first;
-		p = keys + (*mb_ptr2len)(keys);
-		n = 1;
-		while (p < keys + len)
+	    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)
 		{
-		    ++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);
+		    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;
+		    }
 		}
-		if (last && n > 2 && same >= 0 && same < n - 1)
-		{
-		    retval = 1;
-		    goto theend;
-		}
-	    }
-	    else if (vim_iswordc(keys[len - 1]))  // ends in keyword char
+		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;
-		}
+		// 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
+	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();
+	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)
+	// Check if a new local mapping wasn't already defined globally.
+	if (map_table == curbuf->b_maphash && haskey && hasarg && maptype != 1)
 	{
-	    if (abbrev)
+	    // need to loop over all global hash lists
+	    for (hash = 0; hash < 256 && !got_int; ++hash)
 	    {
-		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)
+		{
+		    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)
 		{
-		    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;
+		    // 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)
+	// When listing global mappings, also list buffer-local ones here.
+	if (map_table != curbuf->b_maphash && !hasarg && maptype != 1)
 	{
-	    if (abbrev)
+	    // need to loop over all global hash lists
+	    for (hash = 0; hash < 256 && !got_int; ++hash)
 	    {
-		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 (abbrev)
 		{
-		    if (!haskey)		    // show all entries
+		    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)
 		    {
-			showmap(mp, TRUE);
-			did_local = TRUE;
-		    }
-		    else
-		    {
-			n = mp->m_keylen;
-			if (STRNCMP(mp->m_keys, keys,
-					    (size_t)(n < len ? n : len)) == 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)
+	// 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)
 	{
-	    if (abbrev)
+	    // need to loop over all hash lists
+	    for (hash = 0; hash < 256 && !got_int; ++hash)
 	    {
-		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
+		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)
 		{
-		    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
+
+		    if (!(mp->m_mode & mode))   // skip entries with wrong mode
 		    {
-			n = (int)STRLEN(mp->m_str);
-			p = mp->m_str;
+			mpp = &(mp->m_next);
+			continue;
+		    }
+		    if (!haskey)	// show all entries
+		    {
+			showmap(mp, map_table != maphash);
+			did_it = TRUE;
 		    }
-		    else
-		    {
-			n = mp->m_keylen;
-			p = mp->m_keys;
-		    }
-		    if (STRNCMP(p, keys, (size_t)(n < len ? n : len)) == 0)
+		    else	// do we have a match?
 		    {
-			if (maptype == 1)	// delete entry
+			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)
 			{
-			    // 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
+			    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;
 			    }
-			    // 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 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
-				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)
+				// new rhs for existing entry
+				mp->m_mode &= ~mode;	// remove mode bits
+				if (mp->m_mode == 0 && !did_it) // reuse entry
 				{
-				    retval = 4;		// no mem
-				    goto theend;
+				    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;
 				}
-				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
 			    }
 			}
-			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);
 		}
-		mpp = &(mp->m_next);
 	    }
 	}
-    }
 
-    if (maptype == 1)			    // delete entry
-    {
-	if (!did_it)
-	    retval = 2;			    // no match
-	else if (*keys == Ctrl_C)
+	if (maptype == 1)
 	{
-	    // 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;
+	    // 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;
 	}
-	goto theend;
-    }
 
-    if (!haskey || !hasarg)		    // print entries
-    {
-	if (!did_it && !did_local)
+	if (!haskey || !hasarg)
 	{
-	    if (abbrev)
-		msg(_("No abbreviation found"));
-	    else
-		msg(_("No mapping found"));
+	    // print entries
+	    if (!did_it && !did_local)
+	    {
+		if (abbrev)
+		    msg(_("No abbreviation found"));
+		else
+		    msg(_("No mapping found"));
+	    }
+	    goto theend;    // listing finished
 	}
-	goto theend;			    // listing finished
-    }
 
-    if (did_it)			// have added the new entry already
-	goto theend;
+	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;
-    }
+	// 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;
-    }
+	// 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_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;
+	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;
+	// 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,7 +987,7 @@ map_to_exists(char_u *str, char_u *modec
     char_u	*buf;
     int		retval;
 
-    rhs = replace_termcodes(str, &buf, FALSE, TRUE, FALSE);
+    rhs = replace_termcodes(str, &buf, REPTERM_DO_LT, NULL);
 
     retval = map_to_exists_mode(rhs, mode_str2flags(modechars), abbr);
     vim_free(buf);
@@ -2036,7 +2089,8 @@ get_maparg(typval_T *argvars, typval_T *
 
     mode = get_map_mode(&which, 0);
 
-    keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE, FALSE);
+    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);
 
--- a/src/menu.c
+++ b/src/menu.c
@@ -372,7 +372,8 @@ ex_menu(
 	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);
+	    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;
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -2696,12 +2696,15 @@ trans_special(
     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		in_string,  // TRUE when inside a double quoted string
+    int		simplify,	// simplify <C-H> and <A-x>
+    int		*did_simplify)  // found <C-H> or <A-x>
 {
     int		modifiers = 0;
     int		key;
 
-    key = find_special_key(srcp, &modifiers, keycode, FALSE, in_string);
+    key = find_special_key(srcp, &modifiers, keycode, FALSE, in_string,
+						       simplify, did_simplify);
     if (key == 0)
 	return 0;
 
@@ -2753,9 +2756,11 @@ special_to_buf(int key, int modifiers, i
 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		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 <C-H> and <A-x>
+    int		*did_simplify)  // found <C-H> or <A-x>
 {
     char_u	*last_dash;
     char_u	*end_of_name;
@@ -2835,7 +2840,8 @@ find_special_key(
 						 && VIM_ISDIGIT(last_dash[6]))
 	    {
 		/* <Char-123> or <Char-033> or <Char-0x33> */
-		vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL, &n, 0, TRUE);
+		vim_str2nr(last_dash + 6, NULL, &l, STR2NR_ALL, NULL,
+								  &n, 0, TRUE);
 		if (l == 0)
 		{
 		    emsg(_(e_invarg));
@@ -2885,11 +2891,10 @@ find_special_key(
 			key = DEL;
 		}
 
-		/*
-		 * Normal Key with modifier: Try to make a single byte code.
-		 */
+		// Normal Key with modifier: Try to make a single byte code.
 		if (!IS_SPECIAL(key))
-		    key = extract_modifiers(key, &modifiers);
+		    key = extract_modifiers(key, &modifiers,
+						       simplify, did_simplify);
 
 		*modp = modifiers;
 		*srcp = end_of_name;
@@ -2903,26 +2908,37 @@ find_special_key(
 /*
  * 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)
+extract_modifiers(int key, int *modp, int simplify, int *did_simplify)
 {
     int	modifiers = *modp;
 
 #ifdef MACOS_X
-    /* Command-key really special, no fancynest */
+    // 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;
+	// With <C-S-a> and <A-S-a> we keep the shift modifier.
+	// With <S-a> and <S-A> we don't keep the shift modifier.
+	if (simplify || modifiers == MOD_MASK_SHIFT)
+	    modifiers &= ~MOD_MASK_SHIFT;
     }
-    if ((modifiers & MOD_MASK_CTRL)
+
+    // <C-H> and <C-h> 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 == '?')
-	     * ???  */
+	    // TODO: EBCDIC Better use:
+	    // && (Ctrl_chr(key) || key == '?')
+	    // ???
 	    && strchr("?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_", key)
 						       != NULL
 #else
@@ -2935,16 +2951,21 @@ extract_modifiers(int key, int *modp)
 	/* <C-@> is <Nul> */
 	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 ((modifiers & MOD_MASK_ALT) && key < 0x80
+    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;
--- a/src/option.c
+++ b/src/option.c
@@ -4495,7 +4495,7 @@ find_key_option(char_u *arg_arg, int has
     {
 	--arg;			    /* put arg at the '<' */
 	modifiers = 0;
-	key = find_special_key(&arg, &modifiers, TRUE, TRUE, FALSE);
+	key = find_special_key(&arg, &modifiers, TRUE, TRUE, FALSE, TRUE, NULL);
 	if (modifiers)		    /* can't handle modifiers here */
 	    key = 0;
     }
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -67,10 +67,10 @@ void append_ga_line(garray_T *gap);
 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 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 extract_modifiers(int key, int *modp);
+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);
--- a/src/proto/term.pro
+++ b/src/proto/term.pro
@@ -67,7 +67,7 @@ int is_mouse_topline(win_T *wp);
 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);
+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);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1172,6 +1172,8 @@ struct mapblock
     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;	// <silent> used, don't echo commands
     char	m_nowait;	// <nowait> used
--- a/src/term.c
+++ b/src/term.c
@@ -4845,6 +4845,7 @@ not_enough:
 		else if ((arg[0] == 27 && argc == 3 && trail == '~')
 			|| (argc == 2 && trail == 'u'))
 		{
+		    seenModifyOtherKeys = TRUE;
 		    if (trail == 'u')
 			key = arg[0];
 		    else
@@ -4853,13 +4854,20 @@ not_enough:
 		    modifiers = decode_modifiers(arg[1]);
 
 		    // Some keys already have Shift included, pass them as
-		    // normal keys.
+		    // normal keys.  Not when Ctrl is also used, because <C-H>
+		    // and <C-S-H> 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 <C-H> and <C-h> are the same.  Typing
+		    // <C-S-H> 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,18 +5348,26 @@ term_get_bg_color(char_u *r, char_u *g, 
  * 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.
+ * 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 <lt>
+ *  REPTERM_SPECIAL	always accept <key> notation
+ *  REPTERM_NO_SIMPLIFY	do not simplify <C-H> to 0x08 and set 8th bit for <A-x>
+ *
+ * "did_simplify" is set when some <C-H> or <A-x> code was simplified, unless
+ * it is NULL.
  */
     char_u *
 replace_termcodes(
     char_u	*from,
     char_u	**bufp,
-    int		from_part,
-    int		do_lt,		/* also translate <lt> */
-    int		special)	/* always accept <key> notation */
+    int		flags,
+    int		*did_simplify)
 {
     int		i;
     int		slen;
@@ -5364,7 +5380,8 @@ replace_termcodes(
     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_special = (vim_strchr(p_cpo, CPO_SPECI) == NULL)
+						  || (flags & REPTERM_SPECIAL);
     do_key_code = (vim_strchr(p_cpo, CPO_KEYCODE) == NULL);
 
     /*
@@ -5383,7 +5400,7 @@ replace_termcodes(
     /*
      * Check for #n at start only: function key n
      */
-    if (from_part && src[0] == '#' && VIM_ISDIGIT(src[1]))  /* function key */
+    if ((flags & REPTERM_FROM_PART) && src[0] == '#' && VIM_ISDIGIT(src[1]))
     {
 	result[dlen++] = K_SPECIAL;
 	result[dlen++] = 'k';
@@ -5403,7 +5420,8 @@ replace_termcodes(
 	 * If 'cpoptions' does not contain '<', check for special key codes,
 	 * like "<C-S-LeftMouse>"
 	 */
-	if (do_special && (do_lt || STRNCMP(src, "<lt>", 4) != 0))
+	if (do_special && ((flags & REPTERM_DO_LT)
+					      || STRNCMP(src, "<lt>", 4) != 0))
 	{
 #ifdef FEAT_EVAL
 	    /*
@@ -5429,7 +5447,8 @@ replace_termcodes(
 	    }
 #endif
 
-	    slen = trans_special(&src, result + dlen, TRUE, FALSE);
+	    slen = trans_special(&src, result + dlen, TRUE, FALSE,
+			     (flags & REPTERM_NO_SIMPLIFY) == 0, did_simplify);
 	    if (slen)
 	    {
 		dlen += slen;
@@ -5509,7 +5528,7 @@ replace_termcodes(
 	    ++src;				/* skip CTRL-V or backslash */
 	    if (*src == NUL)
 	    {
-		if (from_part)
+		if (flags & REPTERM_FROM_PART)
 		    result[dlen++] = key;
 		break;
 	    }
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -772,7 +772,8 @@ ex_terminal(exarg_T *eap)
 
 	    p = skiptowhite(cmd);
 	    *p = NUL;
-	    keys = replace_termcodes(ep + 1, &buf, TRUE, TRUE, TRUE);
+	    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,7 +1373,12 @@ term_convert_key(term_T *term, int c, ch
     }
 
     // add modifiers for the typed key
-    mod |= mod_mask;
+    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:
--- a/src/testdir/test_termcodes.vim
+++ b/src/testdir/test_termcodes.vim
@@ -862,7 +862,7 @@ endfunc
 " The mode doesn't need to be enabled, the codes are always detected.
 func RunTest_modifyOtherKeys(func)
   new
-  set timeoutlen=20
+  set timeoutlen=10
 
   " Shift-X is send as 'X' with the shift modifier
   call feedkeys('a' .. a:func('X', 2) .. "\<Esc>", 'Lx!')
@@ -902,11 +902,8 @@ func RunTest_modifyOtherKeys(func)
   set timeoutlen&
 endfunc
 
-func Test_modifyOtherKeys_CSI27()
+func Test_modifyOtherKeys_basic()
   call RunTest_modifyOtherKeys(function('GetEscCodeCSI27'))
-endfunc
-
-func Test_modifyOtherKeys_CSIu()
   call RunTest_modifyOtherKeys(function('GetEscCodeCSIu'))
 endfunc
 
@@ -928,7 +925,7 @@ endfunc
 
 func RunTest_mapping_works_with_shift(func)
   new
-  set timeoutlen=20
+  set timeoutlen=10
 
   call RunTest_mapping_shift('@', a:func)
   call RunTest_mapping_shift('A', a:func)
@@ -944,7 +941,88 @@ func RunTest_mapping_works_with_shift(fu
   set timeoutlen&
 endfunc
 
-func Test_mapping_works_with_shift()
+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) .. "\<Esc>", '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
--- a/src/usercmd.c
+++ b/src/usercmd.c
@@ -868,7 +868,7 @@ uc_add_command(
     char_u	*rep_buf = NULL;
     garray_T	*gap;
 
-    replace_termcodes(rep, &rep_buf, FALSE, FALSE, FALSE);
+    replace_termcodes(rep, &rep_buf, 0, NULL);
     if (rep_buf == NULL)
     {
 	// Can't replace termcodes - try using the string as is
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2145,
+/**/
     2144,
 /**/
     2143,
--- a/src/vim.h
+++ b/src/vim.h
@@ -2633,4 +2633,10 @@ long elapsed(DWORD start_tick);
 
 #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